从systrace看app冷启动过程(一)-应用程序启动

原创文章,转载注明出处,多谢合作。

本文从systrace的角度通过关键标签,来简单看下app冷启动牵扯到的图形渲染的整个流程。代码参考Android 9.0。

以优酷app冷启动为例:

从systrace看app冷启动过程(一)-应用程序启动_第1张图片

先看第一帧显示过程,如下图所示:

从systrace看app冷启动过程(一)-应用程序启动_第2张图片

整个过程包括:

  • 应用程序启动。
  • 首帧的绘制与渲染。
  • 首帧的合成与送显。

注:
我们可以看到systrace中,每一帧主要分红、黄、绿三种颜色:

  • 红色: 代表从performTraversals到renderthread绘制完成的时间超过2 * vsync,定义成terrible frame。
  • 黄色: 代表时间超过1 * vsync 不到 2* vsync,在有renderthread的情况下,超过一个vsync是不一定会导致掉帧的,所以只是黄色,定义成bad frame。
  • 绿色: 代表时间在1 * vsync以内, 定义成ok frame。

先看第一个过程:应用程序启动。

因为展开的图太长了,这个过程又主要拆分为如下两个阶段:

1)进程创建阶段

Zygote初始化过程中,最后会调用runSelectLoop(),随时待命,当接收到请求创建新进程请求时立即唤醒并执行相应工作。

那么在App冷启过程中,先确定调用的Activity以及对信息与权限的验证,并调整其task和stack之后,会开始准备创建进程。

从systrace看app冷启动过程(一)-应用程序启动_第3张图片
进程创建流程

runSelectLoop之前流程是将启动参数封装成ZygoteState。Zygote 待命的loop通过socket获取到请求信息,runOnce就开始读取参数列表, 并执行forkAndSpecialize来创建进程,之后就是创建Binder线程池以及通过反射调用ActivityThread的main函数等等。PostFork过程就在forkAndSpecialize与handleChildProc之间,描述进程fork的过程。而ZygoteInit部分则是描述进程启动之后一些准备工作。

以PostFork为例:

frameworks/base/core/java/com/android/internal/os/Zygote.java

133    public static int forkAndSpecialize(int uid, int gid, int[] gids, int runtimeFlags,
134          int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
135          int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir) {
            ...
143        if (pid == 0) {
144            Trace.setTracingEnabled(true, runtimeFlags);
146            // Note that this event ends at the end of handleChildProc,
147            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork");
148        }
149        VM_HOOKS.postForkCommon();
150        return pid;
151    }

从这里看到了traceBegin的标签,而且通过注释我们知道对应的End标签在handleChildProc.

 frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java

844    private Runnable handleChildProc(Arguments parsedArgs, FileDescriptor[] descriptors,
845            FileDescriptor pipeFd, boolean isZygote) {
             ...
871        // End of the postFork event.
872        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
             ...
}

另外,CPU调度也提一下,也是systrace需要重点关注的部分:

从systrace看app冷启动过程(一)-应用程序启动_第4张图片

这部分表示CPU调度状态:

  • 灰色:Sleeping
  • 蓝色:Runnable (它可以运行,但是需要等待调度程序唤醒)
  • 绿色:Running
  • 橙色:Uninterruptible sleep 由于 I/O 负载而不可中断休眠

如果当前部分存在耗时情况,先对比看看相同部分的Running 时间是否差不多,如果差很多可能是方法本身耗时了,如果差不多,那么可能是调度上耗时了,比如Uninterruptible sleep状态太多,如果是这种情况的话则需要看紧随其后的Runnable,跟一下wake up它的对应进程or线程是谁,一步步分析下去,找到root issue。

2)Activity启动阶段

进程创建后,继续Activity的启动阶段,包括对要启动的Activity信息、权限等的验证,数据的封装、任务栈的调整等等一系列操作,最后进入应用的ActivityThread。

从systrace看app冷启动过程(一)-应用程序启动_第5张图片

ActivityThreadMain部分:

frameworks/base/core/java/android/app/ActivityThread.java

6764    public static void main(String[] args) {
6765        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
6782        ...
6783        Looper.prepareMainLooper();
               ...
6796        ActivityThread thread = new ActivityThread();
6797        thread.attach(false, startSeq);
6808        // End of event ActivityThreadMain.
6809        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
6810        Looper.loop();
6811
6812        throw new RuntimeException("Main thread loop unexpectedly exited");
6813    }

ActivityThreadMain这就是反映了ActivityThread的main方法执行过程,这个过程很简单就是创建ActivityThread并让它的Looper消息泵循环跑起来。所以一般这个过程不会有什么耗时问题。

其中有个不起眼的方法:attach 看看它的调用流程:

ActivityThread.attach() ->AMS.attachApplicationLocked()->ActivityThread.bindApplication - >sendMessage(H.BIND_APPLICATION, data);

那么就到了接下的bindApplication过程了。

bindApplication部分:

class H extends Handler {
1665        public void handleMessage(Message msg) {
1666            if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
1667            // MIUI ADD
1668            long startTime = SystemClock.uptimeMillis();
1669            switch (msg.what) {
1670                case BIND_APPLICATION:
1671                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
1672                    AppBindData data = (AppBindData)msg.obj;
1673                    handleBindApplication(data);
1674                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
1675                    break;
                  ...
 }
}

这个标签对应的就是 handleBindApplication过程,应用进程的创建是从Zygote fork的进程,copy了一份Zygote进程的内存拷贝,在当前过程中,就是正式往fork出来的进程中填充属于当前应用私有的资源与数据。

注:
Zygote 刚fork出应用进程时,两者是共享物理内存的,且对data区内容只有可读权限,一方开始了写操作了,即会开辟一块新的内存,保存新的修改内容。这技术就是fork copy-on-write.

bindApplication这个过程包括:

  • 加载应用资源、load Class到内存。
  • 创建上下文。
  • 初始化Instrumentation,并通过它调用application的onCreate。Instrumentation是ActivityThread与组件之间的传话官,ActivityThread通过H分发出来的对应的操作都是通过Instrumentation来调用组件实现。

注:
google原生逻辑是安装应用的时候解压APK包,对dex做初步优化,你会发现在安装完应用后 data/app//oat/arm / 路径下会生成 .odex .vdex文件。之后会在空闲状态下执行dex2oat。这部分之后开章节来详细说。

继续:


从systrace看app冷启动过程(一)-应用程序启动_第6张图片
scheduleLaunchActivity流程

这部分就是走scheduleLaunchActivity流程了,内容包括:

创建Activity,并走对应的生命周期。这个过程主要是应用布局的加载:

setContentView 创建DecorView,并把xml的View树解析出来,加到DecorView上的contentParent部分。之后Activity 调用makeVisible 通过WindowManagerGlobal执行addView操作,开启下一个阶段的绘制工作。

至此,第一个Vsync信号响应阶段就简单介绍完了,中间牵扯到的其他标签有兴趣的可以一一追源码看下,这里就不赘述了。下一篇分析首帧绘制与渲染过程。

你可能感兴趣的:(从systrace看app冷启动过程(一)-应用程序启动)