Android 应用进程启动过程

Android应用进程启动流程

注意,这里讲的是应用进程的启动流程,不是应用的启动流程

本文承接部分Android系统启动流程的内容,建议有欲望的童鞋先看看:传送门

一:简介

想要启动一个应用程序,首先要保证这个应用所需要的应用程序进程已经启动。

AMS在启动应用程序时会检查这个应用所需要的应用程序进程是否存在,如果不存在就会请求Zygote进程启动一个新的应用程序进程。这个流程用到的通讯方式,就是我们在Android系统启动流程中提到过的,Zygote Server端的 Socket(这个socketName就是zygote)。

Android系统启动流程一文中提到过,Zygote在注册好了Socket之后,会调用一个方法:zygoteServer.runSelectLoop(),这样就会进入一个循环,等待AMS请求Zygote来创建新的应用程序进程。Zygote进程通过 fork自身创建应用程序进程,这样的应用程序进程就会获得Zygote进程在启动时创建的java虚拟机实例。当然,在应用程序进程创建过程中除了获取虚拟机实例外,还会创建 Binder线程池以及 Looper消息队列循环,这样运行在应用进程中的应用程序就可以方便地使用Binder进行进程间通信以及处理消息了。

二:应用程序进程的启动过程

1.AMS发送指令

AMS想要启动应用程序进程,就需要向Zygote进程发送创建应用进程的请求,AMS会通过调用
startProcessLocked方法向Zygote发送进程请求。
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java:

private final void startProcessLocked(ProcessRecord app,String hostingType,String hostingNameStr,String abiOverride,String entryPoint,String entryPointArgs){
    ...
    //获取要创建的应用程序进程的用户ID
    int uid=app.uid;
    
    ...
    //判断是否隔离
    if(!app.isolated){
        ...
        //对gids进行创建和赋值
        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));
    }
    ...
    //准备反射ActivityThread
    if(entryPoint==null) entryPoint="android.app.ActivityThread";
    
    ...
    //启动应用程序进程,参数过长请忽略
    startResult=Process.start(entryPoint,app.processName,uid,uid,gids,debugFlags,mountExternal,app.info.targetSdkVersion,seInfo,requiredAbi,instructionSet,app.info.dataDir,invokeWith,entryPointArgs);
    
    ...
}

该方法主要做了三件事,取得了uid,创建并赋值了gids,调用Process的start方法并传入ActivityThread等启动参数,准备启动进程。

接下来看看Process的start方法。

frameworks/base/core/java/android/os/ZygoteProcess.java:

//参数过长请忽略那些不太重要的
public final Process.ProcessStartResult start(final String processClass,final String niceName,int uid,int gid,int[] gids,int debugFlags,int mountExternal,int targetSdkVersion,String seInfo,String abi,String instructionSet,String appDataDir,String invokeWith,String[] zygoteArgs){
    try{
        //调用了startViaZygote方法
        return startViaZygote(processClass,niceName,uid,gid,gids,debugFlags,mountExternal,targetSdkVersion,seInfo,abi,instructionSet,appDataDir,invokeWith,zygoteArgs);
    }catch(ZygoteStartFailedEx ex){
        ...
        throw new RuntimeException("Starting VM process through Zygote failed",ex)
    }
}

...
//参数过长请忽略那些不太重要的
private Process.ProcessStartResult startViaZygote(final String processClass,final String niceName,final int uid,final int gid,final int[] gids, int debugFlags,int mountExternal,int targetSdkVersion,String seInfo,String abi,String instructionSet,String appDataDir,String invokeWith,String[] extraArgs){
    ...
    synchronized(mLock){
        //这两个方法,zygoteSendArgsAndGetResult以及openZygoteSocketifNeeded才是重点
        return zygoteSendArgsAndGetResult(openZygoteSocketifNeeded(abi),argsForZygote)
    }
}

调来调去没有什么新鲜的,接下来就有两个方法,zygoteSendArgsAndGetResult方法,和openZygoteSocketifNeeded方法,这两个才是重点。

frameworks/base/core/java/android/os/ZygoteProcess.java:

