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

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

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

以优酷app冷启动为例:

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

整个过程包括:

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

注:
我们可以看到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之后,会开始准备创建进程。

进程创建流程

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需要重点关注的部分:

这部分表示CPU调度状态:

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

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

2)Activity启动阶段

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

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/<packageName>/oat/arm / 路径下会生成 .odex .vdex文件。之后会在空闲状态下执行dex2oat。这部分之后开章节来详细说。

继续:


scheduleLaunchActivity流程

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

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

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

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,179评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,229评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,032评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,533评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,531评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,539评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,916评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,813评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,568评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,654评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,354评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,937评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,918评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,152评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,852评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,378评论 2 342

推荐阅读更多精彩内容