android进阶——深入理解应用程序的启动过程(上)

android进阶——深入理解应用程序的启动过程(上)

Android小彩虹2021-07-10 11:20:24130A+A-

简介

当我们打开android手机的时候,不知道你是否想过app是如何启动的呢?

接下来,我将从源码角度进行解析,当然,本文作为上篇,是介绍应用程序的进程启动过程,而不是应用程序的启动过程,他们的区别就是煮饭前要准备锅具,没有锅具就无法煮饭,本文就是准备锅具的,但是也不简单哦。

文章将从两个方面介绍,一个AMS发送请求,一个是Zygote接受请求。

AMS就是Activity Manager Sevice,管理Activity的,而Zygote就是创建进程的一个进程,所以AMS要想创建进程必须从Zygote那里进行申请,一系列的故事,就此展开。

AMS发送请求

image.png

从图中可以简略看出,首先AMS自己调用了自己的startProcessLocked方法,会创建相应的用户id,用户id组,以及对应的应用程序进程的主线程名——android.app.ActivityThread

private final void startProcessLocked(ProcessRecord app, String hostingType, String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {
  	...
    try {
        int uid = app.uid;
        int[] gids = null;
        int mountExternal = Zygote.MOUNT_EXTERNAL_NONE;
        if (!app.isolated) {
            int[] permGids = null;
            if (ArrayUtils.isEmpty(permGids)) {
                gids = new int[3];
            } else {
                gids = new int[permGids.length + 3];
                System.arraycopy(permGids, 0, gids, 3, permGids.length);
            }
            gids[0] = UserHandle.getSharedAppGid(UserHandle.getAppId(uid));
            gids[1] = UserHandle.getCacheAppGid(UserHandle.getAppId(uid));
            gids[2] = UserHandle.getUserGid(UserHandle.getUserId(uid));
        }
        String requiredAbi = (abiOverride != null) ? abiOverride : app.info.primaryCpuAbi;
        if (requiredAbi == null) {
            requiredAbi = Build.SUPPORTED_ABIS[0];
        }

		...
        if (entryPoint == null) entryPoint = "android.app.ActivitThread";
      	...
        if (hostingType.equals("webview_service")) {
            ...
        } else {
            startResult = Process.start(entryPoint,
                    app.processName, uid, uid, gids, debugFlags, mountExternal,
                    app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
                    app.info.dataDir, invokeWith, entryPointArgs);
        }
   		...
}

这里面的start中有一个参数名为requireAbi,在下面会改成abi起到比对的重要作用。

startProcessLocked方法内部会调用Process的start方法,而这个start方法只干了一件事,就是调用ZygoteProcessstart方法。

ZygoteProcess的作用在于保持与Zygote通信的状态,在其start方法中,会调用StartViaZygote方法,这里的出现了下面很重要的abi。

public final Process.ProcessStartResult start(final String processClass, final String niceName, int uid, int gid, int[] gids, ... String abi, ... String[] zygoteArgs) {
        try {
            return startViaZygote(processClass, niceName, uid, gid, gids,
                    debugFlags, mountExternal, targetSdkVersion, seInfo,
                    abi, instructionSet, appDataDir, invokeWith, zygoteArgs);
       ...
    }

StartViaZygote方法会维护一个名为argsForZygote的列表,顾名思义,所有能够启动应用程序的进程的参数,都会保存在这里。然后这个列表作为一个参数,被传入到ZygoteSendArgsAndGetResult方法中,这个方法也是在StartViaZygote方法中被调用.另外要注意的是,openZygoteSocketIfNeeded方法也是作为参数传入了ZygoteSendArgsAndGetResult方法中。

private Process.ProcessStartResult startViaZygote(final String processClass, final String niceName, final int uid, final int gid, final int[] gids, ... String abi, ...) throws ZygoteStartFailedEx {
    ArrayList<String> argsForZygote = new ArrayList<String>();
    argsForZygote.add("--runtime-args");
    argsForZygote.add("--setuid=" + uid);
    argsForZygote.add("--setgid=" + gid);
 	...
    synchronized(mLock) {
        return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
    }
}

ZygoteSendArgsAndGetResult方法作用是将传入的参数,也就是argsForZygote中保存的数据写入ZygoteState中,从图中我们虽然将ZygoteStateZygoteProcess做了并列处理,但是ZygoteStateZygoteProcess的一个静态内部类,上面提到的ZygoteProcess的作用在于保持与Zygote通信的状态,其功能的完成就是在ZygoteState中。

private static Process.ProcessStartResult zygoteSendArgsAndGetResult( ZygoteState zygoteState, ArrayList<String> args) throws ZygoteStartFailedEx {
    try {
     
        final BufferedWriter writer = zygoteState.writer;
        final DataInputStream inputStream = zygoteState.inputStream;

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

        for (int i = 0; i < sz; i++) {
            String arg = args.get(i);
            writer.write(arg);
            writer.newLine();
        }

        writer.flush();
		...
        result.pid = inputStream.readInt();
        result.usingWrapper = inputStream.readBoolean();
        ...
}

从上面的方法看出,ZygoteSendArgsAndGetResult方法的第一个参数变成了ZygoteState,也就是说,openZygoteSocketIfNeeded方法的返回值是ZygoteState

openZygoteSocketIfNeeded方法也处于ZygoteProcess中,就写在StartViaZygote下面,可以看到,在第6行与Zygote进程建立了连接。

private ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedE Preconditions.checkState(Thread.holdsLock(mLock), "ZygoteProcess lock not held");

    if (primaryZygoteState == null || primaryZygoteState.isClosed()) {
        try {
            primaryZygoteState = ZygoteState.connect(mSocket);//建立连接
        } catch (IOException ioe) {
            throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe);
        }
    }
	//这里primaryZygoteState是连接主模式后返回的ZygoteState,比对其是否与需要的abi匹配
    if (primaryZygoteState.matches(abi)) {
        return primaryZygoteState;
    }
	//如果主模式不成功,连接辅模式
    if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) {
        try {
            secondaryZygoteState = ZygoteState.connect(mSecondarySocket);
        } catch (IOException ioe) {
            throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe);
        }
    }
	//匹配辅模式的abi
    if (secondaryZygoteState.matches(abi)) {
        return secondaryZygoteState;
    }

    throw new ZygoteStartFailedEx("Unsupported zygote ABI: " + abi);

为什么第6行可以连接Zygote呢?因为Zygote是最先创建的进程,如果没有Zygote,我们的应用程序的进程也无从谈起,而Zygote为了创建其他进程,他会设置一个Socket监听,就是通过这个Socket,完成与Zygote的连接。

而对于主模式和辅助模式,这里不细讲,就是有两次连接并匹配abi的机会。

要匹配abi的原因在于,Zygote虽然可以fork自己不断创建进程,但是毕竟是有限的,而且需要资源,只有启动某个应用程序所必须的进程才有被创建的意义,这时候就在一开始的AMS中给设置一个abi,如果与后来的Zygote匹配,证明是需要被创建的。

当匹配完成时,就会返回一个primaryZygoteState,此时AMS发送请求这一流程就结束了,接下来,就是Zygote如何接受请求并创建应用程序的进程的过程了。

Zygote接受请求

依然是时序图,甚至说,有了时序图,根本就不需要看后面的文章啦。

image.png

看起来比上一个图稍微复杂些,不过可以发现还是很有规律的,Zygote先到ZygoteServer再回来,再到ZygoteConnection,再回来……

所以,后面几个方法都在ZygoteInit中被调用,而且时间先后就是图中所示。

在AMS发送请求完成后,Zygote会连接到一个ZygoteState,而这个ZygoteState在之前的ZygoteSendArgsAndGetResult方法中被写入了argsForZygote所保存的数据,这些数据就是请求的具体参数。

现在Zygote将会收到这些参数,并在ZygoteInit中进行相应操作。

ZygoteInit首先会执行其main方法。

 public static void main(String argv[]) {
       ...
        try {
           ...
            //设置Socket监听
            zygoteServer.registerServerSocket(socketName);
            if (!enableLazyPreload) {
                bootTimingsTraceLog.traceBegin("ZygotePreload");
                EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
                    SystemClock.uptimeMillis());
                preload(bootTimingsTraceLog);
                EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
                    SystemClock.uptimeMillis());
                bootTimingsTraceLog.traceEnd(); // ZygotePreload
            } else {
                Zygote.resetNicePriority();
           ...

            if (startSystemServer) {
                startSystemServer(abiList, socketName, zygoteServer);//启动SystemServer进程
            }

            Log.i(TAG, "Accepting command socket connections");
            zygoteServer.runSelectLoop(abiList);//等待AMS请求

            zygoteServer.closeServerSocket();
        ...
    }

这里最后一个注释,就是一切都准备好了,只等AMS发来请求,现在我们看看RunSelectLoop方法是如何做的,猜测既然是等待,那肯定有一个死循环while,这个函数就在ZygoteServer之中。

void runSelectLoop(String abiList) throws Zygote.MethodAndArgsCaller {
    ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
    ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
    fds.add(mServerSocket.getFileDescriptor());
    peers.add(null);
    while (true) {
      	...
        for (int i = pollFds.length - 1; i >= 0; --i) {
            if ((pollFds[i].revents & POLLIN) == 0) {
                continue;
            }
            if (i == 0) {
                ZygoteConnection newPeer = acceptCommandPeer(abiList);
                peers.add(newPeer);
                fds.add(newPeer.getFileDesciptor());
            } else {
                //一个连接一个连接的运行
                boolean done = peers.get(i).runOnce(this);
                if (done) {
                    peers.remove(i);
                    fds.remove(i);
                }
            }
        }
    }
}

果然有一个while(true),哈哈,奖励自己晚上早睡十分钟。

可以看到这里维护了一个ZygoteConnection型的列表,每当连接不够时,就会增加,然后一个一个连接的进行执行,执行玩一个连接,就移除一个。

runOnce方法的作用就是读取argsForZygote所保存的数据,完成交接,这个方法显然,在ZygoteConnection中。

boolean runOnce(ZygoteServer zygoteServer) throws Zygote.MethodAndArgsCaller {
    String args[];
    Arguments parsedArgs = null;
    FileDescriptor[] descriptors;
    try {
        args = readArgumentList(); //读取之前写入的数据
        descriptors = mSocket.getAncillaryFileDescriptors();
    } catch (IOException ex) {
        Log.w(TAG, "IOException on command socket " + ex.getMessage());
        closeSocket();
        return true;
    }
	...
    try {
        parsedArgs = new Arguments(args);
		...
        //创建应用程序进程
        pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
                parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
                parsedArgs.niceName, fdsToClose, fdsToIgnore, parsedArgs.instructionSet,
                parsedArgs.appDataDir);
    }
    ...
    try {
        if (pid == 0) {//当前逻辑运行在子程序中
            zygoteServer.closeServerSocket();
            IoUtils.closeQuietly(serverPipeFd);
            serverPipeFd = null;
            handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);

            return true;
        }
        ...
    } 
}

