IdleHandler,页面启动优化神器

作者: df556ada620a | 来源:发表于2018-11-14 21:55 被阅读19次

随着App的开发到了某个阶段必然会遇到一个需求,那就是优化页面的启动时间。

第一个问题:有什么方法可以去统计页面的启动时间呢?

<pre style="margin: 0px; padding: 0px; border: 0px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-weight: 400; font-stretch: inherit; font-size: 18px; line-height: inherit; font-family: inherit; vertical-align: baseline; word-break: break-word; color: rgb(93, 93, 93); letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">

adb logcat -s ActivityManager | grep "Displayed"

上面的命令行可用来进行查看。

第二个问题:启动时间是包括了哪些流程,是如何被计算出来的呢?

App启动主要经过如下几个流程

  1. Launch the process.
  2. Initialize the objects.
  3. Create and initialize the activity.
  4. Inflate the layout.
  5. Draw your application for the first time.

最末尾的步骤5是绘制你的界面。所以完整的启动时间是要到绘制完成为止。

那么绘制界面对应的是什么时候呢?一般我们开发,最晚能被回调的是在onResume方法,那么onResume方法是在绘制之后还是之前呢?

no code no truth

<pre style="margin: 0px; padding: 0px; border: 0px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-weight: 400; font-stretch: inherit; font-size: 18px; line-height: inherit; font-family: inherit; vertical-align: baseline; word-break: break-word; color: rgb(93, 93, 93); letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">

 final void handleResumeActivity(IBinder token,
 boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
 //省略部分代码
 r = performResumeActivity(token, clearHide, reason);
 //省略部分代码
 if (a.mVisibleFromClient) {
 if (!a.mWindowAdded) {
 a.mWindowAdded = true;
 wm.addView(decor, l);

看上面的代码,就先放结论了。

在performResumeActivity 中进行了onResume的回调,在wm.addView 中进行了绘制,因此onResume的方法是在绘制之前,在onResume中做一些耗时操作都会影响启动时间。

下面就剥一下onResume的逻辑,绘制的有兴趣可以自己看源码。 首先performResumeActivity中会调用r.activity.performResume();

<pre style="margin: 0px; padding: 0px; border: 0px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-weight: 400; font-stretch: inherit; font-size: 18px; line-height: inherit; font-family: inherit; vertical-align: baseline; word-break: break-word; color: rgb(93, 93, 93); letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">

 public final ActivityClientRecord performResumeActivity(IBinder token,
 boolean clearHide, String reason) {
 //省略部分代码

 try {
 r.activity.onStateNotSaved();
 r.activity.mFragments.noteStateNotSaved();
 checkAndBlockForNetworkAccess();
 if (r.pendingIntents != null) {
 deliverNewIntents(r, r.pendingIntents);
 r.pendingIntents = null;
 }
 if (r.pendingResults != null) {
 deliverResults(r, r.pendingResults);
 r.pendingResults = null;
 }
 r.activity.performResume();
 //省略部分代码
 }
 }

然后在performResume中调用了 mInstrumentation.callActivityOnResume(this);

<pre style="margin: 0px; padding: 0px; border: 0px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-weight: 400; font-stretch: inherit; font-size: 18px; line-height: inherit; font-family: inherit; vertical-align: baseline; word-break: break-word; color: rgb(93, 93, 93); letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">

 final void performResume() {
 //省略部分代码
 mInstrumentation.callActivityOnResume(this);
 //省略部分代码
 }

最后在callActivityOnResume 调用了onResume

<pre style="margin: 0px; padding: 0px; border: 0px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-weight: 400; font-stretch: inherit; font-size: 18px; line-height: inherit; font-family: inherit; vertical-align: baseline; word-break: break-word; color: rgb(93, 93, 93); letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">

 public void callActivityOnResume(Activity activity) {
 activity.mResumed = true;
 activity.onResume();
 //省略代码
 }

到了此处就算真正调用到了onResume的方法。

既然知道了onResume中做的操作会影响到启动时间,那么就有一个优化启动时间的思路了。

思路

把在onResume以及其之前的调用的但非必须的事件(如某些界面View的绘制)挪出来找一个时机(即绘制完成以后)去调用。那样启动时间自然就缩短了。但是整体做的事并没有明显变化。那么这个时机是什么呢?

IdleHandler

看下IdleHandler的源码

<pre style="margin: 0px; padding: 0px; border: 0px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-weight: 400; font-stretch: inherit; font-size: 18px; line-height: inherit; font-family: inherit; vertical-align: baseline; word-break: break-word; color: rgb(93, 93, 93); letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">

 /**
 * Callback interface for discovering when a thread is going to block
 * waiting for more messages.
 */
 public static interface IdleHandler {
 /**
 * Called when the message queue has run out of messages and will now
 * wait for more. Return true to keep your idle handler active, false
 * to have it removed. This may be called if there are still messages
 * pending in the queue, but they are all scheduled to be dispatched
 * after the current time.
 */
 boolean queueIdle();
 }

从这个源码可知道,IdleHandler即在looper里面的message处理完了的时候去调用,这不就是我们onResume调用完了以后的时机么。

来一张图说明一下,明显的IdleHandler在onResume以及performTraversals绘制之后调用

由这个思路我把自己负责的页面中的一些界面的绘制逻辑挪到了IdleHandler中,由于有LoadingView时间,我把Adapter的绑定也挪出去了。看下优化前后效果图 ,效果还是挺明显的;

Android技术交流QQ群;701740775。
本群提供免费的学习指导 资料视频像高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)等内容 以及免费的解答。不懂得问题都可以在本群提出来 ,之后还会有职业生涯规划以及面试指导

相关文章

  • IdleHandler,页面启动优化神器

    随着App的开发到了某个阶段必然会遇到一个需求,那就是优化页面的启动时间。 第一个问题:有什么方法可以去统计页面的...

  • 使用 IdleHandler 优化启动速度

    概括 IdleHandler 可以用来优化性能,它会在消息队列空闲时被调用。详细介绍参看注释: 使用 使用非常简单...

  • App白屏和启动优化的一些思路

    App启动优化 App启动优化原理与技术方案 启动优化 黑白屏问题 启动页面主题设置为图片 启动页面,不要再onC...

  • Android性能优化之总括篇

    本文先总括Android性能优化的主要几个方面,后续再逐条详细说明。 1.启动页面优化: 启动页面网络请求优化(可...

  • Android 启动优化

    1.视觉优化 给启动页面设置主题: 2 .代码优化 冷启动耗时统计adb命令 : adb shell am sta...

  • Andoroid性能优化

    一、页面启动优化: 页面启动慢主要有2种页面渲染慢和java代码初始化慢的问题。 页面渲染: 1、最少布局嵌套 布...

  • 第三天(03.21)

    完善android禁止消息通知,吐司无效 App启动优化 Splash页面优化 apk瘦身 待解决

  • 浅谈Activity 启动过程

    引言:页面启动时间怎么计算? 在做启动优化的过程中,在做了一堆的优化工作之后,需要对整个的启动优化效果进行对比和评...

  • Android优化总结

    1. 启动优化一般指冷启动的优化,从点击桌面的icon图标到显示页面之间的时间长短 查看启动事件;优化的可操作方法...

  • 50. 安卓启动优化

    启动优化包含app的启动和单页面的启动,今天只说app的启动,二者优化的逻辑是相同的。 app启动的三种状态 冷启...

网友评论

    本文标题:IdleHandler,页面启动优化神器

    本文链接:https://www.haomeiwen.com/subject/jlpsfqtx.html