private static Process.ProcessStartResult zygoteSendArgsAndGetResult(ZygoteState zygoteState,ArrayList<String> args){
    try{
        int sz=args.size();
        for(int i=0;i<sz;i++){
            if(args.get(i).indexOf('\n') >= 0){
                throw new ZygoteStartFailedEx("embedded newlines not allowed");
            }
        }
        final BufferedWriter writer = zygoteState.writer;
        final DataInputStream inputStream = zygoteState.inputStream;
        writer.write(Integer.toString(args.size()));
        write.newLine();
        for(int i = 0; i < sz; i++){
            String arg=args.get(i);
            writer.write(arg);
            writer.newLine();
        }
        writer.flush();
        Process.ProcessStartResult result=new Process.ProcessStartResult();
        result.pid=inputStream.readInt();
        result.usingWrapper=inputStream.readBoolean();
        
        if(result.pid<0){
            throw new ZygoteStartFailedEx("fork() failed");
        }
        return result;
    }else{
        zygoteState.close();
        throw new ZygoteStartFailedEx(ex);
    }
}

从上面的读写操作,我们仿佛已经看到了Socket通信的过程。接下来ZygoteProcess中应该就有与Zygote建立Socket链接的部分了。

frameworks/base/core/java/android/os/ZygoteProcess.java:

private ZygoteState openZygoteSocketIfNeeded(String abi){
    //如果与Zygote未建立连接或者连接已断开
    if(primaryZygoteState == null || primaryZygoteState.isClosed()){
        try{
            //尝试与Zygote进程建立连接
            primaryZygoteState = ZygoteState.connect(mSocket);
        }catch(IOException ioe){
            throw new ZygoteStartFailedEx("Error connecting to primary zygote",ioe);
        }
    }
    //连接Zygote主模式返回的ZygoteState是否与启动应用程序进程所需要的ABI匹配
    if(primaryZygoteState.matches(abi)){
        return primaryZygoteState;
    }
    
    //如果不匹配,则尝试连接Zygote的辅模式
    if(secondaryZygoteState == null || secondaryZygoteState.isClosed()){
        try{
            //尝试与Zygote进程再次建立连接
            secondaryZygoteState = ZygoteState.connect(mSecondarySocket);
        }catch(IOException ioe){
            throw new ZygoteStartFailedEx("Error connecting to Secondary zygote",ioe);
        }
    }
    
    //连接Zygote辅模式返回的ZygoteState是否与启动应用进程所需要的ABI匹配
    if(secondaryZygoteState.matched(abi)){
        return secondaryZygoteState;
    }
    throw new ZygoteStartFailedEx("Unsupported zygote ABI:"+abi);
}

的确,在这段代码中我们看到了AMS尝试与Zygote建立Socket通信的代码。

这里有两个概念:

1.ABI:abi全称Application binary interface(应用程序二进制接口),主要针对各种手机CPU架构不同,作出的一套适配规则。大家可以了解下详情:传送门

2.Zygote辅模式:在上一篇文章《Android系统启动流程》介绍过,Zygote启动脚本分为4种:

1.init.zygote32.rc

2.init.zygote32_64.rc

3.init.zygote64.rc

4.init.zygote64_32.rc

对于采用的是init.zygote32_64.rc或者init.zygote64_32.rc的Zygote,Socket的name为“zygote”的为主模式,name为“zygote_secondary”为辅模式。

到此为止,AMS进程试图与Zygote建立连接的代码已经展示完成。


2.Zygote 接收请求

在Socket连接成功并匹配ABI后会返回ZygoteState类型对象,应用进程的启动参数会写入ZygoteState中,这样Zygote进程就会收到一个创建新的应用程序的进程请求。我们回到ZygoteInit的main方法中。
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

public static void main(String argv[]){
    ...
    //创建一个Server端的Socket,socketName的值为“zygote”
    zygoteServer.registerServerSocket(socketName);

    ...
    if(startSystemServer){
        //启动SystemServer进程
        startSystemServer(abiList,socketName,zygoteServer);
    }
    ...
    //等待AMS请求
    zygoteServer.runSelectLoop(abiList);
    
    ...
}

我们又回到了Android系统启动流程中,当Zygote启动后,会注册一个Server端的Socket,并且调用runSelectLoop方法来等待AMS的请求。下面来看看runSelectLoop方法。
frameworks/base/core/java/com/android/internal/os/ZygoteServer.java:

