Android应用启动优化:一种DelayLoad的实现和原理

0. 应用启动优化概述

在 Android 开发中,应用启动速度是一个非常重要的点,应用启动优化也是一个非常重要的过程.对于应用启动优化,其实核心思想就是在启动过程中少做事情,具体实践的时候无非就是下面几种:

异步加载

延时加载

懒加载

不用一一去解释,做过启动优化的估计都使用过,本篇文章将详细讲解一下一种延时加载的实现以及其原理.其实这种加载的实现是非常简单的,但是其中的原理可能比较复杂,还涉及到Looper/Handler/MessageQueue/VSYNC等.以及其中碰到的一些问题,还会有一些我自己额外的思考.

1. 优化后的DelayLoad的实现

一提到DelayLoad,大家可能第一时间想到的就是在 onCreate 里面调用 Handler.postDelayed方法, 将需要 Delay 加载的东西放到这里面去初始化, 这个也是一个比较方便的方法. Delay一段时间再去执行,这时候应用已经加载完成,界面已经显示出来了, 不过这个方法有一个致命的问题: 延迟多久?大家都知道,在 Android 的高端机型上,应用的启动是非常快的 , 这时候只需要 Delay 很短的时间就可以了, 但是在低端机型上,应用的启动就没有那么快了,而且现在应用为了兼容旧的机型,往往需要 Delay 较长的时间,这样带来体验上的差异是很明显的.

这里先说优化方案:

首先 , 创建 Handler 和 Runnable 对象, 其中 Runnable 对象的 run方法里面去更新 UI 线程.

private Handler myHandler = new Handler();

private Runnable mLoadingRunnable = new Runnable() {

@Override

public void run() {

updateText(); //更新UI线程

}

};

在主 Activity 的 onCreate 中加入下面的代码

getWindow().getDecorView().post(new Runnable() {

@Override

public void run() {

myHandler.post(mLoadingRunnable);

}

});

其实实现的话非常简单,我们来对比一下三种方案的效果.

2. 三种写法的差异对比

为了验证我们优化的 DelayLoad的效果,我们写了一个简单的app , 这个 App 中包含三张不同大小的图片,每张图片下面都会有一个 TextView , 来标记图片的显示高度和宽度. MainActivity的代码如下:

public class MainActivity extends AppCompatActivity {

private static final int DEALY_TIME = 300 ;

private ImageView imageView1;

private ImageView imageView2;

private ImageView imageView3;

private TextView textView1;

private TextView textView2;

private TextView textView3;

private Handler myHandler = new Handler();

private Runnable mLoadingRunnable = new Runnable() {

@Override

public void run() {

updateText();

}

};

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

imageView1 = (ImageView) findViewById(R.id.image1);

imageView2 = (ImageView) findViewById(R.id.image2);

imageView3 = (ImageView) findViewById(R.id.image3);

textView1 = (TextView) findViewById(R.id.text1);

textView2 = (TextView) findViewById(R.id.text2);

textView3 = (TextView) findViewById(R.id.text3);

//      第一种写法:直接Post

myHandler.post(mLoadingRunnable);

//      第二种写法:直接PostDelay 300ms.

//        myHandler.postDelayed(mLoadingRunnable, DEALY_TIME);

//      第三种写法:优化的DelayLoad

//      getWindow().getDecorView().post(new Runnable() {

//            @Override

//            public void run() {

//                myHandler.post(mLoadingRunnable);

//            }

//        });

// Dump当前的MessageQueue信息.

getMainLooper().dump(new Printer() {

@Override

public void println(String x) {

Log.i("Gracker",x);

}

},"onCreate");

}

private void updateText() {

TraceCompat.beginSection("updateText");

textView1.setText("image1 : w=" + imageView1.getWidth() +

" h =" + imageView1.getHeight());

textView2.setText("image2 : w=" + imageView2.getWidth() +

" h =" + imageView2.getHeight());

textView3.setText("image3 : w=" + imageView3.getWidth() +

" h =" + imageView3.getHeight());

TraceCompat.endSection();

}

我们需要关注两个点:

updateText 这个函数是什么时候被执行的?

