参考文档
- 作者:Android帅次
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
解决问题的基本思路
发现问题 ==> 定位问题 ==> 改善问题 ==> 验证问题
背景
随着我们开发项目的游戏包越来越大,app冷启动的问题耗时也是越来越严重,在高端手机上还好,但是在低端手机上的问题会越来越明显,毕竟启动快慢在一定程度上是玩家体验很重要的一环。我们尽量Unity和Android角度尽可能去分析。
Android的启动机制
我们需要这几种启动的方式有什么不同,这块不会很复杂,大部分同学都是接触过。
应用有三种启动状态:
冷启动:
温启动:
温启动介于冷启动和热启动中间吧。例如:
用户按返回键退出应用,然后重新启动。进程可能还没有被杀死,但应用必须通过调用onCreate()重新创建 Activity。
系统回收了应用的内存,然后用户重新运行应用。应用进程和Activity都需要重新启动。
热启动:
热启动时,系统将应用从后台拉回前台,应用程序的 Activity 在内存中没有被销毁,那么应用程序可以避免重复对象初始化,UI的布局和渲染。
如果 Activity 被销毁则需要重新创建。
和冷启动的区别:不需要创建 Application。
启动分析
这块我们主要是对于这启动流程的剖析,从用户点击图标开始,整个启动过程经过哪
几个关键阶段,又会给用户带来哪些体验问题。
其实就是作为开发者要知道哪些是我们可以干预的,哪些是干预不了的。
冷启动
我们针对的主要还是冷启动这块,先简单的剖析下启动的流程。
T1 预览窗口显示。系统在拉起微信进程之前,会先根据微信的 Theme 属性创建预览窗口。当然如果我们禁用预览窗口或者将预览窗口指定为透明,用户在这段时间依然看到的是桌面。
T2 闪屏显示。在微信进程和闪屏窗口页面创建完毕,并且完成一系列 inflate view、onmeasure、onlayout 等准备工作后,用户终于可以看到熟悉的“小地球”。
T3 主页显示。在完成主窗口创建和页面显示的准备工作后,用户可以看到微信的主界面。
T4 界面可操作。在启动完成后,微信会有比较多的工作需要继续执行,例如聊天和朋友圈界面的预加载、小程序框架和进程的准备等。在这些工作完成后,用户才可以真正开始愉快地聊天。
冷启动之前(关于android启动Api,后面我们会单独开一章来讲)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| 一. Step1 - Step 11: Launcher通过Binder进程间通信机制通知ActivityManagerService, 它要启动一个Activity;
二. Step 12 - Step 16: ActivityManagerService通过Binder进程间通信机制通知Launcher进入Paused状态;
三. Step 17 - Step 24: Launcher通过Binder进程间通信机制通知ActivityManagerService,它已经准备就绪进入Paused状态, 于是ActivityManagerService就创建一个新的进程,用来启动一个ActivityThread实例, 即将要启动的Activity就是在这个ActivityThread实例中运行;
四. Step 25 - Step 27: ActivityThread通过Binder进程间通信机制将一个ApplicationThread类型的Binder对象传递给ActivityManagerService, 以便以后ActivityManagerService能够通过这个Binder对象和它进行通信;
五. Step 28 - Step 35: ActivityManagerService通过Binder进程间通信机制通知ActivityThread, 现在一切准备就绪,它可以真正执行Activity的启动操作了。
|
首先,会启动App
然后,加载空白Window
最后,创建进程
需要注意的是,这些都是系统的行为,一般情况下我们是无法直接干预的。
随后任务
首先,创建Application
启动主线程
创建MainActivity
加载布局
布置屏幕
首帧绘制
通常到了界面首帧绘制完成后,我们就可以认为启动已经结束了。
优化方向
我们的优化方向就是 Application和Activity的生命周期 这个阶段,因为这个阶段的时机对于我们来说是可控的
优化核心思想
启动优化是对 启动流程的那些步骤进行优化呢?
作为app的使用用户,不论你的app是游戏还是电商,用户关心的是当我的手机按下桌面的图标后,app的内容要尽快的显示出来。
特别像打车遇到高峰期,抢单软件和游戏app或者做定时活动的时候,启动响应太慢,这用户群体基本上就凉了一半,不能忽视其重要性。
根据启动流程的分析,显示页面能和用户交互,这是主线程做的事情。那么就要求 我们不能再主线程做耗时的操作。启动中的系统任务我们无法干预,能干预的就是在创建应用和创建 Activity 的过程中可能会出现的性能问题。这一过程具体就是:
关于 Activity,Application生命周期具体可以看这里
Android生命周期
关于Activity启动模式 : Activity 启动模式
1 2 3 4 5 6
| App = Application App ==> attachBaseContext App ==> onCreate Activity ==> onCreate Activity ==> onStart Activity ==> onResume
|
启动检测
使用adb shell获取应用的启动时间
1
| adb shell am start -W packageName]/AppstartActivity
|
ThisTime
表示最后一个Activity启动耗时。
TotalTime
表示所有Activity启动耗时。
WaitTime
表示AMS启动Activity的总耗时。
一般来说,只需查看得到的TotalTime,即应用的启动时间,其包括 创建进程 + Application初始化 + Activity初始化到界面显示 的过程。
1 2 3 4
| 作者:jsonchao 链接:https://juejin.cn/post/6844904093786308622 来源:稀土掘金 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
|
Java-Code 线下测
你可以使用 reportFullyDrawn() 方法来测量应用程序启动和所有资源和视图层次结构的完整显示之间经过的时间。在应用程序执行延迟加载的情况下,这可能很有价值。在延迟加载中,应用程序不会阻止窗口的初始绘制,而是异步加载资源并更新视图层次结构。
这里我在Activity.onCreate()中加了个工作线程。并在里面调用reportFullyDrawn() 方法。代码如下
作者:Android帅次
链接:https://juejin.cn/post/7020245974962405412
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| new Thread(new Runnable() { @Override public void run() { try { reportFullyDrawn(); } catch (InterruptedException e) { e.printStackTrace(); } } }).start();
作者:Android帅次 链接:https://juejin.cn/post/7020245974962405412 来源:稀土掘金 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
|
白屏或者黑屏
这块我就不写了,网上一堆,自己google一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| //优点:避免启动app时白屏黑屏等现象 //缺点:容易造成点击桌面图标无响应 //(可以配合三方库懒加载,异步初始化等方案使用,减少初始化时长) //实现如下 //0. appTheme <!-- Base application theme. --> <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar"> <!-- Customize your theme here. --> <item name="colorPrimary">@color/c_ff000000</item> <item name="colorPrimaryDark">@color/c_ff000000</item> <item name="colorAccent">@color/c_ff000000</item> <item name="android:windowActionBar">false</item> <item name="android:windowNoTitle">true</item> </style> //1. styles.xml中设置 //1.1 禁用预览窗口 <style name="AppTheme.Launcher"> <item name="android:windowBackground">@null</item> <item name="android:windowDisablePreview">true</item> </style> //1.2 指定透明背景 <style name="AppTheme.Launcher"> <item name="android:windowBackground">@color/c_00ffffff</item> <item name="android:windowIsTranslucent">true</item> </style> //2. 为启动页/闪屏页Activity设置theme <activity android:name=".splash.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> //3. 在该Activity.onCreate()中设置AppTheme(设置布局id之前) //比如我是基类中单独抽取的获取布局id方法,那么在启动页中重写此方法时加入如下配置: @Override protected int getContentViewId() { setTheme(R.style.AppTheme_Launcher); return R.layout.activity_splash; }
|
MultiDex
参考:简书-MultiDex
提出一个疑问 :主进程如何得知加载进程完成加载?
如何优化