void runSelectLoop(String abiList){
    ...
    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{
                //AMS的请求过来,最终会走到这里
                boolean done = peers.get(i).runOnce(this);
                if(done){
                    peers.remove(i);
                    fds.remove(i);
                }
            }
        }
    }
}

当AMS的请求过来,最终会调用ZygoteConnection的runOnce方法来处理请求数据。
frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java:

boolean runOnce(ZygoteServer zygoteServer){
    ...
    //获取应用程序进程的启动参数
    args = readArgumentList();
    
    ...
    //对启动参数的封装
    parsedArgs = new Argument(args);
    
    ...
    //关键代码,Zygote fork出来子进程
    pid = Zygote.forkAndSpecialize(parsedArgs.uid,parsedArgs.gid,parsedArgs.gids,parsedArgs.debugFlags,rlimits,parsedArgs.mountExternal,parsedArgs.seInfo,parsedArgs.appDataDir);
    
    try{
        //注意:当前代码逻辑运行在新创建的子进程中了
        if(pid==0){
            //第一步当然是关闭Zygote与AMS通信的Socket,这对于子进程无意义
            zygoteServer.closeServerSocket();
            IoUtils.closeQuietly(serverPipeFd);
            //处理应用程序进程
            handleChildProc(parsedArgs,descriptors,childPipeFd,newStderr);
            return true;
        }else{
            ...
        }finally{
            ...
        }
    }
}

我们可以在代码中看见,最终在ZygoteConnection中的runOnce方法,Zygote 的forkAndSpecialize方法创建了一个子进程,当pid等于0时,证明当前代码逻辑运行于新创建的子进程也就是应用程序进程中了,然后调用了handleChildProc来进行下一步处理。

由于调用关系繁多,这里不做handleChildProc的代码展示了,最后在该方法中,会调用ZygoteInit的zygoteInit方法,如下图所示。
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java:

public static final void zygoteInit(int targetSdkVersion,String[] argv,ClassLoader classLoader){
    ...
    
    RuntimeInit.redirectLogStreams();
    RuntimeInit.commonInit();
    //Zygote的nativeZygoteInit方法会启动Binder线程池
    ZygoteInit.nativeZygoteInit();
    //应用程序进程初始化
    RuntimeInit.applicationInit(targetSdkVersion,argv,classLoader);
}

ZygoteiInit的nativeZygoteInit方法最后会启动Binder线程池,这部分一会再说,先继续探究RuntimeInit的applicationInit方法。

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

protected static void applicationInit(int targetSdkVersion,String[] argv,ClassLoader classLoader){
    ...
    final Arguments args;
    //包装参数
    args=Arguments(argv);
    ...
    //args.startClass 就是 android.app.ActivityThread
    invokeStaticMain(args.startClass,args.startArgs,classLoader);
}

...

private static void invokeStaticMain(String className,String[] argv,ClassLoader classLoader){
    Class<?> cl;
    try{
        //获得android.app.ActivityThread类
        cl = Class.forName(className,true,classLoader);
    }catch(ClassNotFoundException ex){
        ...
    }
    Method m;
    try{
        //获取ActivityThread的main方法
        m = cl.getMethod("main",new Class[]{String[].class});
    }catch(NoSuchMethodException ex){
        ...
    }
    ...
    //又来这套...
    throw new Zygote.MethodAndArgsCaller(m,argv);
}

在invokeStaticMain方法中,通过反射的方式找到了ActivityThread类以及它的main方法。在最后抛出异常:throw new Zygote.MethodAndArgsCaller(m,arg),该异常会被Zygote的main方法捕获,至于这里为什么采用了抛出异常的方式而不是直接调用ActivityThread的main方法,原理和Zygote处理SystemServer进程是一毛一样。这种抛出异常的处理会清除所有的设置过程需要的堆栈帧,并让ActivityThread的main方法看起来像是应用程序进程的入口方法(其实不是,在main方法调用前已经做过很多的事情了,比如开启Binder线程池等等)。

既然启动SystemServer以及应用程序进程都用到了这种“奇葩”方式,我们来补齐这部分的代码知识。

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

public static void main(String argv[]){
    try{
        ...
        closeServerSocket();
    }catch(MethodAndArgsCaller caller){
        //在这里捕获异常,并且调用MethodAndArgsCaller的run方法
        caller.run();
    }catch(RuntimeException ex){
        ...
    }
}

