一、启动流程
Activity启动流程①、点击桌面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的主界面。
二、启动状态
应用有三种启动状态,每种状态都会影响应用向用户显示所需的时间:冷启动
、温启动
与热启动
。在冷启动中,应用从头开始启动。在另外两种状态中,系统需要将后台运行的应用带入前台。建议始终在假定冷启动的基础上进行优化。这样做也可以提升温启动和热启动的性能。
-
冷启动:
冷启动是指应用从头开始启动:系统进程在冷启动后才创建应用进程。发生冷启动的情况包括应用自设备启动后或系统终止应用后首次启动。 -
热启动:
在热启动中,系统的所有工作就是将 Activity 带到前台。只要应用的所有 Activity 仍驻留在内存中,应用就不必重复执行对象初始化、布局加载和绘制。 -
温启动:
温启动包含了在冷启动期间发生的部分操作;同时,它的开销要比热启动高。有许多潜在状态可视为温启动。例如:
用户在退出应用后又重新启动应用。进程可能未被销毁,继续运行,但应用需要执行
onCreate()
从头开始重新创建 Activity。系统将应用从内存中释放,然后用户又重新启动它。进程和 Activity 需要重启,但传递到
onCreate()
的已保存的实例savedInstanceState
对于完成此任务有一定助益。
三、启动耗时统计
3.1、系统日志统计
在 Android 4.4(API 级别 19)及更高版本中,logcat 包含一个输出行,其中包含名为 Displayed
的值。此值代表从启动进程到在屏幕上完成对应 Activity 的绘制所用的时间。
3.2、adb命令统计
adb shell
am start -S -R -W [包名]/[启动的Activity全类名、或在一个包下直接 .Activity]
-
S
:表示每次启动前先强制杀死应用进程,达到每次都是冷启动,一定要大写 -
R
:表示重复次数,一定要大写 -
W
:表示是否等待输出日志信息,一定要大写 - 后面接包名加Activity名称;要启动的Activity一定要有< intent-filter>标签或者android:exported="true"属性,否则会报SecurityException: Permission Denial异常
开发者一般只要关心TotalTime即可,这个时间才是自己应用真正启动的耗时。
3.3、启动耗时统计
adb 命令启动应用,一般会输入三个值:ThisTime
、TotalTime
与WaitTime
。
-
WaitTime
:包括前一个应用Activity pause的时间和新应用启动的时间; -
ThisTime
:表示一连串启动Activity的最后一个Activity的启动耗时; -
TotalTime
:表示新应用启动的耗时,包括新进程的启动和Activity的启动,但不包括前一个应用Activity pause的耗时
四、CPU Profile
-
Debug API
除了直接使用 Profile 启动之外,我们还可以借助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即可。
三、StrictMode
StrictMode是一个开发人员工具,它可以检测出我们可能无意中做的事情,并将它们提请我们注意,以便我们能够修复它们。
StrictMode最常用于捕获应用程序主线程上的意外磁盘或网络访问。帮助我们让磁盘和网络操作远离主线程,可以使应用程序更加平滑、响应更快。
public class MyApplication extends Application {
public MyApplication() {
}
@Override
public void onCreate() {
if (BuildConfig.DEBUG) {
//线程检测策略
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads() //读、写操作
.detectDiskWrites()
.detectNetwork() // or .detectAll() for all detectable problems
.penaltyLog()
.penaltyDeath()
.build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects() //Sqlite对象泄露
.detectLeakedClosableObjects() //未关闭的Closable对象泄露
.penaltyLog() //违规打印日志
.penaltyDeath() //违规崩溃
.build());
}
super.onCreate();
}
}
六、冷启动白屏问题
当系统加载并启动 App 时,需要耗费相应的时间,这样会造成用户会感觉到当点击 App 图标时会有 “延迟” 现象,为了解决这一问题,Google 的做法是在 App 创建的过程中,先展示一个空白页面,让用户体会到点击图标之后立马就有响应。
如果你的application或activity启动的过程太慢,导致系统的BackgroundWindow没有及时被替换,就会出现启动时白屏或黑屏的情况(取决于Theme主题是Dark还是Light)。
消除启动时的黑/白屏问题,大部分App都采用自己在Theme中设置背景图的方式来解决。
<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="windowNoTitle">true</item>
</style>
<style name="AppTheme.Launcher">
<item name="android:windowBackground">@drawable/bg</item>
<item name="android:windowFullscreen">true</item>
</style>
</resources>
<activity android:name=".MainActivity" 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);
}
七、总结
启动速度优化也会涉及到布局优化与卡顿优化,包括内存抖动等问题。优化是一条持续的道路,很多时候我们会发现通过各种检测手段花费了大量的精力去对代码进行修改得到的优化效果可能并不理想。因为优化就是一点一滴积累下来的,我们平时在编码的过程中就需要多注意自己的代码性能。
可能实际过程中优化并不会很顺利,不同的设备上可能表现不一样。我们只能结合对业务、对自己代码的了解去不断去实践。
-
1)、合理的使用异步初始化、延迟初始化、懒加载机制。
-
2)、启动过程避免耗时操作,如数据库 I/O操作不要放在主线程执行。
-
3).、类加载优化:提前异步执行类加载。
-
4)、合理使用IdleHandler进行延迟初始化。
-
5)、简化布局
网友评论