Android Activity 启动过程(中)

前言

本篇是 Activity 启动过程(中)篇,承接上篇文章 Android Activity 启动过程(上)

Android Activity 启动过程(上) 主要描述SystemServer 进程如何处理startActivity请求以及如何走到请求Zygote fork app 进程的流程

此篇主要是介绍Zygote进程接收到 来自SystemServer进程请求创建App进程的消息后,如何去fork app 进程。

Zygote 进程 fork App进程过程

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

scss 复制代码
Runnable runSelectLoop(String abiList) {
    ArrayList<FileDescriptor> socketFDs = new ArrayList<>();
    ArrayList<ZygoteConnection> peers = new ArrayList<>();
    // 省略
    mUsapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP;
​
    while (true) {
        fetchUsapPoolPolicyPropsWithMinInterval();
        mUsapPoolRefillAction = UsapPoolRefillAction.NONE;
        // 省略
        try {
            pollReturnValue = Os.poll(pollFDs, pollTimeoutMs);//chld 正常情况下此处传入的值是 -1,即一直轮询等待数据
        } catch (ErrnoException ex) {
            throw new RuntimeException("poll failed", ex);
        }
​
        if (pollReturnValue == 0) {
            mUsapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP;
            mUsapPoolRefillAction = UsapPoolRefillAction.DELAYED;
        } else {
            boolean usapPoolFDRead = false;
            while (--pollIndex >= 0) {
                if ((pollFDs[pollIndex].revents & POLLIN) == 0) {
                    continue;
                }
​
                if (pollIndex == 0) {//chld - 走到 pollIndex 说明此时没有需要处理的创建进程的需求,而是有的新的连接到来
                    // Zygote server socket
                    ZygoteConnection newPeer = acceptCommandPeer(abiList);// chld 等待 新的链接,没有时 阻塞着
                    peers.add(newPeer);
                    socketFDs.add(newPeer.getFileDescriptor());
​
                } else if (pollIndex < usapPoolEventFDIndex) { //  chld  说明此时系统不支持 usap.(如果有的话,pollIndex 值是大于等于 usapPoolEventFDIndex 的,原因具体情况Android 11 对应代码,此处相关逻辑已被删除)
                    // Session socket accepted from the Zygote server socket
​
                    try {
                        ZygoteConnection connection = peers.get(pollIndex);
                        final Runnable command = connection.processOneCommand(this);// 代码1 
​
                        // TODO (chriswailes): Is this extra check necessary?
                        if (mIsForkChild) {// chld 此时处于fork出来的应用进程中,返回 Runnable 对象,去在`ZygoteInit::main`方法中执行
                            // We're in the child. We should always have a command to run at
                            // this stage if processOneCommand hasn't called "exec".
                            if (command == null) {
                                throw new IllegalStateException("command == null");
                            }
​
                            return command;
                        } else {
                            if (connection.isClosedByPeer()) {//代码2  Zygote 进程关闭 socket 连接
                                connection.closeSocket();
                                peers.remove(pollIndex);
                                socketFDs.remove(pollIndex);
                            }
                        }
                    } catch (Exception e) {
                        // 省略
                    } finally {
                        // 重置标志
                        mIsForkChild = false;
                    }
​
                } else {// chld USAP 启动
                    // 省略
                    usapPoolFDRead = true;
                }
            }
​
            if (usapPoolFDRead) {
                int usapPoolCount = Zygote.getUsapPoolCount();
​
                if (usapPoolCount < mUsapPoolSizeMin) {//chld  最小值默认是1,即表示此时 可用USAP进程小于1个
                    // Immediate refill
                    mUsapPoolRefillAction = UsapPoolRefillAction.IMMEDIATE;
                } else if (mUsapPoolSizeMax - usapPoolCount >= mUsapPoolRefillThreshold) {//chld 可使用的USAP 进程低于阈值(最大10个,阈值一般是最大值的一半)
                    // Delayed refill
                    mUsapPoolRefillTriggerTimestamp = System.currentTimeMillis();
                }
            }
        }
​
        if (mUsapPoolRefillAction != UsapPoolRefillAction.NONE) {// chld 说明此时使用 usap 方式启动
            int[] sessionSocketRawFDs =
                    socketFDs.subList(1, socketFDs.size())
                            .stream()
                            .mapToInt(FileDescriptor::getInt$)
                            .toArray();
​
            final boolean isPriorityRefill =
                    mUsapPoolRefillAction == UsapPoolRefillAction.IMMEDIATE;// chld 判断是否立即将usap 进程补充完
​
            final Runnable command =
                    fillUsapPool(sessionSocketRawFDs, isPriorityRefill);// 代码3
​
            if (command != null) {
                return command;
            } else if (isPriorityRefill) {
                // Schedule a delayed refill to finish refilling the pool.
                mUsapPoolRefillTriggerTimestamp = System.currentTimeMillis();
            }
        }
    }
}

