InCallUi外呼电话的优化

我们的目标是提升电话模块打电话的速度,打电话的话,有多个场景,通话记录拨打电话,拨号盘拨打电话,通话记录详情拨打电话,联系人详情拨打电话。各个场景拨打电话的速度不一致,每种场景都会对通话界面的显示存在着不同的影响,至于是什么影响了,我们暂时先不考虑,我们先考虑在一个空白界面拨打电话的速度是怎么样的,先从InCallUi的角度上着手进行优化,之后再对各个场景进行分析和优化。

我们现在搞清楚了我们要做什么,那我们就要朝这个方向入手了,拨打电话的流程是怎样的,当用户点击拨打的时候,我们会调用DialerUtils类中的startActivityWithErrorToast方法,其中调用了TelecomManager.placeCall方法,调用底层的方法进行呼叫,之后会走InCallServiceImpl类的onBind方法,onBind方法中调用了InCallPresenter类中的maybeStartRevealAnimation方法,会根据条件判断,是否启动InCallActivity.之后会走InCallActivity的onCreate方法,onCreate方法中调用internalResolveIntent方法,会显示CallCardFragment。在CallCardFragment中的onActivityCreated方法中,当条件满足的情况下会触发数据查询。

一、onBind ------->startActivity

在Dialer中拨打电话流程主体是这样的,那我们接下来看看哪些步骤是可控的,哪些不是,placeCall到onBind这个是不受我们所控制的,onBind到startActivity这个过程可控的,我们接下来就看看这一块具体干了些什么,代码如下:

 @Override
    public IBinder onBind(Intent intent) {
        Log.d(this, "onBind");
        final Context context = getApplicationContext();
        /// M: [plugin]ensure a context is valid.
        ExtensionManager.registerApplicationContext(context);
        final ContactInfoCache contactInfoCache = ContactInfoCache.getInstance(context);
        InCallPresenter.getInstance().setUp(
                getApplicationContext(),
                CallList.getInstance(),
                AudioModeProvider.getInstance(),
                new StatusBarNotifier(context, contactInfoCache),
                contactInfoCache,
                new ProximitySensor(context, AudioModeProvider.getInstance())
                );
        InCallPresenter.getInstance().onServiceBind();
        InCallPresenter.getInstance().maybeStartRevealAnimation(intent);
        TelecomAdapter.getInstance().setInCallService(this);

        return super.onBind(intent);
    }

通过Log打印,发现该过程耗时110ms左右,虽然耗时不是很长,但是我们还是要具体分析一下,看看能不能够缩短时间。

在onBind方法开头这里调用Debug类中的startMethodTracing方法,并在startActivity处调用Debug类中的stopMethodTracing方法,这两个函数运行过程中将采集onBind到startActivity内该应用所有线程的函数执行情况。运行代码,生成.trace文件,要么是在SD根目录,要么是在/sdcard/Android/data/包名/files/dmtrace.trace,将文件获取之后,我们将其复制到\sdk\tools这个文件夹下,在这文件夹打开命令行输入 traceview.bat test.trace,这种方式打开比直接在Eclipse打开的好处在于能够进行关键字的搜索。

那么我们打开了trace文件之后要做些什么呢,我们需要查找以下两类:
一类是调用次数不多,但每次调用却需要花费很长时间的函数
一类是那些自身占用时间不长,但调用却非常频繁的函数。

107747-f914568bdddef7d8.png

图中的从左到右
Name :该线程运行过程中所调用的函数名
Incl Cpu Time %: 某函数占用的CPU时间,包含内部调用其它函数的CPU时间的百分比
Incl Real Time : 某函数运行的真实时间(以毫秒为单位),内含调用其它函数所占用的真实时间
Call+Recur Calls/Total:某函数被调用次数以及递归调用占总调用次数的百分比
Real Time/Call: 同CPU Time/Call类似,只不过统计单位换成了真实时间

我们先来查查调用次数不多,但每次调用确话费很长时间的函数,即对Incl Real Time进行排序,排序之后,输入我们的包名,对关键字进行搜索。我们可以看到StatusBarNotifier的构造方法中checkCam方法比较耗时,那我们就要看该方法是不是可以去除,或者修改调用顺序,或者改用其他的方式实现。

image.png

