UI 篇1. 从应用进程的启动到 ActivityThread

前言:

从打开一个 app 到手机屏幕上看到的界面基本上要经历以下的三个步骤:
步骤1:android 程序启动 Activity 加载并完成生命周期
步骤2:setContentView()
步骤3:图形绘制

(1)Android 应用程序如何启动的,生命周期是如何调用的?
(2)在 onCreate() 中 setContentView() 是如何加载 ui 文件的?
(3)ui 是如何绘制的?
UI 的绘制流程基本上就是围绕以上三个问题进行展开的,也是接下来几个章节的核心内容。

1. 应用程序的启动

1.1 从应用进程的启动到 ActivityThread

其实就是进程的创建,这部分内容在 framework 笔记13 中有讲到,这里就简述一下过程:
(1)在 Launcher 中的图表点击事件启动 app:startAppShortcutOrInfoActivity(v);在 framework 笔记12 中有提到;
(2)最终调用的还是 Activity 中的 startActivity();
(3)startActivity() 调用 startActivityForResult(),在这个方法中调用了 mInstrumentation.execStartActivity() 进行跨进程通信;也就是 int result = ActivityManagerNative.getDefault().startActivity(...),最终交给 AMS 进行处理;
(4)经过一系列的调用最终 ActivityStackSupervisor 的 startSpecificActivityLocked() 方法中:

    void startSpecificActivityLocked(ActivityRecord r,
            boolean andResume, boolean checkConfig) {
        // Is this activity's application already running?  这里就是判断进程是否已经启动
        ProcessRecord app = mService.getProcessRecordLocked(r.processName,
                r.info.applicationInfo.uid, true);  // 根据uid和进程名进行查询

        r.task.stack.setLaunchTime(r);

        if (app != null && app.thread != null) {  // 第一次打开某个进程时,肯定不会进入这个分支
          // ... 以后的笔记中再分析
            try {
                if ((r.info.flags&ActivityInfo.FLAG_MULTIPROCESS) == 0
                        || !"android".equals(r.info.packageName)) {
                    app.addPackage(r.info.packageName, r.info.applicationInfo.versionCode,
                            mService.mProcessStats);
                }
                realStartActivityLocked(r, app, andResume, checkConfig);  // 启动activity
                return;
            } catch (RemoteException e) {
                // ... Log信息
            }
        }       
        // 进程的创建、application的创建和绑定
        mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
                "activity", r.intent.getComponent(), false, false, true);
    }

(5)AMS 与 Zygote 的通信过程:r.processName, r.info.applicationInfo, true, 0,
"activity", r.intent.getComponent(), false, false, true

// 第一步:AMS 中 startProcessLocked()
    final ProcessRecord startProcessLocked(String processName, ApplicationInfo info,
            boolean knownToBeDead, int intentFlags, String hostingType, ComponentName hostingName,
            boolean allowWhileBooting, boolean isolated, int isolatedUid, boolean keepIfLarge,
            String abiOverride, String entryPoint, String[] entryPointArgs, Runnable crashHandler) {
        long startTime = SystemClock.elapsedRealtime();
        ProcessRecord app;
        // ...省略一些代码
        if (app == null) {  // 本节学习进程的创建,所以从此处开始
            checkTime(startTime, "startProcess: creating new process record");
            // 创建新的Process Record对象
            app = newProcessRecordLocked(info, processName, isolated, isolatedUid);
            app.crashHandler = crashHandler;
            if (app == null) {
                return null;
            }
            mProcessNames.put(processName, app.uid, app);
            if (isolated) {
                mIsolatedProcesses.put(app.uid, app);
            }
            checkTime(startTime, "startProcess: done creating new process record");
        } else {
            // 如果这是进程中新package,则添加到列表
            app.addPackage(info.packageName, info.versionCode, mProcessStats);
            checkTime(startTime, "startProcess: added package to existing proc");
        }

        // 当系统未准备完毕,则将当前进程加入到mProcessesOnHold
        if (!mProcessesReady
                && !isAllowedWhileBooting(info)
                && !allowWhileBooting) {
            if (!mProcessesOnHold.contains(app)) {
                mProcessesOnHold.add(app);
            }
            if (DEBUG_PROCESSES) Slog.v(TAG, "System not ready, putting on hold: " + app);
            checkTime(startTime, "startProcess: returning with proc on hold");
            return app;
        }

        checkTime(startTime, "startProcess: stepping in to startProcess");
        // 启动进程,见第二步
        startProcessLocked( 
               app, hostingType, hostingNameStr, abiOverride, entryPoint,entryPointArgs);
        checkTime(startTime, "startProcess: done starting proc!");
        return (app.pid != 0) ? app : null;
    }