如果在18行处的pid返回值为0,那么恭喜,此时的子进程已经创建完成,各种id和参数都传进去了,此时就会调用handleChildProc方法来处理出现的应用程序的进程(还是要提醒,本文创建的是应用程序的进程,而非应用程序本身)。

handleChildProc方法回调了ZygoteInit中的zygoteInit方法,注意,这里的z是小写!

private void handleChildProc(Arguments parsedArgs, FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr) throws Zygote.MethodAndArgsCaller {
    ...
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    if (parsedArgs.invokeWith != null) {
        WrapperInit.execApplication(parsedArgs.invokeWith,
                parsedArgs.niceName, parsedArgs.targetSdkVersion,
                VMRuntime.getCurrentInstructionSet(),
                pipeFd, parsedArgs.remainingArgs);
    } else {
        ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion,
                parsedArgs.remainingArgs, null /* classLoader */);
    }
}

现在我们回到ZygoteInit中,看看zygoteInit是怎么写的。

public static final void zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader) throws Zygote.MethodAndArgsCaller {
    if (RuntimeInit.DEBUG) {
        Slog.d(RuntimeInit.TAG, "RuntimeInit: Starting application from zygote");
    }

    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ZygoteInit");
    RuntimeInit.redirectLogStreams();

    RuntimeInit.commonInit();
    ZygoteInit.nativeZygoteInit();
    RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
}