之后对调用比较频繁的函数进行排序,Call+Recur Calls/Total进行排序,排序完之后,输入我们的包名,对关键字进行搜索。发现getSimpleName调用比较频繁,该方法是可以删除或使用字符串替代的。

之后看代码发现在InCallPresenter.setUp方法中注册了皮套广播,但是我们有的机型是不支持皮套功能的,那我们就可以根据是否支持皮套功能,来决定是否注册皮套广播。进行完这些优化之后
,我们再次打印LOG,已经只需要将近63ms了。

二、startIntent------->onCreate

通过打印Log发现startIntent到InCallActivity的onCreate方法,耗时将近218ms,这个时间我们也不知道是不是正常的,那怎么办呢,我们可以写一个Demo,测试一下简单的点击跳转到另外一个Activity,startIntent到onCreate所需的时间,我测出来将近130ms左右,那我们这边肯定是有问题的,需要进行优化的。

原生中InCallPresente构造方法中是没有调用任何方法的,这是为了优化获取归属地,所以开启了一个线程,对获取归属地方法进行初始化,虽然说是开了一个线程,但是还是会对主线程有阻塞效果的。为了判断是不是该方法造成的,只能先将其注释掉运行,发现startIntent到onCreate方法所花的时间顿时减少了,跟Demos所测的数据差不多。可以将归属地方法的初始化放入到一个子线程中,放入到DialerApplication的onCreate方法中,正好同样对拨号盘输入号码获取归属地有着优化效果。但是这样做的话,会影响到应用的冷启动和第三方应用拨打电话。但是我选择这样做的原因是,是因为我们已经准备将应用改为常驻进程了,这样正好可以解决这个问题了。

三、onCreate----->onCreateView

抓取trace,发现在onCrate方法中,getActionBar方法相对来说耗时久一点,获取到ActionBar之后将其隐藏,那我们直接修改InCallActivity的theme,原先是继承@android:style/Theme.Material.Light,现在我们将其改为@android:style/Theme.Material.Light.NoActionBar。
在onCreate方法中调用AnimationUtils.loadAnimation方法,但是并不会立即调用,那我们是不是可以改为在调用的时候,再加载呢,于是改成在弹出和收缩DialpadFragment的时候调用,也不会影响起动画效果。
原生在显示CallCardFragment布局的过程中,有一个过渡的动画,我们因为需求的原因,将该功能删除了,这样也对InCallUi起到优化作用。

四、onCreateView

CallCardFragment的onCreateView方法,耗时长达555ms,这一块优化就是针对布局进行优化了。

1.查看是否使用了大图片

那我们首先来看看是不是使用了大图片的布局,正好发现CallCardFragment使用了大图片作为背景,于是跟设计进行了协商,将大图片改为了一张小图片,我们再对图片进行缩放。

2.查看有些布局是不是可使用ViewStub标签

正好我们这边的三方通话布局和皮套布局只有满足特定的条件下,才会去显示,那么我们就可以使用ViewStub标签,同VideoFragment中的布局一样。

3.去掉多余的控件

CallButtonFragment中onCreateView方法占用CallCardFragment中onCreateView方法一大半。因为需求的改动,导致CallButtonFragment布局中还保留了一些多余的控件,未被使用的。
CallButtonFragment中去掉了多余的控件之后,还有8个按钮。但是耗时还是比较长,那想着是不是布局嵌套严重,导致的,为了验证这个,我直接忽略布局上的其他问题,将布局嵌套减少到一层,测试发现数据并没有减少多少,那我们就没有必要往这个方向走了。
之后发现每个按钮有多个状态,不可点击的,可以点击的,按下的状态,每个状态有对应的图片,我们是直接针对每个按钮写了个selector,再在布局上直接使用,按照我们以前的想法这应该是没有啥问题的,都是这样做的,但是当按钮多了的时候,加载图片所耗用的时间就比较长了,为了验证这个,我就直接把每个按钮的背景设为null,运行发现时间大大的减少了。我们现在知道问题的关键在哪了,那我们现在就该想办法怎么去解决了。我发现刚开始的时候各个按钮都是不可点击的,只有当满足条件的时候,才会将其设为可以点击的,那我就直接将布局中按钮的背景设为不可点击对应的图片,当按钮设为可以点击的时候,才将各个按钮的背景设为selector。

4.减少布局的嵌套。