代码3 处,是在使用usap 方式启动。此处暂时不细讲了,有兴趣的同学自行搜索下吧,usap 只是免除了进程的fork,加快了应用启动速度,其他流程基本一致。(USAP 进程默认会创建10个待用,待低于一半值时重新填空完成,同时其是在避免影响应用进程创建的,在没有应用创建请求的时候去创建) 而在未开启 usap 启动方式的情况下,正常是走到代码1处, processOneCommand 返回一个 Runnable 对象,该 Runnable 对象将在 ZygnoteInit::main 被执行(注:在Android 8.1 及之前,是通过抛出异常的方式抛到 ZygnoteInit::main 方法中被捕获后执行的,一种奇奇怪怪的古怪方式,在Android 9开始终于才用 返回的方式了)。 该 Runnable对象的 run方法中将执行 ActivityThread 的创建方法。

接下来看下 ZygoteConnection::processOneCommand 方法吧

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

ini 复制代码
​
Runnable processOneCommand(ZygoteServer zygoteServer) {
    String[] args;
    try {
        args = Zygote.readArgumentList(mSocketReader);
    } catch (IOException ex) {
        throw new IllegalStateException("IOException on command socket", ex);
    }
  
    if (args == null) {
        isEof = true;
        return null;
    }
    ZygoteArguments parsedArgs = new ZygoteArguments(args);//代码1
​
    //... 省略
​
    // 代码2
    pid = Zygote.forkAndSpecialize(parsedArgs.mUid, parsedArgs.mGid, parsedArgs.mGids,
            parsedArgs.mRuntimeFlags, rlimits, parsedArgs.mMountExternal, parsedArgs.mSeInfo,
            parsedArgs.mNiceName, fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote,
            parsedArgs.mInstructionSet, parsedArgs.mAppDataDir, parsedArgs.mIsTopApp,
            parsedArgs.mPkgDataInfoList, parsedArgs.mWhitelistedDataInfoList,
            parsedArgs.mBindMountAppDataDirs, parsedArgs.mBindMountAppStorageDirs);
​
    try {
        if (pid == 0) {//代码3 子进程
            zygoteServer.setForkChild();
            zygoteServer.closeServerSocket();
            IoUtils.closeQuietly(serverPipeFd);// 关闭与server的通道
            serverPipeFd = null;
            return handleChildProc(parsedArgs, childPipeFd, parsedArgs.mStartChildZygote);// 代码4
        } else {
            IoUtils.closeQuietly(childPipeFd);
            childPipeFd = null;
            handleParentProc(pid, serverPipeFd);// 代码5
            // 代码6 
            return null;
        }
    } finally {
        // 最终再尝试一次关闭管道
        IoUtils.closeQuietly(childPipeFd);
        IoUtils.closeQuietly(serverPipeFd);
    }
}

