Android性能优化之启动速度优化

  Android app 启动速度优化,首先谈谈为什么会走到优化这一步,如果一开始创建 app 项目的时候就把这个启动速度考虑进去,那么肯定就不需要重新再来优化一遍了。这是因为在移动互联网时代,大家都追求快,什么功能都是先做出来再说,其他的可以先不考虑,先占据先机,或者验证是否值得做。那为什么要这么做呢?我个人的观点有以下几点

  • 如果 app 不能快速开发出来,先放出去验证一下可行性,可能连是否值得做都不知道,如果花很长时间做了一个对用户无价值的功能,那么还不如不做
  • 如果 app 不能快速做出来,可能被竞争对手捕获先机,那么可能错失最佳商业时机
  • 如果一开始就规定不能影响启动速度的这个目标,那么做功能的时候就会有束缚,快不起来
  • app 初期大家都忙着开发新功能,迭代新版本,没有时间停下来做优化
  • 同类型 app 变多,竞争对手变多,大家才开始关注启动性能,才开始做启动速度优化(有主动出击也有被动优化)

一、引起性能问题的原因

  随着项目不断的快速迭代,往往会造成App启动卡慢现象,因为可能在App主进程启动阶段或者在主界面启动阶段放了很多初始化其他业务的逻辑,而这些业务落地可能一开始并不需要用到。本文从作者的亲身经历给大家阐述启动速度优化相关的点点滴滴,为启动速度优化提供一种思路给大家参考。

二、为什么要做启动速度优化

  App启动卡慢会影响一个App的卸载率和使用率。启动速度快会给人一种轻快的感觉,减少用户等待时间。如果一个App从点击桌面图标到看到主界面花了10秒,请问你能接受么?忍耐不好的估计直接就卸载了,或者没等打开就直接Home键按出去,然后杀进程了。这样一来App卸载率提升了,使用率下降了。所以对于有大量用户的App来说,这些性能细节是很重要的,毕竟用户就是钱啊。

三、分析制定优化技术路线

3.1 分析启动性能瓶颈

  在具体的优化之前,首先我们得找到需要优化的地方,怎么找?这就要求了解Android App的启动原理,我们要知道一个App从点击桌面图标到我们看到App的主界面整个过程中经过了哪些步骤,哪些地方是我们可以优化的地方。下图是App启动过程的一个大概描述。

具体的代码流程,分析关键的函数耗时

  图中onFirstDrawFinish和onWindowFocusChanged的前后顺序可能会颠倒,但是时间差不大。

3.2 制定优化方向

  从上面的分析可以看出,App启动过程中我们优化的地方包括主进程启动流程和主界面启动流程,主进程启动就是Application的创建过程,主界面启动就是MainActivity的创建过程。只需要分别对这两个部分进行优化即可。

  1. Application中attachBaseContext最早被调用,随后是onCreate方法,尽量在这两个方法中不要有耗时操作。

  2. MainActivity中重点关注onCreate,onResume,onWindowFocusChange,Activity启动完成结束标志这里采用没有使用生命周期函数,而是以主界面View的第一次绘制作为启动完成的标志,View被第一次绘制证明View即将展示出来被我们看到。所以我们在Activity根布局中加入一个自定义View,以它的onDraw方法第一次回调作为Activity启动完成的标志。

     public class FirstDrawListenView extends View {
    private boolean isFirstDrawFinish = false; private IFirstDrawListener mIFirstDrawListener; public FirstDrawListenView(Context context, AttributeSet attrs) {
    super(context, attrs);
    } @Override
    protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    if (!isFirstDrawFinish) {
    isFirstDrawFinish = true;
    if (mIFirstDrawListener != null) {
    mIFirstDrawListener.onFirstDrawFinish();
    }
    }
    } public void setFirstDrawListener(IFirstDrawListener firstDrawListener) {
    mIFirstDrawListener = firstDrawListener;
    } public interface IFirstDrawListener {
    void onFirstDrawFinish();
    }
    }

