启动优化包含app的启动和单页面的启动,今天只说app的启动,二者优化的逻辑是相同的。
app启动的三种状态
冷启动
此时需要创建进程,表示应用的首次启动
热启动
应用的所有 Activity 仍驻留在内存中,不必重复执行对象初始化、布局加载和绘制。
温启动
如:用户在退出应用后又重新启动应用。进程可能未被销毁,继续运行,但应用需要执行 onCreate() 从头开始重新创建 Activity
Google 提出的Android Vitals计划建议:冷启动控制在 5 秒内, 温启控制在2秒内, 热启动控制在1.5 秒内。
app启动分析
如何查看app启动时间
Android 4.4(API 级别 19)及更高版本中,logcat 包含一个输出行,代表从启动进程到在屏幕上完成对应 Activity 的绘制所用的时间,用“ActivityManager: Displayed”过滤即可
还可以通过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
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中会显示一个这样的界面
Call Chart
以图形来呈现方法跟踪数据或函数跟踪数据,其中调用的时间段和时间在横轴上表示(横轴越长,消耗时间越长),而其被调用方则在纵轴上显 示。对系统 API 的调用显示为橙色,对应用自有方法的调用显示为绿色,对第三方 API(包括 Java 语言 API)的调 用显示为蓝色。 (实际颜色显示有Bug)
如上图,自定义Application的 onCreate 调用了 Thread.sleep 耗时为:3s。
Call Chart 已经比原数据可读性高很多,但它仍然不方便发现那些运行时间很长的代码,这时我们便需要使用
Flame Chart。
Flame Chart
提供一个倒置的调用图表,用来汇总完全相同的调用堆栈。也就是说,将具有相同调用方顺序的完全相同的方法或 函数收集起来,并在火焰图中将它们表示为一个较长的横条 。
横轴显示的是百分比数值。由于忽略了时间线信息,Flame Chart 可以展示每次调用消耗时间占用整个记录时长的 百分比。 同时纵轴也被对调了,在顶部展示的是被调用者,底部展示的是调用者。此时的图表看起来越往上越窄, 就好像火焰一样,因此得名: 火焰图。
耗时最长的为:Thread.sleep
Top Down Tree
如果我们需要更精确的时间信息,就需要使用 Top Down Tree。 Top Down Tree显示一个调用列表,在该列表中 展开方法或函数节点会显示它调用了的方法节点。
对于每个节点,三个时间信息:
Self Time —— 运行自己的代码所消耗的时间; Children Time —— 调用其他方法的时间; Total Time —— 前面两者时间之和。
此视图能够非常方便看到耗时最长的方法调用栈。
Bottom Up Tree
方便地找到某个方法的调用栈。在该列表中展开方法或函数节点会显示哪个方法调用了自己。
通过工具可以定位到耗时代码,然后查看是否可以进行优化。对于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());
}
}
}