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类中啦,也真正的进入应用层了。

相关推荐
踏雪羽翼几秒前
android 使用实现音效--Equalizer
android·音效·eqequalizer·bassboost·presetreverb
老码沉思录4 分钟前
Android开发实战班 - Android开发基础之 Kotlin语言基础与特性
android·微信·kotlin
前端fighter4 分钟前
js基本数据新增的Symbol到底是啥呢?
前端·javascript·面试
峥嵘life25 分钟前
Android adb shell dumpsys audio 信息查看分析详解
android·adb
豆子熊.1 小时前
外包干了3年,技术退步明显...
软件测试·selenium·测试工具·面试·职场和发展
GISer_Jing1 小时前
Vue前端进阶面试题目(二)
前端·vue.js·面试
standxy2 小时前
集成金蝶云星空数据至MySQL的完整案例解析
android·数据库·mysql
小墙程序员3 小时前
一文了解 Gradle 插件
android·gradle
数懒女士3 小时前
饿汉模式和懒汉模式(面试)
面试·职场和发展
老码沉思录3 小时前
Android开发实战班 -网络编程 - Retrofit 网络请求 + OkHttp 使用详解
android·网络·retrofit