5.合并布局,用TextView代替图片加文字

进行了这些优化之后,CallCardFragment中onCreateView方法所花的时间大大减少了。

五、onCreateView----->startContactInfoSearch

刚开始的时候我认为在CallCardFragment中onActivityCreated方法中调用CallCardPresenter中的init方法,开始数据查询。

public void init(Context context, Call call) {
        mContext = Preconditions.checkNotNull(context);

        /// M: For volte @{
        // Here we will use "mContext", so need add here, instead of "onUiReady()"
        ContactInfoCache.getInstance(mContext).addContactInfoUpdatedListener(mContactInfoUpdatedListener);
        /// @}

        // Call may be null if disconnect happened already.
        if (call != null) {
            mPrimary = call;
            CallList.getInstance().addCallUpdateListener(call.getId(), this);

            // start processing lookups right away.
            if (!call.isConferenceCall()) {
                startContactInfoSearch(call, true, call.getState() == Call.State.INCOMING);
            } else {
                updateContactEntry(null, true);
            }
        }

        onStateChange(null, InCallPresenter.getInstance().getInCallState(), CallList.getInstance());
    }

但是并不总是会这样的,有可能在InCallServiceImpl中onCallAdded方法回调的慢,导致条件不成立,这时候就不会去进行数据的查询了,当回调onCallAdded方法时,会一步步调用到CallCardPresenter类中的maybeStartSearch方法。这种情况的话会比在onActivityCreated中调用慢一些,但是没有办法,这个过程不受我们所控制。

private void maybeStartSearch(Call call, boolean isPrimary) {
        // no need to start search for conference calls which show generic info.
        /**
         * M: [VoLTE conference] incoming call still need to search.
         * google original code:
         if (call != null && !call.isConferenceCall()) {
         * @{
         */
        if ((call != null && !call.isConferenceCall()) || isIncomingVolteConference(call)) {
        /** @} */
            startContactInfoSearch(call, isPrimary, call.getState() == Call.State.INCOMING);
        }
    }

抓取CallCardFragment中onActivityCreated方法和CallCardPresenter中startContactInfoSearch方法中的trace,发现在ProximitySensor类中onStateChange方法,mAccelerometerListener.enable(mIsPhoneOffhook)调用耗时较长,这个方法是开关重力加速器,是根据当前手机是水平还是垂直摆放来决定是否关闭距离传感器的,但是我们这边正好把这个功能给去除了。那正好我们可以吧这段代码给注释掉。

六、startContactInfoSearch-------->onWindowFocusChanged

直接抓取trace,进行分析。

七、总结

优化需要耐心,一点点去分析,将一个流程给分段进行分析,再去分析整个流程。要学会勇于尝试,忽略一些因素的影响,去判断是不是哪个因素导致性能出现问题,再去想办法解决。优化也需要善用工具去进行分析,这会让我们更加精准的定位到问题所在。

在优化的过程中,我们需要去对耗时点,进行分类,我的分类如下:可控制的,不可控制的。可控制的,我们才能去进行优化。不可控制的,我们可以直接忽略掉。可控制的,可以再次分为:可以修改的,不可以修改的。有些点因为需求方面,是不可以修改的,那我们就没用办法了,那我们就不要去纠结这个点了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,542评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,596评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,021评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,682评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,792评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,985评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,107评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,845评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,299评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,612评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,747评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,441评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,072评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,828评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,069评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,545评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,658评论 2 350

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,846评论 25 707
  • 1.什么是Activity?问的不太多,说点有深度的 四大组件之一,一般的,一个用户交互界面对应一个activit...
    JoonyLee阅读 5,729评论 2 51
  • 第二章 第六房女人胡氏死去以後,娘俩发生了重大分歧。母亲白赵氏仍然坚持胡氏不过也是一张破旧了的糊窗纸,撕了就应该尽...
    幽与冥阅读 515评论 0 1
  • 按照官网的提示安装mongodb一帆风顺,可是想要添加权限管理就遇到了很大的坑 打开配置文件 sudo vim /...
    Airghc阅读 326评论 0 0
  • “拖延就像蒲公英。你把它拔掉,以为它不会再长出来了,但实际上它的根埋藏得很深,很快又长出来了” ——《拖...
    大耳朵的小圈阅读 434评论 0 0