文章目录
- 概述
- 历史
- 黑白屏出现原因
- Android系统架构
- App启动流程
- Zygote 进程
- Launcher应用启动
- app白屏和黑屏的一般解决方案
- Trace工具分析代码执行时间
- 查看页面启动时间
- APP启动时间优化
- 引用文章
概述
我们在很多APP启动的时候都能看到一闪而过的黑屏或者白屏,在我们这些强迫症深度患者看来,确确实实是个很糟糕的体验。但是在我们打开QQ或者一些优化过的应用的时候却看不到所谓的黑屏或白屏。我们今天就来探讨下这个黑白屏启动优化
历史
在最早的Android机器上, 点击需要一定的时间来响应,然后启动。但是在这 App 未完全启动的时候,用户不能明确 App 是否已经启动,为了解决这个用户体验的问题,特意加上了启动黑白屏来表示 App 已经启动。
但是解决了点击无反应的问题,又出现了黑白屏体验不好的问题,方法总比问题多,有的应用设置背景为透明,有的应用放置图片,总之各有妙招
黑白屏出现原因
当我们设置下面主题属性时,会出现首次启动白屏或者黑屏的问题
白屏 <style name="AppTheme" parent="Theme.AppCompat.Light">
黑屏 <style name="AppTheme">(在以前的老版本上有效,现在的版本默认使用透明处理了)我们点击Theme.AppCompat.Light一直追溯,找到一个父类name="Platform.AppCompat.Light"中定义了<item name="android:windowBackground">用来控制黑白屏,我们能从下面图中看到windowBackground被设置为@color/background_material_light

当从launcher启动一个APP时,需要fork一个进程并进行一些初始化,而fork进程和初始化是需要时间的,这时候就有了StartingWindow(也称之为PreviewWindow)的出现,
StartingWindow一般出现在应用程序进程创建并初始化成功前,所以它是个临时窗口,对应的WindowType是TYPE_APPLICATION_STARTING。目的是告诉用户,系统已经接受到操作,正在响应,在程序初始化完成后实现目的UI,同时移除这个窗口
我们看到windowBackground这个属性设置的值就是为这个StartingWindow服务的,设置黑屏主题就会显示黑屏,设置白屏主题就会显示白屏,设置一张图片就会显示一张图片
Android系统架构
我们作为上层开发者,理解Android架构便于我们更好的优化我们的应用,下面开始我们的性能优化之路
像Android开源和兼容性技术负责人Dan Morrill在Android开发手册兼容性部分所解释的,“Android并不是传统的Linux风格的一个规范或分发版本,也不是一系列可重用的组件集成,Android是一个用于连接设备的软件块。如果在真个Android系统生态来看的话,我们设计编写的应用只是作为上层的展示,仅仅是整个生态中最表面的一块,用一个通俗的说法就是应用只是一个我们成为的APK的压缩文件,他大体内容页如下图所示


Android是基于Linux的一个操作系统,它可以分为五层,下面是它的层次架构图,可以记一下,因为后面应该会总结到SystemServer这些Application Framework层的东西

App启动流程
下面该图是阿里公众号里找的一个图,大致描述了Android系统的启动流程,图上标识的数字跟下面的介绍没有关系,仅供参考

