关闭 x
IT技术网
    技 采 号
    ITJS.cn - 技术改变世界
    • 实用工具
    • 菜鸟教程
    IT采购网 中国存储网 科技号 CIO智库

    IT技术网

    IT采购网
    • 首页
    • 行业资讯
    • 系统运维
      • 操作系统
        • Windows
        • Linux
        • Mac OS
      • 数据库
        • MySQL
        • Oracle
        • SQL Server
      • 网站建设
    • 人工智能
    • 半导体芯片
    • 笔记本电脑
    • 智能手机
    • 智能汽车
    • 编程语言
    IT技术网 - ITJS.CN
    首页 » 安卓开发 »Android应用启动优化:一种DelayLoad的实现和原理

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

    2015-11-22 00:00:00 出处:希尔瓦娜斯女神
    分享

    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图:

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

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

    图片的宽高是否正确显示

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

    从图片看一看到,宽高并没有显示. 这是为什么呢 这个问题就要从Activity 的 onCreate/onStart/onResume三个回调说起了. 其实Activity 的 onCreate/onStart/onResume三个回调中,并没有执行Measure和Layout操作, 这个是在后面的performTraversals中才执行的. 所以在这之前宽高都是0. 是否有 Delay Load 的效果 并没有. 因为我们知道, 应用启动的时候,要等两次 performTraversals 都执行完成之后才会显示第一帧, 而 updateText 这个方法在第一个 performTraversals 执行之前就执行了. 所以 updateText 方法的执行时间是算在应用启动的时间里面的.

    2.2 第二种写法

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

    updateText执行的时机

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

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

    图片的宽高是否正确显示

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

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

    是否有 Delay Load 的效果

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

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

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

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

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

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

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

    2.3 第三种写法

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

    updateText执行的时机

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

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

    图片的宽高是否正确显示

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

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

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

    3. 一些思考

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

    上一篇返回首页 下一篇

    声明: 此文观点不代表本站立场;转载务必保留本文链接;版权疑问请联系我们。

    别人在看

    抖音安全与信任开放日:揭秘推荐算法,告别单一标签依赖

    ultraedit编辑器打开文件时,总是提示是否转换为DOS格式,如何关闭?

    Cornell大神Kleinberg的经典教材《算法设计》是最好入门的算法教材

    从 Microsoft 下载中心安装 Windows 7 SP1 和 Windows Server 2008 R2 SP1 之前要执行的步骤

    Llama 2基于UCloud UK8S的创新应用

    火山引擎DataTester:如何使用A/B测试优化全域营销效果

    腾讯云、移动云继阿里云降价后宣布大幅度降价

    字节跳动数据平台论文被ICDE2023国际顶会收录,将通过火山引擎开放相关成果

    这个话题被围观超10000次,火山引擎VeDI如此解答

    误删库怎么办?火山引擎DataLeap“3招”守护数据安全

    IT头条

    平替CUDA!摩尔线程发布MUSA 4性能分析工具

    00:43

    三起案件揭开侵犯个人信息犯罪的黑灰产业链

    13:59

    百度三年开放2.1万实习岗,全力培育AI领域未来领袖

    00:36

    工信部:一季度,电信业务总量同比增长7.7%,业务收入累计完成4469亿元

    23:42

    Gartner:2024年全球半导体营收6559亿美元,AI助力英伟达首登榜首

    18:04

    技术热点

    iOS 8 中如何集成 Touch ID 功能

    windows7系统中鼠标滑轮键(中键)的快捷应用

    MySQL数据库的23个特别注意的安全事项

    Kruskal 最小生成树算法

    Ubuntu 14.10上安装新的字体图文教程

    Ubuntu14更新后无法进入系统卡在光标界面解怎么办?

      友情链接:
    • IT采购网
    • 科技号
    • 中国存储网
    • 存储网
    • 半导体联盟
    • 医疗软件网
    • 软件中国
    • ITbrand
    • 采购中国
    • CIO智库
    • 考研题库
    • 法务网
    • AI工具网
    • 电子芯片网
    • 安全库
    • 隐私保护
    • 版权申明
    • 联系我们
    IT技术网 版权所有 © 2020-2025,京ICP备14047533号-20,Power by OK设计网

    在上方输入关键词后,回车键 开始搜索。Esc键 取消该搜索窗口。