四、怎么统计数据查看优化前后的数据对比

  通过上面的分析,我们可以统计进程启动各个阶段的耗时点,以及Activity启动各个阶段的耗时点(这个步骤需要额外在主布局中加入一个自定义的空View,监听它的onDraw方法的第一次回调),可以通过埋点数据收集这些数据,在优化之前可以先加入埋点数据,统计上报各个时间段的埋点,所以需要先发个版本验证一下优化之前的情况。统计数据的机制加入之后,就可以着手优化了,一边优化一边对比,可以很清楚看到优化前后的对比。

五、制定优化的目标

  由于App启动速度在不同是设备上差别很大,所以目标不太好定,但是做事情总得要有个目标吧。首先我们使用大家都熟悉的一个概念“秒开”,其次是冷启动热启动分开算,再次是分出不同的机型(高端机,中端机型,低端机型),最后是需要先看看没优化之前的启动数据。这样就可以定义出类似下面的目标:

  1. 高端机型1秒内打开(比如小米5,Android6.0以上)
  2. 中端机型1.5秒内打开
  3. 低端机型2.5秒内打开

  上面是终极目标,真正优化的时候,要结合App实际数据以及团队实际情况来定自己的优化目标。

六、优化具体步骤

  一般来说,快速优化最好的方式就是把不必要提前做的操作放到异步线程中去做,也就是我们经常做的异步加载。除了异步加载,一些真正有性能影响的代码需要做具体优化。下面依次介绍一些具体的优化实施步骤。

6.1 封装一个打印耗时点日志的辅助类

  优化的时候为了快速定位耗时的代码块,我们需要在耗时代码块的前后加上日志,统计耗时具体的时间。这个能在Debug模式下帮助我们快速分析定位到耗时的代码块,然后我们在针对具体的耗时代码块去下手,看看怎么优化。

6.2 异步加载一:Application中加入异步线程

  在Application中封装两个方法:onSyncLoad(同步加载)和 onAsyncLoad(异步加载,在Thread中执行),把不需要同步加载的部分全部放到onAsyncLoad方法,需要同步的方法放到onSyncLoad中去做,就这种简单的分类就可以带来一个很好的优化效果。

public class StartUpApplication extends Application {

    @Override
public void onCreate() {
// 程序创建时调用,次方法应该执行应该尽量快,否则会拖慢整个app的启动速度
super.onCreate();
onSyncLoadForCreate();
} @Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
onSyncLoad();
onAsyncLoad();
} private void onSyncLoadForCreate() {
AppStartUpTimeLog.isColdStart = true; // 设置为冷启动标志
AppLog.log("StartUpApplication onCreate");
AppStartUpTimeLog.logTimeDiff("App onCreate start", false, true);
BlockingUtil.simulateBlocking(500); // 模拟阻塞100毫秒
AppStartUpTimeLog.logTimeDiff("App onCreate end");
} private void onSyncLoad() {
AppLog.log("StartUpApplication attachBaseContext");
AppStartUpTimeLog.markStartTime("App attachBaseContext", true);
BlockingUtil.simulateBlocking(200); // 模拟阻塞100毫秒
AppStartUpTimeLog.logTimeDiff("App attachBaseContext end", true);
} public void onAsyncLoad() {
new Thread(new Runnable() {
@Override
public void run() {
// 异步加载逻辑
}
}, "ApplicationAsyncLoad").start();
}
}

6.3 异步加载二:MainActivity中加入异步线程

  这一步骤与Application的优化思路一样,也是封装onSyncLoad和onAsyncLoad方法对现有代码进行一个分类,但是这两个方法的调用时机要晚一点,是在主界面首屏绘制完成的时候调用。这个步骤也需要new一个Thead,属于额外的开销,不过这不影响我们整体性能。

6.4 延迟加载功能:首屏绘制完成之后加载

  还有些操作必须要在UI线程做,但是不需要那么快速就做,这里放到首屏绘制完成之后,我们之前在主布局中加入一个空的View来监听它的第一次onDraw回调,我们通过接口的方式把这个事件接到我们的MainActivity中去(Activity中实现接口的onFirstDrawFinish方法)。为了让用户尽快看到主界面,我们就可以把一些需要在UI线程执行,但是又不需要那么快的执行的操作放到onFirstDrawFinish中去。

