文章目录

  • 概述
  • 历史
  • 黑白屏出现原因
  • 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

android 黑白屏显示 安卓黑白屏_黑白屏

当从launcher启动一个APP时,需要fork一个进程并进行一些初始化,而fork进程和初始化是需要时间的,这时候就有了StartingWindow(也称之为PreviewWindow)的出现,

StartingWindow一般出现在应用程序进程创建并初始化成功前,所以它是个临时窗口,对应的WindowTypeTYPE_APPLICATION_STARTING。目的是告诉用户,系统已经接受到操作,正在响应,在程序初始化完成后实现目的UI,同时移除这个窗口

我们看到windowBackground这个属性设置的值就是为这个StartingWindow服务的,设置黑屏主题就会显示黑屏,设置白屏主题就会显示白屏,设置一张图片就会显示一张图片

Android系统架构

我们作为上层开发者,理解Android架构便于我们更好的优化我们的应用,下面开始我们的性能优化之路

像Android开源和兼容性技术负责人Dan Morrill在Android开发手册兼容性部分所解释的,“Android并不是传统的Linux风格的一个规范或分发版本,也不是一系列可重用的组件集成,Android是一个用于连接设备的软件块。如果在真个Android系统生态来看的话,我们设计编写的应用只是作为上层的展示,仅仅是整个生态中最表面的一块,用一个通俗的说法就是应用只是一个我们成为的APK的压缩文件,他大体内容页如下图所示

android 黑白屏显示 安卓黑白屏_启动速度_02


android 黑白屏显示 安卓黑白屏_黑白屏_03

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

android 黑白屏显示 安卓黑白屏_性能优化_04

App启动流程

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

android 黑白屏显示 安卓黑白屏_启动速度_05

下面详细介绍具体启动流程

  1. 打开电源 引导芯片代码加载引导程序Boot Loader到RAM中去执行
  2. BootLoader把操作系统拉起来
  3. Linux 内核启动开始系统设置,找到一个init.rc文件启动初始化进程
  4. init进程初始化和启动属性服务,之后开启Zygote进程
  5. Zygote开始创建JVM并注册JNI方法,开启SystemServer
  6. 启动Binder线程沲和SystemServiceManager,并启动各种服务
  7. 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点击应用图标的入口

android 黑白屏显示 安卓黑白屏_Android_06


这里放个startActivitySafely方法的源码

android 黑白屏显示 安卓黑白屏_性能优化_07

下面是大神老师神级画作

android 黑白屏显示 安卓黑白屏_Android_08


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

android 黑白屏显示 安卓黑白屏_性能优化_09

app白屏和黑屏的一般解决方案

  1. 在自己的<style name="AppTheme" parent="Theme.AppCompat.Light">中加入windowsbackground,这样,在App启动的时候就会直接弹出设置的背景

android 黑白屏显示 安卓黑白屏_Android_10

  1. 设置windowbackground为透明的 <item name="android:windowIsTranslucent">true</item>

前面两种方法都有一个问题,就是在所有Activity启动的时候都会先显示这个默认的图片,如果是透明的话会出现点了图标没反映的情况

  1. 单独制作一个主题
<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还是使用以前的样式,这样做就只有启动时才使用自己的样式

  1. 介绍下qq中方式,在style中设置下面的属性
关闭预显示
<item name="android:windowDisablePreview">true</item> 
背景设置为空
<item name="android:windowBackground">@null</item>

qq比较牛逼的一点是他在Application中不初始化任何的东西,将启动时间优化到极致,下面会讲到具体的方法

  1. 其实大多数应用不需要做到像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\am

APP启动时间优化

优化方案:

1.开线程

内部没有创建handler 没有操作UI的事件 对异步要求不高

2.懒加载

用到的时候再初始化,如网络,数据库操作