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

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

· 如果 app 不能快速开发出来,先放出去验证一下可行性,可能连是否值得做都不知道,如果花很长时间做了一个对用户无价值的功能,那么还不如不做

· 如果 app 不能快速做出来,可能被竞争对手捕获先机,那么可能错失最佳商业时机

· 如果一开始就规定不能影响启动速度的这个目标,那么做功能的时候就会有束缚,快不起来

· app 初期大家都忙着开发新功能,迭代新版本,没有时间停下来做优化

· 同类型 app 变多,竞争对手变多,大家才开始关注启动性能,才开始做启动速度优化(有主动出击也有被动优化)

一、引起性能问题的原因

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

高耗时任务
数据库初始化、某些第三方框架初始化、大文件读取、MultiDex加载等,导致CPU阻塞

复杂的View层级
使用的嵌套Layout过多,层级加深,导致View在渲染过程中,递归加深,占用CPU资源,影响Measure、Layout等方法的速度

类过于复杂
Java对象的创建也是需要一定时间的,如果一个类中结构特别复杂,new一个对象将消耗较高的资源,特别是一些单例的初始化,需要特别注意其中的结构

主题及Activity配置
有一些App是带有Splash页的,有的则直接进入主界面,由于主题切换,可能会导致白屏,或者点了Icon,过一会儿才出现主界面

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

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

三、启动速度优化方案

3.1减少应用启动时的耗时

针对冷启动时候的一些耗时,如上测得这个应用算是中型的app,在冷启动的时候耗时已经快700ms了,如果项目再大点在Application中配置了更多的初始化操作,这样将可能达到1s,这样每次启动都明显感觉延迟,所以在进行应用初始化的时候采取以下策略:

· 1、在Application的构造器方法、attachBaseContext()、onCreate()方法中不要进行耗时操作的初始化,一些数据预取放在异步线程中,可以采取Callable实现。

· 2、对于sp的初始化,因为sp的特性在初始化时候会对数据全部读出来存在内存中,所以这个初始化放在主线程中不合适,反而会延迟应用的启动速度,对于这个还是需要放在异步线程中处理。

· 3、对于MainActivity,由于在获取到第一帧前,需要对contentView进行测量布局绘制操作,尽量减少布局的层次,考虑StubView的延迟加载策略,当然在onCreate、onStart、onResume方法中避免做耗时操作。

遵循上面三种策略可明显提高app启动速度。

优化应用启动时的体验

对于应用的启动时间,只能是尽量的避免一些耗时的、非必要的操作在主线程中,这样相对可以缩减一部分启动的耗时,另外一方面在等待第一帧显示的时间里,可以加入一些配置以增加体验,比如加入Activity的background,这个背景会在显示第一帧前提前显示在界面上。
1、先为主界面单独写一个主题style,设置一张待显示的图片,这里我设置了一个颜色,然后在manifest中设置给MainActivity:

<style name="AppTheme.Launcher">
 <item name="android:windowBackground">@drawable/bule</item>
</style>
//...
 <activity
 android:name=".MainActivity"
 android:label="@string/app_name"
 android:theme="@style/AppTheme.Launcher">
 <intent-filter>
 <action android:name="android.intent.action.MAIN" />
 <category android:name="android.intent.category.LAUNCHER" />
 </intent-filter>
 </activity>

2、然后在MainActivity中加载布局前把AppTheme重新设置给MainActivity:

@Override
 protected void onCreate(Bundle savedInstanceState) {
 
 setTheme(R.style.AppTheme);
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
} 

这样在启动时会先显示background,然后待界面绘制完成再显示主界面:

201611591540200.gif

3.2Glide及其他图片框架
Glide是一个很好用的图片加载框架,除了常用的图片加载、缓存功能以外,Glide支持对网络层进行定制,比如换成OkHttp来支持HTTP 2.0。不过,如果在追求启动速度的情况下,在Splash页或主界面加载某一张图片时,往往是第一次使用Glide,由于Glide没有初始化,会导致这次图片加载的时间比较长(不管本地还是网络),特别是在其他操作也在同时抢占CPU资源的时候,慢的特别明显!而后面再使用Glide加载图片时,还是比较快的

Glide初始化耗时分析:Glide的初始化会加载所有配置的Module,然后初始化RequestManager(包括网络层、工作线程等,比较耗时),最后还要应用一些解码的选项(Options)

解决方案:在Application的onCreate方法中,在工作线程调用一次GlideApp.get(this)

override fun onCreate() { super.onCreate() // 使用Anko提供的异步工作协程,或者自行创建一个并发线程池

    doAsync {
        GlideApp.get(this)   // 获取一个Glide对象,Glide内部会进行初始化操作
    }
}

3.3TraceView的使用 - 数据采集与数据分析
TraceView是什么,TraceView 是 Android平台特有的数据采集和分析工具,主要用做热点分析,找出最需要优化的点。TraceView 从代码层面分析性能问题,针对每个方法来分析,比如当我们发现我们的应用出现卡顿的时候,我们可以来分析出现卡顿时在方法的调用上有没有很耗时的操作,通过TraceView,可以得到两种数据。

· 单次执行最耗时的方法

· 执行次数最多的方法


image.png

要打开上面的面板,代码中一般有两种方式:

· 第一种方式:

  1. 首先选择跟踪范围,在想要根据的代码片段之间使用以下两句代码

Debug.startMethodTracing(“hello”);

Debug.stopMethodTracing();
生成的traceview文件会自动放在SDCARD上,没有SDCARD卡会出现异常,所以使用这种方式需要确保应用的AndroidMainfest.xml中的SD卡的读写权限是打开的,其中hello是traceview文件的名字,

  1. 然后用adb导出traceview文件。

adb pull sdcard/hello.trace C:\Users\lwf\Desktop

  1. 然后启动Android Device Monitor-->File-->openFile,打开traceview文件即可。

· 第二种方式:

1.同样是要先打开Android Device Monitor


image.png

2.先选择应用进程,然后点击Start Method Profiling(开启方法分析),按钮会变为Stop Method Profiling(停止方法分析),开启方法分析后,对应用的目标页面进行测试操作,测试完毕后停止方法分析,界面会自动跳转到 DDMS 的 trace 分析界面。

两种方式的对比:第一种方式更精确到方法,起点和终点都是自己定,不方便的地方是自己需要添加方法并且要导出文件,第二种方式的优缺点刚好相反。

四、总结
关于应用启动加速,一般从以下几个方面来入手:

· 利用主题快速显示界面;

· 异步初始化组件;

· 梳理业务逻辑,延迟初始化组件、操作;

· 去掉无用代码、重复逻辑等。

开发过程中,对核心模块与应用阶段如启动时,使用TraceView进行分析,尽早发现瓶颈。

推荐阅读更多精彩内容