6.5 动态加载布局:主布局文件优化

  把主界面中不需要第一次就用到的布局全部使用动态加载的方式来处理,使用ViewStub或者直接在使用时动态addView的方式。

6.6 主布局文件深度优化

  如果做了上面这些优化还是会发现进入主界面还是有些慢,那么需要重点关注主布局文件了。主布局文件的复杂度直接影响到了Activity的加载速度,这个时候需要对主布局文件进行深度优化了。Activity在加载布局的时候,会对整个布局文件进行解析,测量(measure),布局(layout)和绘制(draw),所以设计简单合理的布局尤为重要。布局的优化不做详细介绍,网上很多文章的。几个重要的优化如下:

  1. 减少布局层级
  2. 减少首次加载View的数量
  3. 减少过度绘制

  如果需要看看主布局加载具体用了多少时间,需要用自定ViewGroup作为根布局根元素,然后监控它的onInflateFinished,onMeasure,onLayout,onDraw方法,通过我们之前写好的打印时间日志的辅助类,打印一些关键日志,可以分析出具体的耗时的步骤,还可以定位哪个View加载耗时最长。

6.7 功能代码深度优化

  前面的优化步骤中,我们有部分耗时操作放到了首屏绘制onFirstDrawFinish之后来做了,这里会带来一个体验上的问题,虽然进入主界面变快了,但是可能进入之后短暂的时间类UI线程是阻塞的,如果有其他的UI操作可能会卡主,因为onFirstDrawFinish中挂了很多耗时的操作,需要等这些做完之后UI线程才能空闲。所以我们还需要对一些功能代码进行优化,确保其真正用时少。另外我们异步加载线程中的操作是有一定的安全风险的,如果有些操作很耗时,可能导致我们进入主界面需要用到数据时还没有准备好,所以异步加载我们要注意代码块的顺序,如果有些非常耗时的操作考虑用单独的线程去处理。

七、总结

  优化是一条持续之路,通过优化我们可以了解到影响启动性能的因素有哪些,这样我们平时在编码的过程中就会多注意自己的代码性能。本文从全局的角度去看待整个启动性能优化,看起来好像还挺容易,但是可能实际过程中优化并不会很顺利,不同的设备上可能表现不一样,有时候可能启动一个服务都会耗时。所以要想真正的不耗时,那就是大招:删除它吧。

八、项目地址

模拟耗时点,打印日志观察生命周期函数回调情况

https://github.com/PopFisher/AppStartUpSpeedOpt

