50. 安卓启动优化

启动优化包含app的启动和单页面的启动,今天只说app的启动,二者优化的逻辑是相同的。

app启动的三种状态

冷启动

此时需要创建进程,表示应用的首次启动

热启动

应用的所有 Activity 仍驻留在内存中,不必重复执行对象初始化、布局加载和绘制。

温启动

如:用户在退出应用后又重新启动应用。进程可能未被销毁,继续运行,但应用需要执行 onCreate() 从头开始重新创建 Activity

Google 提出的Android Vitals计划建议:冷启动控制在 5 秒内, 温启控制在2秒内, 热启动控制在1.5 秒内。

app启动分析

如何查看app启动时间

Android 4.4(API 级别 19)及更高版本中,logcat 包含一个输出行,代表从启动进程到在屏幕上完成对应 Activity 的绘制所用的时间,用“ActivityManager: Displayed”过滤即可


截屏2021-06-20 上午9.28.31.png

还可以通过adb命令查看

adb shell am start -S -W com.rzm.easy.demo/.MainActivity -c android.intent.category.LAUNCHER -a android.intent.action.MAIN 

启动完成后,将输出:

ThisTime: 415 表示一连串启动Activity的最后一个Activity的启动耗时
TotalTime: 415  真正启动的耗时,表示新应用启动的耗时,包括新进程的启动和Activity的启动,但不包括前一个应用Activity pause 的耗时
WaitTime: 437 总的耗时,包括前一个应用Activity pause的时间和新应用启动的时间;
定位代码中的耗时情况-CPU Profile
截屏2021-06-20 上午9.35.08.png
截屏2021-06-20 上午9.35.29.png
截屏2021-06-20 上午9.36.35.png
Sample Java Methods

对 Java 方法采样:在应用的 Java 代码执行期间,频繁捕获应用的调用堆栈。分析器会比较捕获的数据集, 以推导与应用的 Java 代码执行有关的时间和资源使用信息。如果应用在捕获调用堆栈后进入一个方法并在下 次捕获前退出该方法,分析器将不会记录该方法调用

Trace Java Methods

跟踪 Java 方法:在运行时检测应用,以在每个方法调用开始和结束时记录一个时间戳。系统会收集并比较这些时间戳,以生成方法跟踪数据,包括时间信息和 CPU 使用率。

Sample C/C++ Functions,对 C/C++

函数采样:捕获应用的原生线程的采样跟踪数据。(Android 8.0(API 级别 26)或更高版本)

Trace System Calls

跟踪系统调用:捕获非常翔实的细节,以便检查应用与系统资源的交互情况。可以检查线程状态的确切 时间和持续时间、直观地查看所有内核的 CPU 瓶颈在何处,并添加要分析的自定义跟踪事件。( Android 7.0(API 级别 24)或更高版本)

profile中会显示一个这样的界面


截屏2021-06-20 上午9.42.28.png
Call Chart

以图形来呈现方法跟踪数据或函数跟踪数据,其中调用的时间段和时间在横轴上表示(横轴越长,消耗时间越长),而其被调用方则在纵轴上显 示。对系统 API 的调用显示为橙色,对应用自有方法的调用显示为绿色,对第三方 API(包括 Java 语言 API)的调 用显示为蓝色。 (实际颜色显示有Bug)


截屏2021-06-20 上午9.45.08.png

如上图,自定义Application的 onCreate 调用了 Thread.sleep 耗时为:3s。
Call Chart 已经比原数据可读性高很多,但它仍然不方便发现那些运行时间很长的代码,这时我们便需要使用
Flame Chart。

Flame Chart

提供一个倒置的调用图表,用来汇总完全相同的调用堆栈。也就是说,将具有相同调用方顺序的完全相同的方法或 函数收集起来,并在火焰图中将它们表示为一个较长的横条 。
横轴显示的是百分比数值。由于忽略了时间线信息,Flame Chart 可以展示每次调用消耗时间占用整个记录时长的 百分比。 同时纵轴也被对调了,在顶部展示的是被调用者,底部展示的是调用者。此时的图表看起来越往上越窄, 就好像火焰一样,因此得名: 火焰图。


截屏2021-06-20 上午9.46.25.png

截屏2021-06-20 上午9.46.34.png

耗时最长的为:Thread.sleep

Top Down Tree

如果我们需要更精确的时间信息,就需要使用 Top Down Tree。 Top Down Tree显示一个调用列表,在该列表中 展开方法或函数节点会显示它调用了的方法节点。

截屏2021-06-20 上午9.47.43.png

对于每个节点,三个时间信息:
Self Time —— 运行自己的代码所消耗的时间; Children Time —— 调用其他方法的时间; Total Time —— 前面两者时间之和。
此视图能够非常方便看到耗时最长的方法调用栈。

Bottom Up Tree

方便地找到某个方法的调用栈。在该列表中展开方法或函数节点会显示哪个方法调用了自己。


截屏2021-06-20 上午9.48.41.png

通过工具可以定位到耗时代码,然后查看是否可以进行优化。对于APP启动来说,启动耗时包括Android系统启动 APP进程加上APP启动界面的耗时时长,我们可做的优化是APP启动界面的耗时,也就是说从Application的构建到 主界的 onWindowFocusChanged 的这一段时间。

Debug API

除了直接使用 Profile 启动之外,我们还可以借助Debug API生成trace文件。

public class MyApplication extends Application { 

    public MyApplication() {
        Debug.startMethodTracing("ram"); 
    }
}

public class MainActivity extends AppCompatActivity { 
    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
        Debug.stopMethodTracing(); 
} }

运行App,则会在sdcard中生成一个rzm.trace文件(需要sdcard读写权限)。将手机中的trace文件保存至电 脑,随后拖入Android Studio即可。

StrictMode严苛模式

StrictMode是一个开发人员工具,它可以检测出我们可能无意中做的事情,并将它们提请我们注意,以便我们能够
修复它们。
StrictMode最常用于捕获应用程序主线程上的意外磁盘或网络访问。帮助我们让磁盘和网络操作远离主线程,可以 使应用程序更加平滑、响应更快。

public class MyApplication extends Application {
    @Override
    public void onCreate() {
        if (BuildConfig.DEBUG) {
            //线程检测策略
            StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
                    .detectDiskReads() //读、写操作
                    .detectDiskWrites()
                    .detectNetwork() // or .detectAll() for all detectable problems .penaltyLog()
                    .build());
            StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectLeakedSqlLiteObjects() //Sqlite对象泄露
                    .detectLeakedClosableObjects() //未关闭的Closable对象泄露 .penaltyLog() //违规打印日志
                    .penaltyDeath() //违规崩溃
                    .build());
        }
    }
}

你可能感兴趣的:(50. 安卓启动优化)