下面详细介绍具体启动流程
- 打开电源 引导芯片代码加载引导程序Boot Loader到RAM中去执行
- BootLoader把操作系统拉起来
- Linux 内核启动开始系统设置,找到一个init.rc文件启动初始化进程
- init进程初始化和启动属性服务,之后开启Zygote进程
- Zygote开始创建JVM并注册JNI方法,开启SystemServer
- 启动Binder线程沲和SystemServiceManager,并启动各种服务
- AMS启动Launcher
Zygote 进程
Zygote 的中文意思是受精卵,从这个意思里也可以看出 Zygote 进程是用来分裂复制(fork)的,实际上所有的 App 进程都是通过对 Zygote 进程的 Fork 得来的。Zygote 会在其启动后,预加载必要的 Java Classes 和 Resources,并启动 System Server ,并打开 /dev/socket/zygote socket 去监听启动应用程序的请求
Android进程与Linux进程一样. 默认情况下, 每个apk运行在自己的Linux进程中. 另外, 默认一个进程里面只有一个线程—主线程. 这个主线程中有一个Looper实例, 通过调用Looper.loop()从Message队列里面取出Message来做相应的处理,就是我们常说的Handlder原理
Launcher应用启动
我们从系统源码 packages\apps\Launcher2\src\com\android\launcher2\目录下可以找到Launcher.java文件,当我们用手点击一个图标时,就到了这个类执行onClick(View view)方法,会把这个应用的相关信息传入
获取一个intent
-->startActivitySafely(v, intent, tag)
–>startActivity(v, intent, tag);–>startActivity(intent);
startActivity(intent)会开一个APP进程
ActivityThread.java做为入口
这里是Launcher点击应用图标的入口

这里放个startActivitySafely方法的源码

下面是大神老师神级画作

下面给出一个好看一点的图

app白屏和黑屏的一般解决方案
- 在自己的
<style name="AppTheme" parent="Theme.AppCompat.Light">中加入windowsbackground,这样,在App启动的时候就会直接弹出设置的背景

- 设置
windowbackground为透明的<item name="android:windowIsTranslucent">true</item>
前面两种方法都有一个问题,就是在所有Activity启动的时候都会先显示这个默认的图片,如果是透明的话会出现点了图标没反映的情况
- 单独制作一个主题
<style name="AppTheme.Launcher">
<item name="android:windowBackground">@drawable/bg</item>
</style>
<style name="AppTheme.Launcher1">
<item name="android:windowBackground">@drawable/bg</item>
</style>
<style name="AppTheme.Launcher2">
<item name="android:windowBackground">@drawable/bg</item>
</style>然后在Menifest中相应的Activity下设置
<activity android:theme="@style/AppTheme.Launcher"然后在程序中使用
setTheme(R.style.AppTheme);让APP中所有的activity还是使用以前的样式,这样做就只有启动时才使用自己的样式
- 介绍下qq中方式,在style中设置下面的属性
关闭预显示
<item name="android:windowDisablePreview">true</item>
背景设置为空
<item name="android:windowBackground">@null</item>qq比较牛逼的一点是他在Application中不初始化任何的东西,将启动时间优化到极致,下面会讲到具体的方法
- 其实大多数应用不需要做到像qq那样启动速度快到极致,像腾讯系、阿里系的一些应用都是只显示一张图片,然后开始显示一个3秒左右的广告,在显示广告的时间里进行一些初始化
Trace工具分析代码执行时间
黑白屏的优化只是表面上的优化,只是做到让用户觉得启动速度很快,但实际启动速度一点都没变,下面就该介绍真正的启动优化
工欲善其事,必先利器其器,google小姐已经为我们准备好了工具
Debug.startMethodTracing(filePath);
中间为需要统计执行时间的代码
Debug.stopMethodTracing();或者直接使用Android Device file Explorer导出的文件
adb pull /storage/emulated/0/app1.trace把文件拉出来分析
把pull到电脑上的文件拖到AS中就可以分析了查看页面启动时间
sdk版本4.4以前
通过命令
adb shell am start -W com.lqr.wechat/com.lqr.wechat.activity.SplashActivity可以查看页面的启动时间
ThisTime:最后一个启动的Activity的启动耗时;
TotalTime:自己的所有Activity的启动耗时;
WaitTime: ActivityManagerService启动App的Activity时的总时间(包括当前Activity的onPause()和自己Activity的启动)。AM.java在系统源码中的路径
frameworks\base\cmds\am\src\com\android\commands\amAPP启动时间优化
优化方案:
1.开线程
内部没有创建handler 没有操作UI的事件 对异步要求不高
2.懒加载
用到的时候再初始化,如网络,数据库操作
















