Android启动之应用进程创建和启动

前言

启动一个应用的前提就是这个应用所需的进程已经启动,所以AMS在启动应用是会首先检查这个应用的进程是否存在,如果不存在就会请求Zygote进程fork一个新的进程并启动起来。

在前面的文章里我们知道Zygote会创建一个Socket,这个Socket用来等待AMS的请求,当收到请求会fork自身创建应用进程,这样就会获得进程中自带的虚拟机实例。

进程创建过程中除了获取虚拟机实例,还会创建Binder线程池和消息循环,这样运作在进程中的应用就可以进行进程间通信了。

AMS发送启动进程请求

首先AMS通过调用startProcessLocked向Zygote进行发送请求,这个过程大致如下:

在这里插入图片描述

先重点来看看ZygoteProcess的openZygoteSocketIfNeeded这个方法,部分代码如下:

private ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx {
        ...

        if (primaryZygoteState == null || primaryZygoteState.isClosed()) {
            try {
                primaryZygoteState = ZygoteState.connect(mSocket);
            } catch (IOException ioe) {
                throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe);
            }
            ...
        }
        if (primaryZygoteState.matches(abi)) {
            return primaryZygoteState;
        }

        // The primary zygote didn't match. Try the secondary.
        if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) {
            try {
                secondaryZygoteState = ZygoteState.connect(mSecondarySocket);
            } catch (IOException ioe) {
                throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe);
            }
            ...
        }

        if (secondaryZygoteState.matches(abi)) {
            return secondaryZygoteState;
        }

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

之前文章讲到过在Zygote的main方法中会创建名为"zygote"的Socket。在openZygoteSocketIfNeeded方法中可以看到会调用ZygoteState的connect方法与mSocket建立连接,mSocket就是名为“zygote”的Socket,并返回ZygoteState类型的对象primaryZygoteState。然后可以看到检查primaryZygoteState与应用程序进程所需的ABI是否匹配,如果不匹配则会继续通过connect连接mSecondarySocket,这个则是名为“zygote_secondary”的Socket。

在之前文章中说过Zygote分32位和64位,再组合成主模式+辅模式可以有4种脚本,名为“zygote”的就是主模式,名为“zygote_secondary”就是辅模式。

所以上面的代码简单来说就是先连接主模式的Zygote,然后判断是否匹配ABI,如果不匹配就连接辅模式。

这个ZygoteState对象会返回给zygoteSendArgsAndGetResult方法,然后将应用进程的启动参数写入ZygoteState,这样就将启动进程的请求发送到Zygote了。

Zygote接受请求并创建进程

先来看看这个过程的大致流程:

在这里插入图片描述

回到Zygote的main方法中,这部分前面文章讲过了,这里简单回顾一下。这里会创建一个name为"zygote"的Socket来等待AMS请求,然后预加载类和资源,接下来启动SystemServer进程,SystemServer进程也会启动系统服务,最后会调用ZygoteServer的runSelectLoop来等待AMS发送创建新应用进程的请求。

runSelectLoop会调用runOnce函数来处理请求,这个函数代码如下:

boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {

   ...

        pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,  parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo, parsedArgs.niceName, fdsToClose, parsedArgs.instructionSet,parsedArgs.appDataDir);

    } catch(){

    }
    try {
        if (pid == 0) {
            ...
            IoUtils.closeQuietly(serverPipeFd);
            serverPipeFd = null;
            handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr); 
            return true;

        } else {
            IoUtils.closeQuietly(childPipeFd);
            childPipeFd = null;
            return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs); 
        }

    } finally {
        IoUtils.closeQuietly(childPipeFd);
        IoUtils.closeQuietly(serverPipeFd);
    }
}

在这个函数中会调用readArgumentList方法来获取应用进程的启动参数,也就是之前写入ZygoteState对象的。然后可以看到执行了forkAndSpecialize方法用这些参数来fork一个应用程序进程,返回pid。如果pid是0说明代码运行在新创建的应用进程中,就会执行handleChildProc。

在handleChildProc方法中调用了ZygoteInit的zygoteInit方法,代码如下:

public static final Runnable zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader) {
    ...
    RuntimeInit.redirectLogStreams();
    RuntimeInit.commonInit();
    ZygoteInit.nativeZygoteInit();
    return RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
}

注意ZygoteInit.nativeZygoteInit()这一步会创建Binder线程池,这个我们后续再说。然后调用了RuntimeInit的applicationInit函数。这个函数中会调用invokeStaticMain方法,代码如下:

private static void invokeStaticMain(String className, String[] argv, ClassLoader classLoader)
            throws Zygote.MethodAndArgsCaller {
        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);
        }

        ...

        throw new Zygote.MethodAndArgsCaller(m, argv); 
}

这里通过反射获得了ActivityThread类,并获得了它的main方法,然后将main方法传入MethodAndArgsCaller类,这个类是一个异常类,最后抛出这个异常。在Zygote的main方法中会捕获这个异常并获取main方法执行。

这里为什么不直接执行main而是抛出异常呢?和之前文章中Zygote处理SystemServer进程一样,抛出异常会清除所有以上过程的堆栈帧,这样ActivityThread的main方法就是最初的,看起来像是应用程序进程的入口方法一样。

Binder线程池

上面说到在Zygote处理创建应用进程的请求过程中会在zygoteInit方法中通过ZygoteInit.nativeZygoteInit()这一步来创建Binder线程池。

很明显nativeZygoteInit是JNI方法,对应的是AndroidRuntime.cpp中的con_android_internal_os_ZygoteInit_nativeZygoteInit函数,这个函数只有一行代码

gCurRuntime->onZygoteInit()

gCurRuntime是AndroidRuntime类型的指针,在这里实际上是AppRuntime,因为AppRuntime继承自AndroidRuntime,它创建的时候就会创建这个指针。

而onZygoteInit函数的真正实现则是在app_main.app中,代码如下

virtual void onZygoteInit(){
    sp<ProcessState> proc = ProcessState::self();
    if(proc->supportsProcesses()){
        proc->startThreadPool();
    }
}

最后一步调用ProcessState的startThreadPool函数来启动Binder线程池,这个函数如下:

void ProcessState::startThreadPool()
{
    AutoMutex _l(mLock);
    if (!mThreadPoolStarted) {
        mThreadPoolStarted = true;
        spawnPooledThread(true);
    }
}

其中mThreadPoolStarted用来标识Binder线程池是否启动过,这样确保Binder线程池只启动一次。如果未启动则调用spawnPooledThread来创建线程池中的第一个线程——主线程。

void ProcessState::spawnPooledThread(bool isMain)
{
    if (mThreadPoolStarted) {
        String8 name = makeBinderThreadName();
        sp<Thread> t = new PoolThread(isMain);
        t->run(name.string());
    }
}

Binder线程都是PoolThread对象,然后执行run函数启动线程。PoolThread代码如下:

class PoolThread : public Thread
{
...
protected:
    virtual bool threadLoop()
    {
        IPCThreadState::self()->joinThreadPool(mIsMain);
        return false;
    }
    
    const bool mIsMain;
};

在PoolThread中调用IPCThreadState的joinThreadPool函数,可以将当前线程注册到Binder驱动中,这样就加入了Binder线程池,新创建的应用进程就支持Binder进程通信了。我们只需要创建当前进程的Binder对象,将它注册到ServiceManager中就可以实现Binder进程间通信,不必关系进程间是如何通过Binder通信。

消息循环创建

在上面的进程中,还有一个过程没有提到,就是应用程序进程启动后会创建消息循环。

上面我们提到Zygote处理创建应用进程请求的最后是通过抛出异常,然后在ZygoteInit的main方法中捕获,从异常中获取ActivityThread的main方法并执行的。这个main方法的代码如下:

public static void main(String[] args) {
   
	Looper.prepareMainLooper();

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

	if (sMainThreadHandler == null) {
		sMainThreadHandler = thread.getHandler();
	}

	Looper.loop();

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

ActivityThread类用于管理当前应用进程的主线程,在这里可以看到创建了主线程的消息循环looper;然后创建了ActivityThread;接下来赋值sMainThreadHandler,这个handler就用于处理主线程的消息;最后通过loop()函数将消息循环启动起来。

这样主线程中就启动了消息循环了,就可以通过Handler进行消息通信。

总结

上面我们总结了应用进程的创建和启动过程,其实主要就是两个部分:

  • AMS发送创建应用程序进程的请求到Zygote
  • Zygote接收创建应用程序进程的请求,fork出新的进程并启动

当然在这个过程中有很多细节,最重要就是Binder线程池和消息循环的创建和启动。

最后

如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。

如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
img
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。

欢迎大家一键三连支持,若需要文中资料,直接扫描文末CSDN官方认证微信卡片免费领取↓↓↓(文末还有ChatGPT机器人小福利哦,大家千万不要错过)

PS:群里还设有ChatGPT机器人,可以解答大家在工作上或者是技术上的问题
图片

  • 21
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值