APP启动流程
启动时间
当Activity执行到onResume方法,再等待到View树构建完成,此时才算app完全启动
在log日志中 查看叫 Displayed 日志 过滤一定要是 NoFilters
命令行查看(需要Android4.4以后手机)
使用adb shell am start -w <包名>可以统计应用启动时间,可以得到以下一些参数
ThisTime:最后一个启动的Activity的启动耗时
TotalTime:自己的所有Activity的启动耗时
WaitTime:ctivityManagerService启动App的Activity时的总时间(包括当前Activity的onPause()和自己Activity的启动)
计算启动时间——Screen Record
通过 adb shell screenrecord -bugreport /sdcard/test.mp4录制屏幕
开始后,屏幕左上方 出现例如15:31:22.261 f=171(0) 15:31:22。261 表示当前时间,171表示第几帧,0表示掉帧率
启动优化方案
系统创建进程部分与rom相关,这部分无法优化,能够优化部分是应用部分
Application
作为程序主入口,很多开发人员喜欢在此处做一些初始化动作。特别是一些第三方SDK需在onCreate方法进行初始化,
当库比较多时,必然带来Application的负荷。
针对这种情况,可以通过以下方式解决
1.延迟初始化
2.开启后台任务
3.进行界面预加载
具体分析耗时
1.可以通过一些工具,如traceView分析耗时的方法和组件
2.按功能和需求,对每个加载库,选择合适的启动时机
例如
1、在Application的构造器方法、attachBaseContext()、onCreate()方法中不要进行耗时操作的初始化,一些数据预取放在异步线程中,可以采取Callable实现。
2、对于sp的初始化,因为sp的特性在初始化时候会对数据全部读出来存在内存中,所以这个初始化放在主线程中不合适,反而会延迟应用的启动速度,对于这个还是需要放在异步线程中处理。
3、对于MainActivity,由于在获取到第一帧前,需要对contentView进行测量布局绘制操作,尽量减少布局的层次,考虑StubView的延迟加载策略,当然在onCreate、onStart、onResume方法中避免做耗时操作。
Theme
当系统加载一个activity,针对耗时操作,系统会先读取当前activity的主题,然后根据theme的主题绘制,当activity加载完成,
才替换为真正的界面
所以,Google官方提供的解决方案,就是通过android:windowBackground属性,来进行加载前的配置,同时,这里不仅可以配置颜色,还能配置图片,例如,我们可以使用一个layer-list来作为android:windowBackground要显示的图:
layer-list xmlns:android="http://schemas.android.com/apk/res/android"
android:opacity="opaque">
<item android:drawable="@android:color/darker_gray"/>
<item>
<bitmap
android:gravity="center"
android:src="@mipmap/ic_launcher"/>
</item>
</layer-list>
可以看见,这里通过layer-list来实现图片的叠加,让开发者可以自由组合。
接下来可以设置一个新的Style,这个Style就是Activity预加载的Style。
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
<style name="StartStyle" parent="AppTheme">
<item name="android:windowBackground">@drawable/start_window</item>
</style>
</resources>
OK,下面在Mainifest中给Activity指定需要预加载的Style:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.xys.startperformancedemo">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
android:theme="@style/StartStyle">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
这里需要注意下,一定是Activity的Theme,而不是Application的Theme。
最后,我们在Activity加载真正的界面之前,将Theme设置回正常的Theme就好了:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
setTheme(R.style.AppTheme);
super.onCreate(savedInstanceState);
SystemClock.sleep(2000);
setContentView(R.layout.activity_main);
}
}
异步初始化
这个很简单,就是让App在onCreate里面尽可能的少做事情,而利用手机的多核特性,尽可能的利用多线程,例如一些第三方框架的初始化,如果能放线程,就尽量的放入线程中,最简单的,你可以直接new Thread(),当然,你也可以通过公共的线程池来进行异步的初始化工作,这个是最能够压缩启动时间的方式
延迟初始化
延迟初始化并不是减少了启动时间,而是让耗时操作让位、让资源给UI绘制,将耗时的操作延迟到UI加载完毕后,所以,这里建议通过mDecoView.post方法,来进行延迟加载,代码如下:
getWindow().getDecorView().post(new Runnable() {
@Override public void run() {
……
}
});
我们的ContentView就是通过mDecoView.addView加入到根布局的,所以,通过这种方式,可以让延迟加载的内容,在ContentView初始化完毕后,再进行执行,保证了UI绘制的流畅性。
IntentService
IntentService是继承于Service并处理异步请求的一个类,在IntentService的内部,有一个工作线程来处理耗时操作,启动IntentService的方式和启动传统Service一样,同时,当任务执行完后,IntentService会自动停止,而不需要去手动控制。
public class InitIntentService extends IntentService {
private static final String ACTION = "com.xys.startperformancedemo.action";
public InitIntentService() {
super("InitIntentService");
}
public static void start(Context context) {
Intent intent = new Intent(context, InitIntentService.class);
intent.setAction(ACTION);
context.startService(intent);
}
@Override
protected void onHandleIntent(Intent intent) {
SystemClock.sleep(2000);
Log.d(TAG, "onHandleIntent: ");
}
}
我们将耗时任务丢到IntentService中去处理,系统会自动开启线程去处理,同时,在任务结束后,还能自己结束Service,多么的人性化!OK,只需要在Application或者Activity的onCreate中去启动这个IntentService即可:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
InitIntentService.start(this);
}
使用ActivityLifecycleCallbacks
Framework提供的这个方法可以监控到所有Activity的生命周期,在这里,我们就可以通过onActivityCreated这样一个回调,来将一些UI相关的初始化操作放到这里,同时,通过unregisterActivityLifecycleCallbacks来避免重复的初始化。同时,这里onActivityCreated回调的参数Bundle,可以用来区别是否是被系统所回收的Activity。
其他方案
下面是两种不同的方案,都是在Style中进行配置:
<item name="android:windowDisablePreview">true</item>
与
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowNoTitle">true</item>