代码1处 将 SystemServer发送来的请求数参数进行转换成 ZygoteArguments 对象。 代码2处 应用将根据这些参数调用 Zygote.forkAndSpecialize方法去fork 一个进程,forkAndSpecialize 下面的处理就涉及作者盲区了。 代码3处 fork后进入代码3判断处,此处根据 pid 的值判断当前是处于fork 处理的子进程还是 父进程。 子线程将进入代码4处调用 handleChildProc 方法处理。而 Zygote 将进入代码5处,并在代码6处返回 null,通过null 来告知上层,此时是处于子进程中。并且在 handleParentProc方法中 Zygote 进程将告知 SystemServer 创建的进程的id.

先看下 handleParentProc方法。

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

csharp 复制代码
private void handleParentProc(int pid, FileDescriptor pipeFd) {
    //... 省略
    try {
        mSocketOutStream.writeInt(pid);
        mSocketOutStream.writeBoolean(usingWrapper);
    } catch (IOException ex) {
        throw new IllegalStateException("Error writing to command socket", ex);
    }
}

此处通过socket 将进程pid 和 usingWrapper 数据传回给了 SysmtemServer 进程的 2.6 章节处 的 ZygoteProcess::attemptZygoteSendArgsAndGetResult方法中.

接下来返回看下 ZygoteConnection::handleChildProc 方法中是怎么去创建一个 Runnable 对象的。

typescript 复制代码
 private Runnable handleChildProc(ZygoteArguments parsedArgs,
            FileDescriptor pipeFd, boolean isZygote) {
        closeSocket();
        Zygote.setAppProcessName(parsedArgs, TAG);//此时设置进程名称
        // chld 应用进程此处 mInvokeWith:null  isZygote:false
        // 输出结果:  I/Zygote: chld handleChildProc setAppProcessName mNiceName:com.example.myapplication2 mPackageName:com.example.myapplication2 mInvokeWith:null isZygote:false
        Log.i(TAG,"chld handleChildProc setAppProcessName mNiceName:" + parsedArgs.mNiceName + " mPackageName:" + parsedArgs.mPackageName + " mInvokeWith:" + parsedArgs.mInvokeWith +" isZygote:" + isZygote);
        if (parsedArgs.mInvokeWith != null) {
            WrapperInit.execApplication(parsedArgs.mInvokeWith,
                    parsedArgs.mNiceName, parsedArgs.mTargetSdkVersion,
                    VMRuntime.getCurrentInstructionSet(),
                    pipeFd, parsedArgs.mRemainingArgs);
            // Should not get here.
            throw new IllegalStateException("WrapperInit.execApplication unexpectedly returned");
        } else {
            if (!isZygote) {
                return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,
                        parsedArgs.mDisabledCompatChanges,
                        parsedArgs.mRemainingArgs, null /* classLoader */);
            } else {// chld isZygote 判断创建的这个新的进程本身是不是一个Zygote(孵化器) 进程,创建的是应用进程显然是走此处
                return ZygoteInit.childZygoteInit(parsedArgs.mTargetSdkVersion,
                        parsedArgs.mRemainingArgs, null /* classLoader */);//代码2
            }
        }
    }

代码1 处此时设置了进程的名字,一般是应用的包名。在此之前 Zygote名称为 Zygote,有兴趣的同学可以试试。注意:如果是UASP 方式启动,在调用 Zygote.setAppProcessName之前名称为 UASP 。 代码2 这个Runnable 对象还得再通过 ZygoteInit::childZygoteInit 方法去创建啊。此处还需要的参数只剩下了 parsedArgs.mTargetSdkVersion,parsedArgs.mRemainingArgs

接下来去看下 ZygoteInit::childZygoteInit 方法的实现吧。

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

arduino 复制代码
static final Runnable childZygoteInit(
            int targetSdkVersion, String[] argv, ClassLoader classLoader) {
    RuntimeInit.Arguments args = new RuntimeInit.Arguments(argv);
    return RuntimeInit.findStaticMain(args.startClass, args.startArgs, classLoader);
}

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