Android性能优化之启动速度优化的更多相关文章

  1. 【腾讯Bugly干货分享】Android性能优化典范——第6季

    本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/580d91208d80e49771f0a07c 导语 这里是Android性能优 ...

  2. Android性能优化篇

    很多App都会遇到以下几个常见的性能问题: 启动速度慢:界面跳转慢:事件响应慢:滑动和动画卡顿. 一.启动速度优化. 优化初始化任务: 1. 把一些初始化任务懒加载初始化 2. 把初始化任务并行化(异 ...

  3. 《Android开发艺术探索》读书笔记 (13) 第13章 综合技术、第14章 JNI和NDK编程、第15章 Android性能优化

    第13章 综合技术 13.1 使用CrashHandler来获取应用的Crash信息 (1)应用发生Crash在所难免,但是如何采集crash信息以供后续开发处理这类问题呢?利用Thread类的set ...

  4. Android性能优化(一)之启动加速35%

    一.前言 随着项目版本的迭代,App的性能问题会逐渐暴露出来,而好的用户体验与性能表现紧密相关,从本篇文章开始,我将开启一个Android应用性能优化的专题,从理论到实战,从入门到深挖,手把手将性能优 ...

  5. 爱奇艺技术分享:爱奇艺Android客户端启动速度优化实践总结

    本文由爱奇艺技术团队原创分享,原题<爱奇艺Android客户端启动优化与分析>. 1.引言 互联网领域里有个八秒定律,如果网页打开时间超过8秒,便会有超过70%的用户放弃等待,对Andro ...

  6. Android性能优化问题总结

    性能优化这块,分为UI性能优化.内存优化.数据库优化.网络优化.耗电优化等等.可以从1.如何发现问题,2.怎么解决问题,3.解决效果对比,这几个方面去描述.举个简单例子——UI优化,可以从 UI出现什 ...

  7. Android性能优化系列之App启动优化

    Android性能优化系列之布局优化 Android性能优化系列之内存优化 Android性能优化系列之apk瘦身 应用的启动速度缓慢是我们在开发过程中常常会遇到的问题,比方启动缓慢导致的黑屏.白屏问 ...

  8. Android 性能优化总结

    App 流畅运行,是用户体验的第一步.App 流程运行,涉及到性能优化,主要涉及到布局优化, 绘制优化,内存泄漏优化,响应速度优化,列表展示优化,Bitmap 优化,线程优化,包大小优化. 布局优化 ...

  9. Android 性能优化的方面方面都在这儿

    又到周六了,鸿洋的不定期的周六放送又来了~~这次来谈谈性能优化吧.大家在工作中或多或少都会拿自家的应用和竞品app做比对,不可避免的需要做一些app性能优化的活.很多时候可能是策略上的调整,不过还是有 ...

随机推荐

  1. 201521123010 《Java程序设计》第13周学习总结

    1. 本周学习总结 以你喜欢的方式(思维导图.OneNote或其他)归纳总结多网络相关内容. 2. 书面作业 1. 网络基础 1.1 比较ping www.baidu.com与ping cec.jmu ...

  2. 猴子吃桃问题(南阳ACM324)

    猴子吃桃问题 时间限制:3000 ms  |  内存限制:65535 KB 难度:0 描述 有一堆桃子不知数目,猴子第一天吃掉一半,又多吃了一个,第二天照此方法,吃掉剩下桃子的一半又多一个,天天如此, ...

  3. ROS学习记录(四)————怎样建立一个package包?

    功能包是什么? 英文表述package,我可没有在炫英文啊,我的英文很烂的,只是在提醒大家,在ROS系统中,这个词使用的频率非常之高,你必须记住它,要不就没法正确的看懂信息.言归正传,package是 ...

  4. Linux-exec命令试验驱动(12)

    对于做驱动经常会使用exec来试验驱动,通过exec将-sh进程下的描述符指向我们的驱动,来实现调试 -sh进程常用描述符号: 0:标准输入 1:标准输出 2:错误信息 5:中断服务 exec命令使用 ...

  5. Mybatis第六篇【配置文件和映射文件再解读、占位符、主键生成与获取、Mapper代理】

    配置文件和映射文件再解读 映射文件 在mapper.xml文件中配置很多的sql语句,执行每个sql语句时,封装为MappedStatement对象,mapper.xml以statement为单位管理 ...

  6. java.lang.RuntimeException: java.sql.SQLException: Too many parameters: expected 0, was given 1 Quer

    如果出现类似这样的错误,检查一下是否sql语句和参数的位置对调了. java.lang.RuntimeException: java.sql.SQLException: Too many parame ...

  7. svn清理失败且乱码 问题解决

    由于昨天在网络不好的状态下频繁尝试svn更新,导致今天svn更新时出现:清理失败且乱码的情况如下: 以下是解决方案:1.下载sqlite3.exe ,地址为:http://download.csdn. ...

  8. vmware三种网络格式

    网络地址转换(NAT) 这种访问模式指的是虚拟机不占用主机所在局域网的ip,通过使用主机的NAT功能访问局域网和互联网,意味着虚拟机可以访问局域网中的其他电脑,但是其他电脑不知道虚拟机的存在. 使用这 ...

  9. ”TCP连接“究竟是什么意思?

    我们经常听到"建立TCP连接","服务器的连接数量有限"等,但仔细一想,连接究竟是个什么东西,是和电话一样两端连起一根线?似乎有点抽象不是么? 1. 久违的分组 ...

  10. [LeetCode] 415 Add Strings && 67 Add Binary && 43 Multiply Strings

    这些题目是高精度加法和高精度乘法相关的,复习了一下就做了,没想到难住自己的是C++里面string的用法. 原题地址: 415 Add Strings:https://leetcode.com/pro ...