Android应用框架之应用启动过程

在Android的应用框架中,ActivityManagerService是非常重要的一个组件,尽管名字叫做ActivityManagerService,但通过之前的博客介绍,我们知道,四大组件的创建都是有AMS来完成的,其实不仅是应用程序中的组件,连Android应用程序本身也是AMS负责启动的。AMS本身运行在一个独立的进程中,当系统决定要在一个新的进程中启动一个Activity或者Service时就会先启动这个进程。而AMS启动进程的过程是从startProcessLocked启动的。

1.ActivityManagerService.startProcessLocked

public final class ActivityManagerService extends ActivityManagerNative  
        implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {  
    ......  
    private final void startProcessLocked(ProcessRecord app,  
                String hostingType, String hostingNameStr) {  
        ......  
        try {  
            int uid = app.info.uid;  
            int[] gids = null;  
            try {  
                gids = mContext.getPackageManager().getPackageGids(  
                    app.info.packageName);  
            } catch (PackageManager.NameNotFoundException e) {  
                ......  
            }  
            ......  
            int debugFlags = 0;  
            ......  
            int pid = Process.start("android.app.ActivityThread",  
                mSimpleProcessManagement ? app.processName : null, uid, uid,  
                gids, debugFlags, null);  
            ......  
        } catch (RuntimeException e) {  
            ......  
        }  
    }  
    ......  
}  

可以看到,函数会调用Process.start函数来创建一个进程,其中第一个参数"android.app.ActivityThread"是需要加载的类,而在完成这个类的加载之后就会运行ActivityThread.main函数。

2.Process.start

public class Process {
    ......
    public static final int start(final String processClass,
        final String niceName,
        int uid, int gid, int[] gids,
        int debugFlags,
        String[] zygoteArgs)
    {
        if (supportsProcesses()) {
            try {
                return startViaZygote(processClass, niceName, uid, gid, gids,
                    debugFlags, zygoteArgs);
            } catch (ZygoteStartFailedEx ex) {
                ......
            }
        } else {
            ......
            return 0;
        }
    }
    ......
}

这个函数最后会调用startViaZygote来创建进程,而Zygote正是Android孵化进程的服务,所有的进程都是通过Zygotefork出来的,所以这里创建进程的任务又落到了Zygote头上了。

3.Process.startViaZygote

public class Process {
    ......
    private static int startViaZygote(final String processClass,
            final String niceName,
            final int uid, final int gid,
            final int[] gids,
            int debugFlags,
            String[] extraArgs)
            throws ZygoteStartFailedEx {
        int pid;

        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);
            if ((debugFlags & Zygote.DEBUG_ENABLE_SAFEMODE) != 0) {
                argsForZygote.add("--enable-safemode");
            }
            if ((debugFlags & Zygote.DEBUG_ENABLE_DEBUGGER) != 0) {
                argsForZygote.add("--enable-debugger");
            }
            if ((debugFlags & Zygote.DEBUG_ENABLE_CHECKJNI) != 0) {
                argsForZygote.add("--enable-checkjni");
            }
            if ((debugFlags & Zygote.DEBUG_ENABLE_ASSERT) != 0) {
                argsForZygote.add("--enable-assert");
            }

            //TODO optionally enable debuger
            //argsForZygote.add("--enable-debugger");

            // --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);
            }

            argsForZygote.add(processClass);

            if (extraArgs != null) {
                for (String arg : extraArgs) {
                    argsForZygote.add(arg);
                }
            }
            pid = zygoteSendArgsAndGetPid(argsForZygote);
        }
    }
    ......
}

函数里面最为重要的工作就是组装argsForZygote参数,这些参数将告诉Zygote具体的启动选项,例如"--runtime-init"就表示要为新启动的运行程序初始化运行库。然后调用zygoteSendAndGetPid函数进一步操作。

4.Process.zygoteSendAndGetPid

public class Process {
    ......

