App启动优化相关知识点总结

本文主要总结归纳,App启动优化相关知识点


App启动时黑白屏问题分析

启动流程

App启动流程如下:

1
Application > onCreate > MainActivity > onCreate > windows > setContentView> layout

在点击桌面App图标时,系统会给我们App分配一个进程,然后调用我们的application入口,最后调用我们的MainActivity的setContentView方法加载布局文件,最后我们就能看到我们的主界面了。

然后在application 到MainActivity 之间,还会有一个预显示窗口,就是出现的黑白屏。

黑白屏来源

  1. Application有设置theme主题,如果你没有自定义
  2. 你会查找到一个属性
    1
    <item name="windowBackground">@drawable/screen_background_selector_light</item>

优化步骤1【App启动时黑白屏解决方案】

可以将背景设置为透明

1
<item name="android:windowBackground">@null</item>
1
<item name="android:windowIsTranslucent">true</item>

注意:

  • ​这种方法会有一个问题,就是所有的Activity启动都会显示为透明

给背景设置一张图片或者xml资源布局文件

1
<item name="android:windowBackground">@drawable/splash_bg</item>

单独做成一个 AppTheme.Launcher

1
2
3
4
5
<style name="AppTheme.Launcher">
<item name="android:windowFullscreen">true</item>
<!--<item name="android:windowDisablePreview">true</item>-->
<item name="android:windowBackground">@color/colorAccent</item>
</style>

在清单文件中 启动 Activity 加入该 主题

1
2
3
4
5
6
7
8
9
10
<activity
android:name="top.goluck.ui.activity.LoginActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:theme="@style/AppTheme.Launcher"
android:windowSoftInputMode="adjustUnspecified|stateHidden">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

在启动 Activity 页面中加入

1
setTheme(R.style.AppTheme_Launcher);

传说QQ的实现方法是,可以试试

1
2
<item name="android:windowDisablePreview">true</item>
<item name="android:windowBackground">@null</item>

优化步骤2【Application onCreate()方法耗时优化】

sdk异步初始化

需要注意的是:

  • 如果涉及到UI操作的话,就不要放在异步线程中去执行,自行筛选。

  • 优先级,时序

  • 举一反三

    1
    不止是Application中,我们的activity也可以用这种方式来进行优化.

优化步骤3【Activity XMl布局优化】

  • 可采取空布局,loading方式进行缓冲,加载数据,响应之后添加Fragment

  • 减少布局层级

  • 请求网络数据不要过大

  • 首页加载图片不要过大

  • 首页图片提前加载到内存

其他

Activity启动,到Layout全部显示的过程耗时获取

从API19之后,Android在系统Log中增加了Display的Log信息,通过过滤ActivityManager以及Display这两个关键字,可以找到系统中的这个Log:

1
2
adb logcat | grep “ActivityManager”
ActivityManager: Displayed com.example.launcher/.LauncherActivity: +999ms

系统给我们定义了一个类似的『自定义上报时间』——reportFullyDrawn

可用于获取一些数据的懒加载等消耗的时间

  • reportFullyDrawn是由我们自己调用的,一般在数据全部加载完毕后,手动调用,这样就会在Log中增加一条日志:

    1
    2
    3
    adb logcat | grep “ActivityManager”
    ActivityManager: Displayed com.example.launcher/. LauncherActivity: +999ms
    ActivityManager: Fully drawn com.example.launcher/. LauncherActivity: +1s999ms
  • 相关调用示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    public class MainActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    }

    @Override
    public void onLoadFinished(Data data) {
    // 加载数据
    // ……
    // 上报reportFullyDrawn
    reportFullyDrawn();
    }
    }

计算启动时间——ADB

通过ADB命令可以统计应用的启动时间,指令如下所示:

1
2
3
4
5
6
7
8
adb shell am start -W com.xys.preferencetest/.MainActivity
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.xys.preferencetest/.MainActivity}
Status: ok
Activity: com.xys.preferencetest/.MainActivity
ThisTime: 1047
TotalTime: 1047
WaitTime: 1059
Complete

该指令一共给出了三个时间:

  • ThisTime:最后一个启动的Activity的启动耗时
  • TotalTime:自己的所有Activity的启动耗时
  • WaitTime: ActivityManagerService启动App的Activity时的总时间(包括当前Activity的onPause()和自己Activity的启动)

这三个时间不是很好理解,我们可以把整个过程分解

1
2
1.上一个Activity的onPause()——2.系统调用AMS耗时——3.第一个Activity(也许是闪屏页)启动耗时——4.第一个Activity的onPause()耗时——5.第二个Activity启动耗时
那么,ThisTime表示5(最后一个Activity的启动耗时)。TotalTime表示3.4.5总共的耗时(如果启动时只有一个Activity,那么ThisTime与TotalTime应该是一样的)。WaitTime则表示所有的操作耗时,即1.2.3.4.5所有的耗时。

工具分析代码执行

  • Appcation 中查看耗时通过

    1
    2
    3
    4
    5
    //开始计时
    Debug.startMethodTracing(filePath);
    中间为需要统计执行时间的代码
    //停止计时
    Debug.stopMethodTracing();
  • 使用trace 文件命令查看每个方法的耗时[adb pull将其导出到本地]

    1
    adb pull /storage/emulated/0/appcation_launcher_time.trace
  • 打开DDMS分析trace文件

1 在下方的方法区点击”Real Time/Call”, 按照方法每次调用耗时降序排.
2 耗时超过500ms都是值得注意的.
3 看左边的方法名, 可以看到耗时大户就是我们用的几大平台的初始化方法, 特别是Bugly, 还加载native的lib, 用ZipFile操作等.
4 点击每个方法, 可以看到其父方法(调用它的)和它的所有子方法(它调用的).
5 点击方法时, 上方的该方法执行时间轴会闪动, 可以看该方法的执行线程及相对时长.