1、启动图
因此,我们可以 整体的将应用启动分成三个阶段:
-
第一阶段:点击桌面Launcher应用的图标,通过与AMS(ActivityManagerService)通信,启动应用的过程。这段时间,应用开发无能为力。
-
第二阶段:应用Application执行过程。这个阶段Applicaton会执行attachBaseContext(),会执行onCreate回调。这个时候是黑白屏。我们再style的background可以放个图,给一种已经进入app的错觉。
-
第三阶段:启动MainActivity执行过程。显示了应用的主要内容才算app启动完成。ActivityThread中有一个函数handleResumeActivity(),,这个函数调了onResume之后,有个wm.addView(decor,l)。也就是onResume()执行完之后,才会decorView显示到windows上。这个过程之后,
就进入到了可见可交互状态,用户才会觉得app已经运行起来了。
void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,String reason){
... 省略不需要的代码
final ActivityClientRecord r = performResumeActivity(token, finalStateRequest,reason);// 这个里面会间接的调用activity的onResume 函数
...省略不需要的代码
if (a.mVisibleFromClient) {
if (!a.mWindowAdded) {
a.mWindowAdded = true;
wm.addView(decor, l); // 代码1
} else {
a.onWindowAttributesChanged(l);
}
}
...
}
2、优化方向
-
1、第一阶段的优化。主要是launcher和AMS交换,及AMS启动应用的过程,framework做得,咱没法动
-
2、第二阶段的优化。
- part1 attachBaseContext回调方法的优化。apk加固 热修复 等dex操作都在这里处理,要分而治之。目前google对5.0及以后的多dex文件加载做了优化,暂时不用考虑。apk加固等天生需要时间。优化方向就是要降低耗时尽量别加固
- part2 oncreate回调方法的优化。这个是核心。主要处理一些sdk、网络、数据库、SP等。根据业务需求,并不一定都要在这里处理,根据业务需要,将一些不需要立即加载的库放到异步线程中区处理。
- 异步线程?那么jvm,线程切换的时间怎么管理
- 使用线程池,每个sdk自己独立的线程池,如何处理?任务先后怎么处理》
- 有些任务必须在UIThread中初始化怎么办?
- 【用到的技术】拓扑排序图论 – 掘金 (juejin.cn)、线程管理【待补充】、优先级管理【待补充】等技术、
- part3 应用执行到MainActivity之前的白屏处理
-
3、第三阶段的优化
- 第一个activity运行到OnResume函数执行完,才算执行完成。因此onCreate()\onStart()\onResume()都是需要优化的对象。
- onCreate()优化
- 1、布局优化,,简化布局用constraintLayout。
- 2、异步加载试视图 AsyncLayoutInflate
- 3、用composer,因为xml是反射加载的,速度比较慢一点。
- 4、掌阅的X2C,在编译时,生成布局java代码,就不用反射了。
- onStart/onPause
- 1、用缓存下,如果异步加载成功,会更新缓存。
- 2、初始化启动时,最好只用一个接口就拉去启动mainactivity和主activity所需数据。
- 3、懒加载,比如viewpager懒加载。
- 4、IdleHandler,把耗时任务丢到idleHandler里执行,提高速度。主线程没事做才会跑这个。
- 总结就是
- 合理使用异步初始化、延迟初始化、懒加载机制
- 启动过程比卖你耗时操作,比如 数据库I/O操作不要放在主线程执行
- 类加载优化:提前异步执行类加载
- 合理使用IdleHandler进行延迟初始化
- 简化布局
3、总结
-
App的启动耗时= app第一个activity 显示的时候(onWindowFocusChanged函数调用的时刻) — application 执行
-
要优化的点。1、application的attachBaseContext(),onCreate().Activity 的onCreate onstart onResume
4、严苛模式
StrictModel是一个开发人员工具检测出我们可能无意做的事情,并提醒我们注意以便我们能够修复她们。
public class MyApplication extends Application {
@Override
public void onCreate() {
if (BuildConfig.DEBUG) {
//线程检测策略
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads() //读、写操作
.detectDiskWrites()
.detectNetwork() // or .detectAll() for all detectable problems
.penaltyLog()
.build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects() //Sqlite对象泄露
.detectLeakedClosableObjects() //未关闭的Closable对象泄露
.penaltyLog() //违规打印日志
.penaltyDeath() //违规崩溃
.build());
}
}
5、启动黑白屏
当系统加载并启动 App 时,需要耗费相应的时间,这样会造成用户会感觉到当点击 App 图标时会有 “延迟” 现象。
为了解决这一问题,Google 的做法是在 App 创建的过程中,先展示一个空白页面,让用户体会到点击图标之后立
马就有响应
<style name="AppTheme.Launcher">
<item name="android:windowBackground">@drawable/bg</item>
</style>
<activity
android:name=".activity.SplashActivity"
android:screenOrientation="portrait"
android:theme="@style/AppTheme.Launcher">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
然后在Activity的onCreate方法,把Activity设置回原来的主题
@Override
protected void onCreate(Bundle savedInstanceState) {
//替换为原来的主题,在onCreate之前调用
setTheme(R.style.AppTheme);
super.onCreate(savedInstanceState);
}
6、启动状态
- 冷启动
- 从头开始启动
- 热启动
- Activity仍驻在内存中,应用则不必重复执行对象初始化、布局加载和绘制
- 温启动
- 启动时间介于两者之间,
- 用户退出应用后又重新启动应用,进程可能为被销毁,继续运行,但因公许哟啊执行onCreate()从头开始创建Activitiy
- 系统将应用从内存中释放,然后重启。进程和Acitivtiy需要重启,但传递到onCreate()中已保存的实例state bundle对完成任务又一定帮助
- 启动时间介于两者之间,
7、启动耗时
- 冷启动用了 5 秒或更长时间。
- 温启动用了 2 秒或更长时间。
- 热启动用了 1.5 秒或更长时间。
8、系统日志统计
在 Android 4.4(API 级别 19)及更高版本中,logcat 包含一个输出行,其中包含名为 Displayed 的值。此值代表从启动进程到在屏幕上完成对应 Activity 的绘制所用的时间。
ActivityManager: Displayed com.android.myexample/.StartupTiming: +3s534ms
如果我们使用异步懒加载的方式,那么 Displayed日志已经打印,但是内容还在加载中。那么异步加载后可以调用activity.reportFullyDrawn()
方法 来让系统打印到调用此方法位置的启动耗时。
9、adb命令统计
adb [-d|-e|-s <serialNumber>] shell am start -S -W
com.example.app/.MainActivity
-c android.intent.category.LAUNCHER
-a android.intent.action.MAIN
启动完成后,将输出:
- ThisTime: 415
- TotalTime: 415 咱们要关注的。
- WaitTime: 437
WaitTime:总的耗时,包括前一个应用Activity pause的时间和新应用启动的时间;
ThisTime表示一连串启动Activity的最后一个Activity的启动耗时;
TotalTime表示新应用启动的耗时,包括新进程的启动和Activity的启动,但不包括前一个应用Activity pause的耗时。
9、CPU profile java traceview
8.0以下
10、Debug API
除了直接使用 Profifile 启动之外,我们还可以借助Debug API生成trace文件。
public class MyApplication extends Application {
public MyApplication() {
Debug.startMethodTracing("enjoy");
}
//.....
}
public class MainActivity extends AppCompatActivity {
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
Debug.stopMethodTracing();
}
//.......
}
运行App,则会在sdcard中生成一个enjoy.trace文件(需要sdcard读写权限)。将手机中的trace文件保存至电
脑,随后拖入Android Studio即可。