不断学习,做更好的自己!💪

一、App 启动方式

  • 冷启动(Cold start)
    冷启动是指APP在手机启动后第一次运行,或者APP进程被kill掉后在再次启动。可见冷启动的必要条件是该APP进程不存在,这就意味着系统需要创建进程,APP需要初始化。在这三种启动方式中,冷启动耗时最长,对于冷启动的优化也是最具挑战的。因此本文重点谈论的是对冷启动相关的优化。
  • 温启动(Warm start)
    App进程存在,当时Activity可能因为内存不足被回收。这时候启动App不需要重新创建进程,但是Activity的onCrate还是需要重新执行的。场景类似打开淘宝逛了一圈然后切到微信去聊天去了,过了半小时再次回到淘宝。这时候淘宝的进程存在,但是Activity可能被回收,这时候只需要重新加载Activity即可。
  • 热启动(Hot start)
    App进程存在,并且Activity对象仍然存在内存中没有被回收。可以重复避免对象初始化,布局解析绘制。场景就类似你打开微信聊了一会天这时候出去看了下日历 在打开微信 微信这时候启动就属于热启动。

二、从点击 APP 图标到主页显示出现需要经过的步骤

启动流程:

①点击桌面App图标,Launcher 进程采用 Binder IPC向system_server 进程发起 startActivity 请求;

②system_server 进程接收到请求后,向 zygote 进程发送创建进程的请求;

③Zygote 进程 fork 出新的子进程,即App进程;

④App 进程,通过 Binder IPC 向 sytem_server 进程发起 attachApplication 请求;

⑤system_server 进程在收到请求后,进行一系列准备工作后,再通过 binder IPC 向 App 进程发送 scheduleLaunchActivity 请求;

⑥App进程的 binder 线程(ApplicationThread)在收到请求后,通过 handler 向主线程发送 LAUNCH_ACTIVITY 消息;

⑦主线程在收到 Message 后,通过发射机制创建目标 Activity,并回调 Activity.onCreate() 等方法。

⑧到此,App便正式启动,开始进入Activity生命周期,执行完onCreate/onStart/onResume 方法,UI渲染结束后便可以看到App的主界面。

三、启动速度优化

如何对启动时间进行量化?
1.目前为止见过最最牛逼的是使用机械手和高速相机测试,手机开机后使用机械手点击应用桌面图标,高速相机记录启动过程,后续通过程序分析视频,从机械手点击图标到Activity显示出来使用了多少时间。这种方式是最直观和精确的,但是成本也很高。

2.通过shell 命令
​​​adb shell am start -W [packageName]/[packageName.MainActivity]​​​ 执行成功后将返回三个测量到的时间:
ThisTime:一般和TotalTime时间一样,除非在应用启动时开了一个透明的Activity预先处理一些事再显示出主Activity,这样将比TotalTime小。
TotalTime:应用的启动时间,包括创建进程+Application初始化+Activity初始化到界面显示。
WaitTime:一般比TotalTime大点,包括系统影响的耗时。

3.可以通过在代码中增加log来计算启动时间

4.使用systrace

Application OnCrate()优化

1.第三方SDK初始化的处理Application是程序的主入口,很多三方SDK示例程序中都要求自己在Application OnCreate时做初始化操作。这就是增加Application OnCreate时间的主要元凶,所以需要尽量避免在Application onCreate时同步做初始化操作。比较好的解决方案就是对三方SDK就行懒加载,不在Application OnCreate()时初始化,在真正用到的时候再去加载。下面实例对比下ImageLoader在采用懒加载后启动速度优化。
一般我们在使用imageLoader时都会在Application onCreate()时在主线程加载:

public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
ImageLoaderConfiguration.Builder config = new ImageLoaderConfiguration.Builder(this);
ImageLoader.getInstance().init(config.build());
}
}

此时使用adb shell am start -W [packageName]/[packageName.MainActivity]检测应用启动时间,每次执行命令时需要杀死进程。

Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.gyq.myapplication/.MainActivity }
Status: ok
Activity: com.gyq.myapplication/.MainActivity
ThisTime: 423
TotalTime: 423
WaitTime: 441

Total time在423ms之间,下面是封装了一个懒加载ImageLoader的工具类示例:

public class ImageUtil {

private static boolean sInit;

private synchronized static void ensureInit() {
if (sInit) {
return;
}
ImageLoaderConfiguration.Builder config =
new ImageLoaderConfiguration.Builder(SecurityCoreApplication.getInstance());
....
// Initialize ImageLoader with configuration.
ImageLoader.getInstance().init(config.build());
sInit = true;
}

public static void display(String uri, ImageView imageView, boolean cacheOnDisk) {
imageView.setImageResource(R.drawable.icon_app_default);
ensureInit();
ImageLoader loader = ImageLoader.getInstance();
if (cacheOnDisk) {
loader.displayImage(uri, imageView);
} else {
loader.displayImage(uri, imageView, OPTIONS_NO_CACHE_DISK);
}
}

使用这种方案后的启动时间:

Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.gyq.myapplication/.MainActivity }
Status: ok
Activity: com.gyq.myapplication/.MainActivity
ThisTime: 389
TotalTime: 389
WaitTime: 405

看到TotalTime比之前减少了34ms(给出的数据为10次检测平均值)。

所以 Application OnCreate 避免在主线程做大量耗时操作,例如和IO相关的逻辑,这样都会影响到应用启动速度。如果必须要做需要放到子线程中。

Activity onCreate()优化
减少LaunchActivity的View层级,减少View测量绘制时间。
避免主线程做耗时操作

四、用户体验优化

消除启动时的白屏/黑屏
为什么启动时会出现短暂黑屏或白屏的现象?当用户点击你的 app 那一刻到系统调用 Activity.onCreate() 之间的这个时间段内, WindowManager 会先加载 app 主题样式中的 windowBackground 做为 app 的预览元素,然后再真正去加载 activity 的 layout 布局。

很显然,如果你的 application 或 activity 启动的过程太慢,导致系统的 BackgroundWindow 没有及时被替换,就会出现启动时白屏或黑屏的情况(取决于你的主题是Dark还是Light)。

解决方案

  1. 甩锅给系统
    使用透明主题:
<item name="android:windowIsTranslucent">true</item>

Activity.onCreate() 之前 App 不做显示,这样用户误以为是手机慢了,这种瞒天过海的方案大家还是不要用了。

<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>
<item name="android:windowIsTranslucent">true</item>
</style>

</resources>

效果图
【Android -- 性能优化】启动速度优化_启动优化

2.主题替换
我们在style中自定义一个样式Lancher,在其中放一张背景图片,或是广告图片之类的。

<style name="AppTheme.Launcher">
<item name="android:windowBackground">@drawable/bg</item>
</style>

把这个样式设置给启动的 Activity

<activity
android:name=".activity.SplashActivity"
android:screenOrientation="portrait"
android:theme="@style/AppTheme.Launcher"
>

然后在 Activity 的 onCreate 方法,把 Activity 设置回原来的主题

@Override
protected void onCreate(Bundle savedInstanceState) {
//替换为原来的主题,在onCreate之前调用
setTheme(R.style.AppTheme);
super.onCreate(savedInstanceState);
}

效果图
【Android -- 性能优化】启动速度优化_性能优化_02