注意到这个方法是一个静态的final方法,在12行代码处,会执行RuntimeInitapplicationInit方法。

protected static void applicationInit(int targetSdkVersion, String[] throws Zygote.MethodAndArgsCaller { ... final Arguments args; try { args = new Arguments(argv);
    } catch (IllegalArgumentException ex) {
        Slog.e(TAG, ex.getMessage());
        // let the process exit
        return;
    }
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    invokeStaticMain(args.startClass, args.startArgs, classLoader);
}

同样在最后一行,调用了invokeStaticMain方法,这里传入了三个参数,第一个参数args.startClass就是本文一开始说的那个主线程的名字android.app.MainThread

终于我们到了最后一站!现在我们进入invokeStaticMain方法。

private static void invokeStaticMain(String className, String[] argv, ClassLoader throws Zygote.MethodAndArgsCaller { Class<?> cl; try { cl = Class.forName(className, true, classLoader);//反射获取MainThread
    } catch (ClassNotFoundException ex) {
        throw new RuntimeException(
                "Missing class when invoking static main " + className,
                ex);
    }

    Method m;
    try {
        m = cl.getMethod("main", new Class[] { String[].class });//获得main
    } 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 Zygote.MethodAndArgsCaller(m, argv);
}

这里的className就是我们刚刚提到的args.startClass,也就是主线程名字,通过第6行的反射获取到对应的类,命名为c1,然后再通过15行代码获取主方法main,并将main传入到MethodAndArgsCaller方法中,当做异常抛出。

为什么这里要用抛出异常的方法呢?

联想到这是最后一步,抛出异常的方式将会清除设置过程中需要的堆栈帧,仅仅留下一个main方法,使得main方法看起来像一个入口方法,毕竟,前面那么多的工作,那么多类的流血牺牲,最后都是为了成就他。当他出现时,前面的东西,也就没有必要存在了。

现在异常抛出了,谁来接受呢?当然,是main了,main位于ZygoteInit中。

public static void main(String argv[]) {
    ...
        zygoteServer.closeServerSocket();
    } catch (Zygote.MethodAndArgsCaller caller) {
        caller.run();
    } catch (Throwable ex) {
        Log.e(TAG, "System zygote died with exception", ex);
        zygoteServer.closeServerSocket();
        throw ex;
    }
}

可以看到,当出现了一个MethodAndArgsCaller型的异常的时候,main会捕捉到,并且执行他的run方法,这个run方法在MethodAndArgsCaller中,他是一个静态类。

public static class MethodAndArgsCaller extends Exception implements Runnable {
    ...
    public void run() {
        try {
            mMethod.invoke(null, new Object[] { mArgs });
        } catch (IllegalAccessException ex) {
            throw new RuntimeException(ex);
        } catch (InvocationTargetException ex) {
           ...
        }
    }
}

调用了invoke方法后,ActivityThread里面的main方法就会被动态调用,应用程序的进程就进入了这一main方法中。

经过上面的种种操作,我们创建了应用程序的进程并且运行了ActivityThread。

小结

简单来说就是要想创建应用程序的进程,需要AMS发送请求,传入主线程名,这一请求必须符合Zygote的abi,并且将请求参数写在ZygoteState中。

然后Zygote读取参数,fork自身去生成新的进程,如果返回的pid为0,那么就完成了应用程序的进程的创建。

然后通过反射将主线程名转换为主线程的类,获得其main方法,通过抛出一个异常清除掉之前的堆栈帧,在main方法里面接受这个异常,并执行run方法以激活main方法,使得应用程序的进程进入到main方法中得以运行。

写了一早上,呼~,准备吃饭啦,还有什么不懂的可以评论区提问哦。

本文取材自《Android进阶解密》,资源在微信公众号【小松漫步】,一起交流学习吧。

点击这里复制本文地址 以上内容由权冠洲的博客整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!

支持Ctrl+Enter提交

联系我们| 本站介绍| 留言建议 | 交换友链 | 域名展示
本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除

权冠洲的博客 © All Rights Reserved.  Copyright quanguanzhou.top All Rights Reserved
苏公网安备 32030302000848号   苏ICP备20033101号-1

联系我们