Androidt图形绘制原理从底层到上层学习(三) 绘制完成到第一帧显示到屏幕时activity所处的生命周期

转载 2018年04月14日 18:00:20

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

http://androidperformance.com/2015/11/18/Android-app-lunch-optimize-delay-load.html


    文章作者本来是在分析--《Android应用启动优化:一种DelayLoad的实现和原理在应用第一帧显示后马上进行数据 Load , 不用考虑 Delay时间的长短的时间优化,因为优化之前是通过人为的定一个时间点,这种方式非常不好,因此需要在图形出现的第一时间进行。

    但是文中将Android图形第一帧是何时渲染到屏幕上的时间点分析的非常到位,前面两篇文章已经将Android图形底层是如何绘制讲的很清楚了,但是它第一帧图片是在哪个时间点出现的呢?因此还需要一篇文章来梳理下第一帧绘制到屏幕时,处在activity的哪个时间点。


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

  1. 异步加载
  2. 延时加载
  3. 懒加载

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

1. 优化后的DelayLoad的实现

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

这里先说优化方案:

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

    1
    2
    3
    4
    5
    6
    7
    8
    private Handler myHandler = new Handler();
    private Runnable mLoadingRunnable = new Runnable() {
    @Override
    public void run() {
    updateText(); //更新UI线程
    }
    };
  2. 在主 Activity 的 onCreate 中加入下面的代码

    1
    2
    3
    4
    5
    6
    7
    getWindow().getDecorView().post(new Runnable() {
    @Override
    public void run() {
    myHandler.post(mLoadingRunnable);
    }
    });

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

2. 三种写法的差异对比

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
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 第一种写法

  1. updateText执行的时机?
    下面是第一种写法的Trace图:
    第一种写法第一种写法
    可以看到 updateText 是在 Activity 的 onCreate/onStart/onResume三个回调执行完成后才去执行的.

  2. 图片的宽高是否正确显示?
    第一种写法第一种写法

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

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

2.2 第二种写法

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

  1. updateText执行的时机?
    第二种写法第二种写法

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

  2. 图片的宽高是否正确显示?
    第二种写法第二种写法

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

  3. 是否有 Delay Load 的效果?
    不一定,取决于 Delay的时长.
    从前面的 Trace 图上我们可以看到 , updateText 方法由于 Delay 了300ms, 所以在应用第一帧显示出来170ms之后, 图片的文字信息才进行了更新. 这个是有 Delay Load 的效果的.
    但是这里只是一个简单的TextView的更新, 如果是较大模块的加载 , 用户视觉上会有很明显的 “ 空白->内容填充” 这个过程, 或者会附加”闪一下”特效…这显然是我们不想看到的.

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

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

    第二种写法:Delay 50ms第二种写法:Delay 50ms

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

2.3 第三种写法

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

  1. updateText执行的时机?

    第三种写法第三种写法

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

  2. 图片的宽高是否正确显示?
    当然是正确显示的.如图:
    第三种写法第三种写法

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


Androidt图形绘制原理从底层到上层学习(一) 硬件加速相关

转载自:Android硬件加速原理与实现简介https://blog.csdn.net/u011403718/article/details/54630760在手机客户端尤其是Android应用的开发...
  • cpcpcp123
  • cpcpcp123
  • 2018-04-14 17:19:11
  • 12

View的生命周期方法和Activity生命周期方法关系

View 是在Activity 中使用到的,所以在自定义View的时候,我们需要了解Activity 生命周期方法和View的生命周期方法调用先后顺序。 见如下图 (1) 在Activit...
  • lue2009
  • lue2009
  • 2015-05-13 13:26:41
  • 8807

Android View绘制和显示原理简介

现在越来越多的应用开始重视流畅度方面的测试,了解Android应用程序是如何在屏幕上显示的则是基础中的基础,就让我们一起看看小小屏幕中大大的学问。这也是我下篇文章——《Android应用流畅度测试分析...
  • zhangcanyan
  • zhangcanyan
  • 2016-10-14 18:08:00
  • 3692

Android从上层到底层完整流程

  • 2013年12月07日 12:06
  • 22KB
  • 下载

maven的三大生命周期

maven的三大生命周期 一、Maven的生命周期 Maven的生命周期就是对所有的构建过程进行抽象和统一。包含了项目的清理、初始化、编译、测试、打包、集成测试、验证、部署和站点生成等几乎所有的构...
  • weixin_33400820
  • weixin_33400820
  • 2017-12-23 16:12:50
  • 37

Android开发_Activity在屏幕旋转时的生命周期

Hello,我是杨焕州,你没听错,传说中的杨焕州就是我啦!——QQ:804212028 原文链接 主题:Activity在屏幕旋转时的生命周期 -不同条件下,旋转屏幕时,activity的生命周...
  • y18334702058
  • y18334702058
  • 2015-03-23 17:14:44
  • 2125

Android 屏幕旋转生命周期以及处理方法

工作期间项目需要经常旋转屏幕,还有平板。所以会经常考虑Android 屏幕旋转生命周期,所以也有相应的处理方法。主要有两种办法解决。是根据屏幕旋转的生命周期具体使用。屏幕旋转时候,如果不做任何处理,a...
  • qq_16064871
  • qq_16064871
  • 2015-06-13 09:12:33
  • 7615

AlertDialog,Toast对Activity生命周期的影响

转自:http://blog.csdn.net/scorpioneal/article/details/19049475经常可以在网上看到一些文章介绍Activity生命周期, 说只要一个Activi...
  • u011348999
  • u011348999
  • 2017-02-20 12:25:36
  • 4136

怎么判断一帧的开始

一帧可能有几个SLICE的!你要把所有的SLICE定位出来,然后再找到每个SLICE的起始宏块的地址,地址为0的话就是一帧开始了!   的确,除去sps和pps,单纯从0x0000000101和0x...
  • cffishappy
  • cffishappy
  • 2011-11-08 16:29:36
  • 830

Activity锁屏状态下的生命周期

Activity锁屏状态下的生命周期         关于Activity生命周期已经讲过两节,有兴趣的大家可以看看。这一篇说一下Activity在锁屏状态下时的生命周期。对于锁屏状态,我们分两...
  • xgangzai
  • xgangzai
  • 2016-12-04 10:42:05
  • 5149
收藏助手
不良信息举报
您举报文章:Androidt图形绘制原理从底层到上层学习(三) 绘制完成到第一帧显示到屏幕时activity所处的生命周期
举报原因:
原因补充:

(最多只允许输入30个字)