php 复制代码
protected static Runnable findStaticMain(String className, String[] argv,
        ClassLoader classLoader) {
    Class<?> cl;
​
    try {
        cl = Class.forName(className, true, classLoader);
    } 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 });
    } 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);
    }
​
    int modifiers = m.getModifiers();
    if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {
        throw new RuntimeException(
                "Main method is not public and static on " + className);
    }
​
    /*
        * 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.
        */
    return new MethodAndArgsCaller(m, argv);// 代码1
}

findStaticMain 方法中传入的 className 就是 2.5 ProcessList ProcessList::startProcessLocked 方法中 final String entryPoint = "android.app.ActivityThread" 值;, 此处获取了该类的静态方法,并将参数传入。 然后一路返回到 ZygoteInit::main方法吧。

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

java 复制代码
public static void main(String argv[]) {
    ZygoteServer zygoteServer = null;
    Runnable caller;
    try {
        //...
        zygoteServer = new ZygoteServer(isPrimaryZygote);
        if (startSystemServer) {// 代码1 fork SystemServer 进程
            Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer);
​
            // {@code r == null} in the parent (zygote) process, and {@code r != null} in the
            // child (system_server) process.
            if (r != null) {
                r.run();
                return;
            }
        }
​
        Log.i(TAG, "Accepting command socket connections");
​
        // The select loop returns early in the child process after a fork and
        // loops forever in the zygote.
        caller = zygoteServer.runSelectLoop(abiList);// 代码2 等待AMS创建进行请求,当创建完应用子进程后,应用子进程会返回一个`Runnable` 对象
    } catch (Throwable ex) {
        Log.e(TAG, "System zygote died with exception", ex);
        throw ex;
    } finally {
        if (zygoteServer != null) {
            zygoteServer.closeServerSocket();
        }
    }
    if (caller != null) {//chld  应用进程f 
        caller.run();
    }
}
​

可以看到此处执行了 MethodAndArgsCaller::run方法,在该 run方法中去进行了 ActivityAThread::main 方法的反射调用。

为什么不直接在 RuntimeInit::findStaticMain 调用呢? 这是为了清除 创建过程中堆栈,让应用进程看起来是直接从 ZygoteInit::main方法中直接启动的。(太长的栈,那遇到异常打印出来,得携带多少无用的栈数据啊)

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

php 复制代码
static class MethodAndArgsCaller 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() {
        try {
            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);
        }
    }
}

如上是 MethodAndArgsCaller类,run 没什么可说的,就是一个反射调用。接下来就走到 ActivityThread类中啦,也真正的进入应用层了。

相关推荐
无极程序员36 分钟前
PHP常量
android·ide·android studio
独行soc2 小时前
#渗透测试#SRC漏洞挖掘#深入挖掘XSS漏洞02之测试流程
web安全·面试·渗透测试·xss·漏洞挖掘·1024程序员节
萌面小侠Plus2 小时前
Android笔记(三十三):封装设备性能级别判断工具——低端机还是高端机
android·性能优化·kotlin·工具类·低端机
慢慢成长的码农2 小时前
Android Profiler 内存分析
android
大风起兮云飞扬丶2 小时前
Android——多线程、线程通信、handler机制
android
理想不理想v2 小时前
‌Vue 3相比Vue 2的主要改进‌?
前端·javascript·vue.js·面试
L72562 小时前
Android的Handler
android
清风徐来辽2 小时前
Android HandlerThread 基础
android
sszmvb12343 小时前
测试开发 | 电商业务性能测试: Jmeter 参数化功能实现注册登录的数据驱动
jmeter·面试·职场和发展
测试杂货铺3 小时前
外包干了2年,快要废了。。
自动化测试·软件测试·python·功能测试·测试工具·面试·职场和发展