一个留得住用户的APP体验感一定要好,应用的启动越快,用户体验自然就好,用户体验差,等了半天都没出来,第一影响就不好,所以我们需要对应用启动以及黑白屏进行处理,快速启动,给用户一个好的体验。最近因为项目需要开始看这个问题,学习一下如何能尽快的启动应用。所以就找来了官方的文档看看,自己翻译和选择性总结了一下。所以本篇文章和官网不是完全相同。如果有哪里不对的,欢迎留言改正。虽然我很多次都希望自己是一个可以把英文一眼就看穿的人,但是能力还有限,所以可能哪里不对。大家多提出来。谢谢!!好吧废话这么多了。开始吧!
首先,应用启动分为三个状态:1.冷启动 2.热启动 3.温启动。其中冷启动是包括了启动APP,创建APP进程,显示startWindow界面的。而热启动和温启动是界面从不可见到可见的过程。我们着重于优化冷启动,当然热启动和温启动也可以优化。
对了,本片文章是Android应用快速启动的系列文章的第一篇,也是基础知识。接下来我会对本文中提到的一些知识,以及工具的使用总结跟大家分享。敬请期待吧. 写系列文章有利于对一个知识点的持续跟踪,对我是一种自律和督促。你也可以试试!看看我能不能坚持下去,如果我可以你也可以!鸡汤一波,加油!
冷启动
只有应用启动了,系统才会创建应用进程。冷启动发生在应用第一次启动或者被系统杀死之后启动的时候。
冷启动的过程:
系统层面
加载app
app加载成功之后,显示一个空白的启动窗口(就是我们平时启动应用看到的黑白屏)。
创建应用进程
系统加载完之后就轮到应用了。
应用层面
创建APP对象
加载主线程
创建主Activity
加载View
布局
绘制视图
一旦App线程完成了绘制,系统就会用主Activity替换到空白的启动窗口。至此,用户可以开始交互了。
从官网上搞了一张图来说明一下启动过程。look
cold-launch.png
在创建APP和创建Activity时容易引发性能问题。
APP Creation
主要工作是创建APP对象,主线程以及主Activity
Activity Creation
1.初始化数据
调用构造函数
调用oncreate
onCreate函数的工作多少和启动时间息息相关。 因为它加载布局,初始化activity运行的对象。
启动性能分析
初始显示的时间
logcat获取时间
从Android4.4(API 19)开始,logcat会打印一条关于acitivty显示时间的信息--Displayed.这个时间包括了启动Activity到Layout显示的过程。包含了一下事件:
启动APP进程
初始化APP对象
创建初始化activity
加载view
首次绘制应用
打印的样子呢就长的跟这个差不多啦!
ActivityManager: Displayed com.android.myexample/.StartupTiming: +3s534ms
注意这个是系统打印的哦,所以不要再logcat里面通过应用包名来过滤,否则你看不到的呢。
** PS:
Logcat输出中的“显示”度量不一定会捕获所有资源加载和显示之前的时间量:它会遗漏布局文件中未引用的资源或应用程序作为对象初始化的一部分创建的资源。 它排除了这些资源,因为加载它们是一个内嵌的过程,并且不会阻止应用程序的初始显示。所以懒加载数据的时间是不会计算到这里面的。切记!!!!**
ADB 命令获取时间
adb [-d|-e|-s ] shell am start -S -W
com.example.app/.MainActivity
-c android.intent.category.LAUNCHER
-a android.intent.action.MAIN
abd管理界面或者串口输入如上命令,就可以获取到启动某个Activity花费的时间。
最简单的语句就是:
adb shell am start -S -W xxxActivity的完整路径
-c,-a使用来指定category和action的可选项。
结果类似:
Starting: Intent
Activity: com.example.app/.MainActivity
ThisTime: 2044
TotalTime: 2044
WaitTime: 2054
Complete
ThisTime:最后一个启动的Activity的启动耗时
TotalTime:自己的所有Activity的启动耗时
WaitTime: ActivityManagerService启动App的Activity时的总时间(包括当前Activity的onPause()和自己Activity的启动)
总的显示时间 (Fulltime)
通过调用reportFullyDrawn()方法可以获取到从应用启动到资源加载完成的全部时间。即包括了懒加载的时间,不过依然记住懒加载是不会影响应用启动的。
log如下:
system_process I/ActivityManager: Fully drawn {package}/.MainActivity: +1s54ms
上面介绍了应用启动的基本知识,下面我们俩看看如何定位应用启动的元凶。
利用AndroidStudio的"Method Tracer tool" 和"Tracer结合Systracer Tool".关于Method Tracer tool的使用可以查看官方的文档MethodTracer.
一般用的比较多的是 Trace和Systrace,因为不一定可以正常使用"Method Tracer TOOL".
常见的问题
谷歌官方列举了经常会影响APP启动性能的常见问题。
Application初始化负载
Applcation oncreate 有很多耗时的操作。比如数据库访问,IO操作之类的。
所以不要在这些方法里面做耗时的操作,解决方案下面将讲解。
问题解决
对于数据的加载,如果不是里面需要的数据,可以改成懒加载方式。对于静态的对象,可以转换成单例对象,这样只会在第一次初始化该对象。官方建议可以使用Dagger来实现依赖注入。这样只会在第一次的时候注入.
Activity初始化负载
Activity创建的时候常常做了很多开销很大的工作。对于这种情况,有很多可以优化的地方。
加载大而复杂的布局
网络操作和磁盘操作堵塞布局绘制
解码加载bitmap
栅格化VectorDrawable对象
初始化activity的其他一些子对象
问题解决
通过tracer定位问题,然后解决。
如果你的布局过于复杂就会加大启动activity的时间,所以尽量优化你的布局
除掉冗余和嵌套的布局,可以通过lint来检测代码,lint会对布局进行检测。
不要加载在启动Activity时,不需要可见的布局,利用ViewStub,通过这个可以在适当的时间加载布局。
在主线程加载所有的资源也会拖慢加载的速度。
在非主线程加载数据,懒加载。当然在不影响你的需求的情况下,把能异步加载的异步加载。
先显示布局,对于图片等的加载可以延迟加载
Activity oncreate,onResume,onstart耗时过多,如果还有上一个Activity,上一个Activity的onPaues也能有太多耗时因为这也会影响APP启动的性能。所以呢不要在这里没进行不必要的耗时操作。
不根治但是实用的解决办法
通过设置theme来避免黑白屏,类似于展示一个闪屏。等Activity加载好之后就会显示Activity。
具体做法:
定义一个Drawable文件,然后在AndroidMainfest.xml文件里面将主题设置给对应的Actvity,最后在该Activity的oncreate方法的setContentView之前将主题设置回来。
代码如下:
drawable示例:
android:src="@drawable/product_logo_144dp"
android:gravity="center"/>
Mainfest file:
android:theme="@style/AppTheme.Launcher" />
Activity onCreate示例:
public class MyMainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
// Make sure this is before calling super.onCreate
setTheme(R.style.Theme_MyApp);
super.onCreate(savedInstanceState);
// ...
}