一、应用启动慢的原因
1.在主线程执行了太多耗时的操作,比如加载数据,或者初始化三方库等等,导致在Application的oncreate或者Activity的oncreate方法中耗时太久
2.布局嵌套太深,或者一些不会立即使用的布局也在一开始一起加载到内存中
二、启动耗时统计
1.使用adb命令方式统计启动时间
powershell
adb shell amstart -S -W com.example.MainActivity
-S 表示杀掉当前进程然后重启该应用
-W 表示开启应用启动耗时日志
对应的打印如下:
powershell
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.example.appstartdemo/.MainActivity }
Warning: Activity not started, its current task has been brought to the front
Status: ok
Activity: com.example.appstartdemo/.MainActivity
ThisTime: 67
TotalTime: 67
WaitTime: 87
Complete
2.直接查看系统日志,在对应的DisPlayed关键字中,会统计应用启动到首个Activity绘制到屏幕上的时间,如下打印:
powershell
ActivityManager: Displayed com.example.appstartdemo/.MainActivity: +401ms
三、启动耗时分析
使用Android Studio自带的Profile,选择Trace Java Methods,然后开启Record,或者在Run 的时候选择Profile,就可以生成一段时间内的方法调用栈记录文件。我们通常可以用如下几种方式去打开这个Record文件:
1.火焰图(Frame Chart)方式
它会把相同的调用栈合并,然后根据执行的时间占比,生成一个个柱状图形,总体像火焰的样子。如下:
X轴:方法调用的时间占比,或者更精确地叫抽样数,即X轴越宽,则被抽中采样的次数越多,方法所消耗的时间就越长。
Y轴:表示调用栈,方法的调用链是由下而上的,每一个Y值都表示一个方法。
因此越在底部的方法,占用的时间就越多,因为这是方法的总入口。
柱子越高,说明调用链越深。
我们应该着重关注"平顶山"的情况,它表示柱子顶部X轴占比比较大,花费的时间比较长,这是消耗CPU的关键所在。
2.Top Down方式
显示一个调用链表中各个方法的具体执行耗时的时间值(微妙μs = 1000/1ms = 百万分之一秒),显示效果如下:
Total:表示该方法执行总体的耗时
Self:执行该方法本身的代码耗时,不包含子函数
Children:子函数的耗时
通过Top Down查看调用链,可以去分析耗时比较多的方法,然后去重点关注和优化。
四、启动性能优化方法
1.黑白屏的优化
由于冷启动一个新的应用,会存在一定的耗时,Google为了避免让程序看起来有延迟的情况出现,即用户点击图标后就会有即时的响应,会在App创建的过程中先显示一个空白页作为过渡。根据主题的不同,就会出现黑屏或者白屏时间比较长的情况,这是大部分用户无法容忍的,需要做优化。
优化的推荐方法是,首先自定义一个主题,继承当前的主题,然后设置它的windowbackground属性为一张跟业务相关的图片,也就是我们常见的欢迎界面,如下:
powershell
# 创建新的主题
<resources>
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
</style>
<style name="MyTheme" parent="AppTheme">
<item name="android:windowFullscreen">true</item>
<item name="android:windowBackground">@drawable/launch_bg</item>
</style>
</resources>
然后在manifest文件中设置该主题:
powershell
# 在清单文件中给Activity设置主题
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.example.appstartdemo">
<application
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
android:theme="@style/MyTheme">
</activity>
</application>
</manifest>
设置了windowbackground属性后,那么图片会一直存在;在启动完Activity之后,需要把主题重新设置回默认主题,避免背景图还一直存在。
powershell
# Activity启动后,设置回默认的主题
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
// 模拟耗时操作,显示更加的明显
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
setTheme(R.style.AppTheme);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
2.主线程的耗时优化
2.1避免在Application的oncreate方法或者Activity的oncreate方法中,去做过多的耗时操作,比如数据加载、网络请求、第三方库的初始化等等
2.2使用idleHandler去做耗时的数据加载和网络请求、三方库的初始化
2.3使用懒加载,只有在用到的时候,才去加载数据
3.布局优化
3.1使用include标签,去包含可以重复使用的布局,减少XML代码的冗余
3.2使用merge标签去作为被include的子布局文件的顶层布局,这样在被合并到主布局之后,就可以减少一层布局的嵌套
3.3使用ViewStub标签来引入一个子布局文件,最开始是不可见的,尺寸大小为0的控件。在启动Activity加载布局的时候,并不会把ViewStub的内容加载到View树里面,只有在对ViewStub执行setvisibility或者inflate的时候,才会去真正加载布局。这相当于是View的一种懒加载,对提高启动性能很有帮助。
对于那些最开始并不会用到的子View,或者那些一开始就设置gone的子View,适合使用ViewStub。