    private static int zygoteSendArgsAndGetPid(ArrayList<String> args)
            throws ZygoteStartFailedEx {
        int pid;

        openZygoteSocketIfNeeded();

        try {
            /**
            * See com.android.internal.os.ZygoteInit.readArgumentList()
            * Presently the wire format to the zygote process is:
            * a) a count of arguments (argc, in essence)
            * b) a number of newline-separated argument strings equal to count
            *
            * After the zygote process reads these it will write the pid of
            * the child or -1 on failure.
            */

            sZygoteWriter.write(Integer.toString(args.size()));
            sZygoteWriter.newLine();

            int sz = args.size();
            for (int i = 0; i < sz; i++) {
                String arg = args.get(i);
                if (arg.indexOf('\n') >= 0) {
                    throw new ZygoteStartFailedEx(
                        "embedded newlines not allowed");
                }
                sZygoteWriter.write(arg);
                sZygoteWriter.newLine();
            }

            sZygoteWriter.flush();

            // Should there be a timeout on this?
            pid = sZygoteInputStream.readInt();

            if (pid < 0) {
                throw new ZygoteStartFailedEx("fork() failed");
            }
        } catch (IOException ex) {
            ......
        }
        return pid;
    }
    ......
}

这里的sZygoteWriter是一个Socket写入流,是由openZygoteSocketIfNeeded函数打开的。而这个Socket由frameworks/base/core/java/com/android/internal/os/ZygoteInit.java文件中的ZygoteInit类在runSelectLoopMode函数侦听的。这个类会返回一个ZygoteConnection实例,并执行ZygoteConnection的runOnce函数。

5.ZygoteConnection.runOnce

class ZygoteConnection {
    ......

    boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {
        String args[];
        Arguments parsedArgs = null;
        FileDescriptor[] descriptors;

        try {
            args = readArgumentList();
            descriptors = mSocket.getAncillaryFileDescriptors();
        } catch (IOException ex) {
            ......
            return true;
        }

        ......

        /** the stderr of the most recent request, if avail */
        PrintStream newStderr = null;

        if (descriptors != null && descriptors.length >= 3) {
            newStderr = new PrintStream(
                new FileOutputStream(descriptors[2]));
        }

        int pid;
        
        try {
            parsedArgs = new Arguments(args);

            applyUidSecurityPolicy(parsedArgs, peer);
            applyDebuggerSecurityPolicy(parsedArgs);
            applyRlimitSecurityPolicy(parsedArgs, peer);
            applyCapabilitiesSecurityPolicy(parsedArgs, peer);

            int[][] rlimits = null;

            if (parsedArgs.rlimits != null) {
                rlimits = parsedArgs.rlimits.toArray(intArray2d);
            }

            pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid,
                parsedArgs.gids, parsedArgs.debugFlags, rlimits);
        } catch (IllegalArgumentException ex) {
            ......
        } catch (ZygoteSecurityException ex) {
            ......
        }

        if (pid == 0) {
            // in child
            handleChildProc(parsedArgs, descriptors, newStderr);
            // should never happen
            return true;
        } else { /* pid != 0 */
            // in parent...pid of < 0 means failure
            return handleParentProc(pid, descriptors, parsedArgs);
        }
    }
    ......
}

真正创建进程的代码在Zygote.forkAndSpecialize,通过Zygote来fork出一个新的进程作为应用进程。fork函数会有两个返回,其中一个在父进程,一个在子进程,其中自进程的进程号会为0,所以按照上面的代码,这里会执行handleChildProc。

6.ZygoteConnection.handleChildProc

class ZygoteConnection {
    ......
    private void handleChildProc(Arguments parsedArgs,
            FileDescriptor[] descriptors, PrintStream newStderr)
            throws ZygoteInit.MethodAndArgsCaller {
        ......
        if (parsedArgs.runtimeInit) {
            RuntimeInit.zygoteInit(parsedArgs.remainingArgs);
        } else {
            ......
        }
    }
    ......
}

因为在创建的时候传入了“--runtime-init”,所以这里会运行RuntimeInit.zygoteInit。

public class RuntimeInit {
    ......