MethodAndArgsCaller是Zygote的静态内部类。
frameworks/base/core/java/com/android/internal/os/Zygote.java:

public static class MethodAndArgsCaller extends Exception implements Runnable{
    private final Method mMethod;
    private final String[] mArgs;
    public MethodAndArgsCaller(Method method,String[] args){
        mMethod=method;
        mArgs=args;
    }
    
    //Runnable的run
    public void run(){
        try{
            //最终反射调用main方法,因为是静态方法,所以第一个参数是null
            mMethod.invoke(null,new Object[]{mArgs});
        }catch(IllegalAccessException ex){
            ...
        }
    }
}

完事,最终调用ActivityThread的main方法。

三:Binder线程池启动

刚刚提到过,在ZygoteInit中,初始化应用程序进程之前,会调用nativeZygoteInit方法来初始化Binder线程池。很明显,nativeZygoteInit从名字上来看就是一个JNI方法。

private static final native void nativeZygoteInit();

它所对应的函数是com_android_internal_os_ZygoteInit_nativeZygoteInit,如下。
frameworks/base/core/jni/AndroidRuntime.cpp:

const JNINativeMethod method[]={
    { "nativeZygoteInit", "()V", (void*) com_android_internal_os_ZygoteInit_nativeZygoteInit }  ,
};

...

static void com_android_internal_os_ZygoteInit_nativeZygoteInit(JNIEnv* env,jobject clazz){
    //这个gCurRuntime就是AndroidRuntime类型的指针
    gCurRuntime->onZygoteInit();
}

...
static AndoridRuntime* gCurRuntime = NULL;
...
AndroidRuntime:AndroidRuntime(char* argBlockStart,const size_t argBlockLength):mExitWithoutCleanup(false),mArgBlockStart(argBlockStart),mArgBlockLength(argBlockLength){
    ...
    gCurRuntime = this;
}

AppRuntime 继承自 AndroidRuntime,AppRuntime在创建时就会调用AndroidRuntime的构造函数,gCurRuntime就会被初始化,它指向的是AppRuntime。接下来看看onZygoteInit函数,AppRuntime在app_main.cpp中的实现。
frameworks/base/cmds/app_process/app_main.cpp:

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

最后调用了ProcessState的startThreadPoll函数来启动线程池。
frameworks/native/libs/binder/ProcessState.cpp:

AutoMutex _l(mLock);
//确保Binder线程池只会被启动一次
if(!mThreadPollStarted){
    mThreadPollStarted = true;
    //创建线程池中的第一个线程,也就是该线程池的主线程
    spawnPolledThread(true);
}

...

void ProcessState::spawnPooledThread(bool isMain){
    if(mThreadPollStarted){
        String8 name = makeBinderThreadName();
        ...
        sp<Thread> t = new PollThread(isMain);
        //调用run方法来启动新线程
        t->run(name.string())
    }
}

...

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

在最后,调用了IPCThreadState的joinThreadPool函数,将当前线程注册到BInder驱动程序中,这样我们创建的线程就加入了Binder线程池中,新创建的应用程序进程就支持Binder间的通信了。我们只需要创建当前进程的Binder对象,并将它注册到ServiceManager中就可以实现Binder进程间通信。

四:消息循环创建

在Zygote创建完毕应用程序进程,会通过抛异常的方式启动ActivityThread的main方式,而后ActivityThread在main方法中,会开启消息循环,也就是Looper。

fragmeworks/base/core/java/android/app/ActivityThread.java

public static void main(String[] args){
    //创建主线程Looper
    Looper.prepareMainLooper();
    
    ...
    
    //Looper开始工作
    Looper.loop();
}

这也就是为什么,我们在主线程中,可以不用调用Looper的prepare方法,但是仍然可以使用Handler的原因了,ActivityThread已经帮我们在主线程做了这些事情。

对于Handler、Looper、MessageQueue的关系,感兴趣的童鞋可以参考:传送门

结尾

整个Android应用进程启动过程介绍完毕,内容不多但十分重要。开发人员了解自己所开发的应用的进程是如何创建的是十分必要的。

本文大量参考《Android 进阶解密》一书,同时建议对Android底层有兴趣的童鞋搞一本看看,里面讲解的必然比本文精辟不少。

本文纯手打,欢迎各位点亮爱心,给个小小的赞以资鼓励,谢谢

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