// 第二步:AMS 中 startProcessLocked()
private final void startProcessLocked(ProcessRecord app, String hostingType,
            String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {
        long startTime = SystemClock.elapsedRealtime();
        // ...
        try {
            // ...
            boolean isActivityProcess = (entryPoint == null);
            if (entryPoint == null) entryPoint = "android.app.ActivityThread";
            // 见第三步 
            Process.ProcessStartResult startResult = Process.start(entryPoint,
                    app.processName, uid, uid, gids, debugFlags, mountExternal,
                    app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
                    app.info.dataDir, entryPointArgs);
           // ...
            synchronized (mPidsSelfLocked) {
                this.mPidsSelfLocked.put(startResult.pid, app); // 保存pid
                // ...
            }
        } catch (RuntimeException e) {
            // ...
        }
    }


// 第三步:
    // Process 中的 start()方法:
    public static final ProcessStartResult start(final String processClass,
                                  final String niceName,
                                  int uid, int gid, int[] gids,
                                  int debugFlags, int mountExternal,
                                  int targetSdkVersion,
                                  String seInfo,
                                  String abi,
                                  String instructionSet,
                                  String appDataDir,
                                  String[] zygoteArgs) {
        try {
            return startViaZygote(processClass, niceName, uid, gid, gids,
                    debugFlags, mountExternal, targetSdkVersion, seInfo,
                    abi, instructionSet, appDataDir, zygoteArgs);
        } catch (ZygoteStartFailedEx ex) {
            Log.e(LOG_TAG, "Starting VM process through Zygote failed");
            throw new RuntimeException("Starting VM process through Zygote failed", ex);
        }
    }

    // startViaZygote() 方法:
    private static ProcessStartResult startViaZygote(final String processClass,
                                  final String niceName,
                                  final int uid, final int gid,
                                  final int[] gids,
                                  int debugFlags, int mountExternal,
                                  int targetSdkVersion,
                                  String seInfo,
                                  String abi,
                                  String instructionSet,
                                  String appDataDir,
                                  String[] extraArgs)
                                  throws ZygoteStartFailedEx {
        synchronized(Process.class) {
            ArrayList<String> argsForZygote = new ArrayList<String>();

            // --runtime-init, --setuid=, --setgid=,
            // and --setgroups= must go first
            argsForZygote.add("--runtime-init");
            argsForZygote.add("--setuid=" + uid);
            argsForZygote.add("--setgid=" + gid);
            // ... 

            // --setgroups is a comma-separated list
            if (gids != null && gids.length > 0) {
                StringBuilder sb = new StringBuilder();
                sb.append("--setgroups=");

                int sz = gids.length;
                for (int i = 0; i < sz; i++) {
                    if (i != 0) {
                        sb.append(',');
                    }
                    sb.append(gids[i]);
                }

                argsForZygote.add(sb.toString());
            }

            if (niceName != null) {
                argsForZygote.add("--nice-name=" + niceName);  // nicieName是class的名字
            }

            if (seInfo != null) {
                argsForZygote.add("--seinfo=" + seInfo);
            }

            if (instructionSet != null) {
                argsForZygote.add("--instruction-set=" + instructionSet);
            }

            if (appDataDir != null) {
                argsForZygote.add("--app-data-dir=" + appDataDir);
            }

            argsForZygote.add(processClass);  // 是刚才传入的 "android.app.ActivityThread"

            if (extraArgs != null) {
                for (String arg : extraArgs) {
                    argsForZygote.add(arg);
                }
            }
            // 调用的是zygoteSendArgsAndGetResult() 
            return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
        }
    }

接下来就是 Zygote fork 出一个进程的过程,此部分内容在 framework 笔记13 中有介绍,不明白的可以去看看。以下是大致的步骤:
(a)Zygote 在 main() 方法中调用了 runSelectLoop(),而 runSelectLoop() 中有一个死循环,当有客户端连接时便会执行 ZygoteConnection.runOnce() 方法,再经过层层调用后 fork 出新的应用进程;
(b)fork 完成后子进程进行处理handleChildProc():关键代码在 ZygoteInit.invokeStaticMain(cloader, className, mainArgs) 方法中;

static void invokeStaticMain(ClassLoader loader,
            String className, String[] argv)
            throws ZygoteInit.MethodAndArgsCaller {
        Class<?> cl;

        try { // className 是传入的 "android.app.ActivityThread"
            cl = loader.loadClass(className); // 获取class的名字
        } catch (ClassNotFoundException ex) {
            // ...
        }

        Method m;
        try {
            // 这里的方法 m 是 android.app.ActivityThread 的 main() 方法
            m = cl.getMethod("main", new Class[] { String[].class });
        } catch (NoSuchMethodException ex) {
            throw new RuntimeException("Missing static main on " + className, ex);
        } catch (SecurityException ex) {
            throw new RuntimeException( "Problem getting static main on " + className, ex);
        }
        // 关键代码
        throw new ZygoteInit.MethodAndArgsCaller(m, argv);
    }


    /**
     * Helper exception class which holds a method and arguments and
     * can call them. This is used as part of a trampoline to get rid of
     * the initial process setup stack frames.
     */
// ZygoteInit内部类:
    public static class MethodAndArgsCaller extends Exception
            implements Runnable {
        /** method to call */
        private final Method mMethod;

        /** argument array */
        private final String[] mArgs;

        public MethodAndArgsCaller(Method method, String[] args) {
            mMethod = method;
            mArgs = args;
        }

        public void run() {
           // 这里的方法 mMethod 是 android.app.ActivityThread 的 main() 方法
            try {
                // 进程真正开始启动 看 ActivityThread的main方法
                mMethod.invoke(null, new Object[] { mArgs });  
            } catch (IllegalAccessException ex) {
                throw new RuntimeException(ex);
            } catch (InvocationTargetException ex) {
                Throwable cause = ex.getCause();
                if (cause instanceof RuntimeException) {
                    throw (RuntimeException) cause;
                } else if (cause instanceof Error) {
                    throw (Error) cause;
                }
                throw new RuntimeException(ex);
            }
        }
    }

讲了这么多,从应用的启动,一直到调用 ActivityThread 的 main() 方法,之所以这么做,是想把整个流程走通,有助于全面理解进程的启动,自己亲身经历过某些一讲进程启动就直接 ActivityThread 的 main() 方法走起,这种生硬的灌输或者说死记硬背并不能让我很好的理解,所以在这里分享给大家。

1.2 入口 main() 函数

进程在 Zygote 进行 fork 、JNI 环境绑定等完成后,使用反射的方式执行了 ActivityThread 的 main() 方法,在这个 main 方法中,有三个需要注意的地方,如下代码中所示:

    public static void main(String[] args) {
        // ... 省略部分代码
        //(1)准备MainLooper
        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        thread.attach(false); //(2)这里是重点

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
        // ... 
        //(3)消息机制:有兴趣可以看看之前的文章中有 handler 消息机制的分析;
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

ActivityThread 的 attach() 方法:

    private void attach(boolean system) {
        sCurrentActivityThread = this;
        mSystemThread = system;
        if (!system) {  // 非系统app
            ViewRootImpl.addFirstDrawHandler(new Runnable() {
                @Override
                public void run() {
                    ensureJitEnabled();
                }
            });
            android.ddm.DdmHandleAppName.setAppName("<pre-initialized>",
                                                    UserHandle.myUserId());
            RuntimeInit.setApplicationObject(mAppThread.asBinder());
            final IActivityManager mgr = ActivityManagerNative.getDefault();  // 获取到AMS
            try {
                 // binder 通讯,和之前的一样;mAppThread -> ApplicationThread,底层是BBinder
                mgr.attachApplication(mAppThread); 
            } catch (RemoteException ex) {
                // Ignore
            }
            // Watch for getting close to heap limit.
            BinderInternal.addGcWatcher(new Runnable() {
                @Override public void run() {
                    if (!mSomeActivitiesChanged) {
                        return;
                    }
                    Runtime runtime = Runtime.getRuntime();
                    long dalvikMax = runtime.maxMemory();
                    long dalvikUsed = runtime.totalMemory() - runtime.freeMemory();
                    if (dalvikUsed > ((3*dalvikMax)/4)) {
                        mSomeActivitiesChanged = false;
                        try {
                            mgr.releaseSomeActivities(mAppThread);
                        } catch (RemoteException e) {
                        }
                    }
                }
            });
        } else {
            // ...
        }
    }

代码简要解析:下面这两行代码是关键
(1)IActivityManager mgr = ActivityManagerNative.getDefault():
通过 Binder 驱动获取 AMS,详细的过程在 framework 笔记10 中有,此处不再赘述;
(2)mgr.attachApplication(mAppThread):
framework 笔记13 也有讲到 AMS.attachApplication() 这个方法;
mgr.attachApplication(mAppThread) ->
ActivityManagerService.attachApplicationLocked() ->
thread.bindApplication() -> ----- 这里的 thread 是 ApplicationThread
使用 Handler 发送消息,执行 handleBindApplication(data) ->
创建 Application:Application app = data.info.makeApplication(data.restrictedBackupMode, null) ->
执行 Application 的 onCreate() 方法:mInstrumentation.callApplicationOnCreate(app)
至此,一个新的进程就创建完成了。

2. Activity 的启动

当前面的进程完成创建后,继续执行 ActivityManagerService.attachApplicationLocked() 的方法:

    private final boolean attachApplicationLocked(IApplicationThread thread,
            int pid) {

        // ...
        try {
            // ...
            thread.bindApplication(processName, appInfo, providers, app.instrumentationClass,
                    profilerInfo, app.instrumentationArguments, app.instrumentationWatcher,
                    app.instrumentationUiAutomationConnection, testMode, enableOpenGlTrace,
                    isRestrictedBackupMode || !normalMode, app.persistent,
                    new Configuration(mConfiguration), app.compat, getCommonServicesLocked(),
                    mCoreSettingsObserver.getCoreSettingsLocked());
            updateLruProcessLocked(app, false, null);
            app.lastRequestedGc = app.lastLowMemory = SystemClock.uptimeMillis();
        } catch (Exception e) {
            // ...
            return false;
        }

        if (normalMode) {
            try {
                // 下面的这行代码就要是启动 Manifest.xml 中配置的Activity 了
                if (mStackSupervisor.attachApplicationLocked(app)) {  
                    didSomething = true;
                }
            } catch (Exception e) {
                Slog.wtf(TAG, "Exception thrown launching activities in " + app, e);
                badApp = true;
            }
        }

        // Find any services that should be running in this process...
        if (!badApp) {
            try {
                didSomething |= mServices.attachApplicationLocked(app, processName);
            } catch (Exception e) {
                Slog.wtf(TAG, "Exception thrown starting services in " + app, e);
                badApp = true;
            }
        }
        // ...
}
    boolean attachApplicationLocked(ProcessRecord app) throws RemoteException {
        final String processName = app.processName;
        boolean didSomething = false;
        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
            ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
            for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
                final ActivityStack stack = stacks.get(stackNdx);
                if (!isFrontStack(stack)) {
                    continue;
                }
                ActivityRecord hr = stack.topRunningActivityLocked(null);
                if (hr != null) {
                    if (hr.app == null && app.uid == hr.info.applicationInfo.uid
                            && processName.equals(hr.processName)) {
                        try {
                            if (realStartActivityLocked(hr, app, true, true)) { // 看到这里了没
                                didSomething = true;
                            }
                        } catch (RemoteException e) {
                            throw e;
                        }
                    }
                }
            }
        }
        if (!didSomething) {
            ensureActivitiesVisibleLocked(null, 0);
        }
        return didSomething;
    }

执行到 realStartActivityLocked(hr, app, true, true) 里,剩下的就是 Activity 的启动流程和生命周期的调用,具体的内容都在 framework 笔记14 中,这里跳过。

本章节内容并没有很多的内容,主要是对以前的知识有一个小的梳理和扩充。把进程的启动全部串联了起来,下一章节讲对 setContentView() 进行分析。

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

推荐阅读更多精彩内容