    public static final void zygoteInit(String[] argv)
            throws ZygoteInit.MethodAndArgsCaller {
        // TODO: Doing this here works, but it seems kind of arbitrary. Find
        // a better place. The goal is to set it up for applications, but not
        // tools like am.
        System.setOut(new AndroidPrintStream(Log.INFO, "System.out"));
        System.setErr(new AndroidPrintStream(Log.WARN, "System.err"));

        commonInit();
        zygoteInitNative();

        int curArg = 0;
        for ( /* curArg */ ; curArg < argv.length; curArg++) {
            String arg = argv[curArg];

            if (arg.equals("--")) {
                curArg++;
                break;
            } else if (!arg.startsWith("--")) {
                break;
            } else if (arg.startsWith("--nice-name=")) {
                String niceName = arg.substring(arg.indexOf('=') + 1);
                Process.setArgV0(niceName);
            }
        }

        if (curArg == argv.length) {
            Slog.e(TAG, "Missing classname argument to RuntimeInit!");
            // let the process exit
            return;
        }

        // Remaining arguments are passed to the start class's static main

        String startClass = argv[curArg++];
        String[] startArgs = new String[argv.length - curArg];

        System.arraycopy(argv, curArg, startArgs, 0, startArgs.length);
        invokeStaticMain(startClass, startArgs);
    }
    ......
}

这里有两个关键的函数调用,一个是zygoteInitNative函数调用,一个是invokeStaticMain函数调用,前者就是执行Binder驱动程序初始化的相关工作了,正是由于执行了这个工作,才使得进程中的Binder对象能够顺利地进行Binder进程间通信,而后一个函数调用,就是执行进程的入口函数,这里就是执行startClass类的main函数了,而这个startClass即是我们在Step 1中传进来的"android.app.ActivityThread"值,表示要执行android.app.ActivityThread类的main函数。

7. Zygote.invokeStaticMain

public class ZygoteInit {
    ......

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

        try {
            cl = loader.loadClass(className);
        } catch (ClassNotFoundException ex) {
            ......
        }

        Method m;
        try {
            m = cl.getMethod("main", new Class[] { String[].class });
        } catch (NoSuchMethodException ex) {
            ......
        } catch (SecurityException ex) {
            ......
        }

        int modifiers = m.getModifiers();
        ......

        /*
        * This throw gets caught in ZygoteInit.main(), which responds
        * by invoking the exception's run() method. This arrangement
        * clears up all the stack frames that were required in setting
        * up the process.
        */
        throw new ZygoteInit.MethodAndArgsCaller(m, argv);
    }
    ......
}

从代码中可以看到,通过ClassLoader加载对应的android.app.ActivityThread类,然后再获取到对应的main函数句柄,最后调用该类的main函数。不过这里的调用方式比较有意思,不知直接调用,而是通过抛出一个异常。这样做的方式是为了清空堆栈,让系统认为新进程是从ActivityThread的main函数开始的。

8.ActivityThread.main

public final class ActivityThread {
    ......

    public static final void main(String[] args) {
        SamplingProfilerIntegration.start();

        Process.setArgV0("<pre-initialized>");

        Looper.prepareMainLooper();
        if (sMainThreadHandler == null) {
            sMainThreadHandler = new Handler();
        }

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (false) {
            Looper.myLooper().setMessageLogging(new
                LogPrinter(Log.DEBUG, "ActivityThread"));
        }
        Looper.loop();

        if (Process.supportsProcesses()) {
            throw new RuntimeException("Main thread loop unexpectedly exited");
        }

        thread.detach();
        String name = (thread.mInitialApplication != null)
            ? thread.mInitialApplication.getPackageName()
            : "<unknown>";
        Slog.i(TAG, "Main thread of " + name + " is now exiting");
    }

    ......
}

从这里我们可以看出,这个函数首先会在进程中创建一个ActivityThread对象,然后进入消息循环中,这样,我们以后就可以在这个进程中启动Activity或者Service了。

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

推荐阅读更多精彩内容