App 启动后,三个图片的长宽是否可以被正确地显示出来?

是否有 Delay Load 的效果?

2.1 第一种写法

updateText执行的时机?

下面是第一种写法的Trace图:

可以看到 updateText 是在 Activity 的 onCreate/onStart/onResume三个回调执行完成后才去执行的.

图片的宽高是否正确显示?

从图片看一看到,宽高并没有显示. 这是为什么呢? 这个问题就要从Activity 的 onCreate/onStart/onResume三个回调说起了. 其实Activity 的 onCreate/onStart/onResume三个回调中,并没有执行Measure和Layout操作, 这个是在后面的performTraversals中才执行的. 所以在这之前宽高都是0.

是否有 Delay Load 的效果?并没有. 因为我们知道, 应用启动的时候,要等两次 performTraversals 都执行完成之后才会显示第一帧, 而 updateText 这个方法在第一个 performTraversals 执行之前就执行了. 所以 updateText 方法的执行时间是算在应用启动的时间里面的.

2.2 第二种写法

第二种写法我们Delay了300ms .我们来看一下表现.

updateText执行的时机?

可以看到,这种写法的话,updateText是在两个performTraversals 执行完成之后(这时候 APP 的第一帧才显示出来)才去执行的, 执行完成之后又调用了一次 performTraversals 将 TextView 的内容进行更新.

图片的宽高是否正确显示?

从上图可以看到,图片的宽高是正确显示了出来. 原因上面已经说了,measure/layout执行完成后,宽高的数据就可以获取了.

是否有 Delay Load 的效果?

不一定,取决于 Delay的时长.

从前面的 Trace 图上我们可以看到 , updateText 方法由于 Delay 了300ms, 所以在应用第一帧显示出来170ms之后, 图片的文字信息才进行了更新. 这个是有 Delay Load 的效果的.

但是这里只是一个简单的TextView的更新, 如果是较大模块的加载 , 用户视觉上会有很明显的 “ 空白->内容填充” 这个过程, 或者会附加”闪一下”特效…这显然是我们不想看到的.

有人会说:可以把Delay的时间减小一点嘛,这样就不会闪了. 话是这么说,但是由于 Android 机器的多元性(其实就是有很多高端机器,也有很多低端机器) , 在这个机子上300ms的延迟算是快,在另外一个机子上300ms算是很慢.

我们将Delay时间调整为50ms, 其Trace图如下:

可以看到,updateText 方法在第一个 performTraversals 之后就执行了,所以也没有 Delay Load 的效果(虽然宽高是正确显示了,因为在第一个 performTraversals 方法中就执行了layout和measure).

2.3 第三种写法

经过前两个方法 , 我们就会想, 如果能不使用Delay方法, updateText 方法能在 第二个performTraversals 方法执行完成后(即APP第一帧在屏幕上显示),马上就去执行,那么即起到了 Delay Load的作用,又可以正确显示图片的宽高.第三种写法就是这个效果:

updateText执行的时机?

可以看到这种写法. updateText 在第二个 performTraversals 方法执行完成后马上就执行了, 然后下一个 VSYNC 信号来了之后, TextView就更新了.

图片的宽高是否正确显示?

当然是正确显示的.如图:

是否有 Delay Load 的效果?

从 Trace 图上看, 是有 Delay Load的效果的, 而且可以在应用第一帧显示后马上进行数据 Load , 不用考虑 Delay时间的长短.

3. 一些思考

关于优化的 Delay Load 的实现,从代码层面来看其实是非常简单的.其带来的效果也是很赞的.但是实现之后我们还需要思考一下,为何这么做就可以实现这种功能呢?很显然要回答这个问题,我们需要知道更底层的一些东西.这个还涉及到 Handler/Message/MessageQueue/Looper/VSYNC/ViewRootImpl等知识. 往大里说应该还涉及到AMS/WMS等.由于涉及到的东西比较多,我就不在这一篇里面阐述了, 下一篇文章将会从从原理上讲解一下为何优化的 Delay Load 会起作用.

原文http://androidperformance.com/2015/11/18/Android-app-lunch-optimize-de

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

推荐阅读更多精彩内容