小夜曲:曉說ActivityThread

該篇是Android啟動系列的最后一篇站绪,本文從應(yīng)用進程的創(chuàng)建啟動角度出發(fā)候引,分析ActivityThread是如何創(chuàng)建的。相對于上篇勘天,本文內(nèi)容較少媚狰,閱讀無壓力~

從接觸Android的那一刻開始型檀,看到最多的是:耗時任務(wù)不能在主線程執(zhí)行憎妙,UI操作要在主線程執(zhí)行……當(dāng)時人傻無知,從來沒有想過什么是主線程口锭,為啥不能在主線程做耗時任務(wù)等等朦前。
大家都說ActivityThread是主線程,那它是線程嗎鹃操?它為什么被稱為主線程韭寸?它做啥了?

回顧

在上文《沉思曲:Activity啟動》一文中荆隘,分析了Activity的啟動過程恩伺。并在調(diào)用startSpecificActivityLocked方法時,提到這里會判斷目的Activity進程是否存在椰拒,如果不存在晶渠,就會創(chuàng)建新進程,本文就從這個創(chuàng)建新進程的分支開始燃观。

創(chuàng)建應(yīng)用進程


先回顧下startSpecificActivityLocked方法

該方法在ActivityStackSupervisor.java中

void startSpecificActivityLocked(ActivityRecord r,
            boolean andResume, boolean checkConfig) {
        //獲取待啟動Activity的進程信息
        ProcessRecord app = mService.getProcessRecordLocked(r.processName,
                r.info.applicationInfo.uid, true);

        r.task.stack.setLaunchTime(r);

        //待啟動Activity所在進程存在
        if (app != null && app.thread != null) {
            try {
                app.addPackage(r.info.packageName, mService.mProcessStats);
                //接著往下執(zhí)行
                realStartActivityLocked(r, app, andResume, checkConfig);
                return;
            } catch (RemoteException e) {
                Slog.w(TAG, "Exception when starting activity "
                        + r.intent.getComponent().flattenToShortString(), e);
            }
        }

        //待啟動Activity所在進程不存在褒脯,為其創(chuàng)建新進程
        mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
                "activity", r.intent.getComponent(), false, false, true);
    }
  • 上方法中,如果目的Activity所在進程不存在缆毁,調(diào)用AMS的startProcessLocked方法創(chuàng)建目的應(yīng)用進程番川。

1.1、startProcessLocked

startProcessLocked定義在ActivityManagerService.java中脊框,該方法會調(diào)用Process的start方法颁督。

// Start the process.  It will either succeed and return a result containing
// the PID of the new process, or else throw a RuntimeException.
Process.ProcessStartResult startResult = Process.start("android.app.ActivityThread",
                    app.processName, uid, uid, gids, debugFlags, mountExternal,
                    app.info.targetSdkVersion, app.info.seinfo, null);
  • 該方法調(diào)用Process的start方法,注意傳入?yún)?shù):<u>"android.app.ActivityThread"</u>

1.2浇雹、 start | Process.java

/**
 * Start a new process.
 * 
 * <p>If processes are enabled, a new process is created and the
 * static main() function of a <var>processClass</var> is executed there.
 * The process will continue running after this function returns.
 * 
 * <p>If processes are not enabled, a new thread in the caller's
 * process is created and main() of <var>processClass</var> called there.
 * 
 * <p>The niceName parameter, if not an empty string, is a custom name to
 * give to the process instead of using processClass.  This allows you to
 * make easily identifyable processes even if you are using the same base
 * <var>processClass</var> to start them.
 * 
 * @param processClass The class to use as the process's main entry
 *                     point.
 * @param niceName A more readable name to use for the process.
 * @param uid The user-id under which the process will run.
 * @param gid The group-id under which the process will run.
 * @param gids Additional group-ids associated with the process.
 * @param debugFlags Additional flags.
 * @param targetSdkVersion The target SDK version for the app.
 * @param seInfo null-ok SE Android information for the new process.
 * @param zygoteArgs Additional arguments to supply to the zygote process.
 * 
 * @return An object that describes the result of the attempt to start the process.
 * @throws RuntimeException on fatal start failure
 * 
 * {@hide}
 */
public static final ProcessStartResult start(final String processClass,
                              final String niceName,
                              int uid, int gid, int[] gids,
                              int debugFlags, int mountExternal,
                              int targetSdkVersion,
                              String seInfo,
                              String[] zygoteArgs) {
    try {
        //通過Zygote啟動新進程
        return startViaZygote(processClass, niceName, uid, gid, gids,
                debugFlags, mountExternal, targetSdkVersion, seInfo, zygoteArgs);
    } catch (ZygoteStartFailedEx ex) {
        Log.e(LOG_TAG,
                "Starting VM process through Zygote failed");
        throw new RuntimeException(
                "Starting VM process through Zygote failed", ex);
    }
}

從注釋可知沉御,該方法用來啟動一個新進程:

  • 如果進程啟動,則processClass(此處入?yún)椤癮ndroid.app.ActivityThread”)會運行在該新進程中昭灵,并且在該方法調(diào)用完成后嚷节,新進程會繼續(xù)運行。
  • 如果進程未啟動虎锚,則調(diào)用該start方法所在的進程會創(chuàng)建一個新的線程,然后processClass運行在該新線程中衩婚。

方法調(diào)用startViaZygote方法窜护,從方法名看出,進程創(chuàng)建似乎是要交給zygote非春。

1.3柱徙、startViaZygote | Process.java

private static 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[] extraArgs)
                              throws ZygoteStartFailedEx {
    synchronized(Process.class) {
        //啟動新進程的參數(shù)
        ArrayList<String> argsForZygote = new ArrayList<String>();

        //添加各種參數(shù)到argsForZygote
        argsForZygote.add("--runtime-init");
        argsForZygote.add("--setuid=" + uid);
        argsForZygote.add("--setgid=" + gid);
        ...

        if (niceName != null) {
            argsForZygote.add("--nice-name=" + niceName);
        }

        if (seInfo != null) {
            argsForZygote.add("--seinfo=" + seInfo);
        }
        argsForZygote.add(processClass);
        
        ...

        //調(diào)用該方法缓屠,由zygote創(chuàng)建進程,并獲取新進程信息
        return zygoteSendArgsAndGetResult(argsForZygote);
    }
}

這個方法設(shè)置啟動新進程所需參數(shù)护侮,并往下調(diào)用zygoteSendArgsAndGetResult方法敌完。

1.4、zygoteSendArgsAndGetResult | Process.java

private static ProcessStartResult zygoteSendArgsAndGetResult(ArrayList<String> args)
        throws ZygoteStartFailedEx {
    //進程通信羊初,連接到Zygote的Server Socket滨溉,交由Zygote創(chuàng)建
    openZygoteSocketIfNeeded();

    try {
        //sZygoteWriter將參數(shù)發(fā)送到Server Socket
        sZygoteWriter.write(Integer.toString(args.size()));
        sZygoteWriter.newLine();

        int sz = args.size();
        for (int i = 0; i < sz; i++) {
            String arg = args.get(i);
            if (arg.indexOf('\n') >= 0) {
                throw new ZygoteStartFailedEx(
                        "embedded newlines not allowed");
            }
            sZygoteWriter.write(arg);
            sZygoteWriter.newLine();
        }

        sZygoteWriter.flush();

        //sZygoteInputStream從Server Socket讀取結(jié)果
        ProcessStartResult result = new ProcessStartResult();
        result.pid = sZygoteInputStream.readInt();
        ...
        result.usingWrapper = sZygoteInputStream.readBoolean();
        return result;
    ...
}

從方法名可以猜出該方法大致干啥了:發(fā)送啟動參數(shù)給Zygote進程,交由Zygote進程啟動长赞,并獲得啟動結(jié)果晦攒。看下是否如此:
1得哆、調(diào)用openZygoteSocketIfNeeded方法脯颜。
2、調(diào)用sZygoteWriter往ServerSocket寫創(chuàng)建進程所需要的參數(shù)贩据,ServerSocket是啥栋操?
3、創(chuàng)建ProcessStartResult饱亮,并寫入從ServerSocket讀取的數(shù)據(jù)矾芙,作為結(jié)果返回。
看來重點在openZygoteSocketIfNeeded方法近尚!

1.5蠕啄、openZygoteSocketIfNeeded | Process.java

private static void openZygoteSocketIfNeeded() 
        throws ZygoteStartFailedEx {

    //重連次數(shù)
    int retryCount;

    if (sPreviousZygoteOpenFailed) {
        retryCount = 0;
    } else {
        retryCount = 10;            
    }

    for (int retry = 0
            ; (sZygoteSocket == null) && (retry < (retryCount + 1))
            ; retry++ ) {
        ...

        try {
            //client socket
            sZygoteSocket = new LocalSocket();
            //連接到Zygote的server socket
            sZygoteSocket.connect(new LocalSocketAddress(ZYGOTE_SOCKET, 
                    LocalSocketAddress.Namespace.RESERVED));
            //sZygoteInputStream負責(zé)讀取server socket信息
            sZygoteInputStream
                    = new DataInputStream(sZygoteSocket.getInputStream());
            //sZygoteWriter發(fā)送信息到server socket
            sZygoteWriter =
                new BufferedWriter(
                        new OutputStreamWriter(
                                sZygoteSocket.getOutputStream()),
                        256);
            sPreviousZygoteOpenFailed = false;
            break;
        ...
}

詳細大家都能一眼看懂,這不是Java中的Socket的連接嗎戈锻,這不過這里是用來進程連接歼跟。
1、創(chuàng)建Client Socket:sZygoteSocket
2格遭、ClientSocket連接到服務(wù)端哈街,ServerSocket:ZYGOTE_SOCKET
3、創(chuàng)建輸入流負責(zé)從ServerSocket讀數(shù)據(jù)拒迅,創(chuàng)建輸出流向ServerSocket寫數(shù)據(jù)骚秦。

即:

  • openZygoteSocketIfNeeded方法負責(zé)連接到Zygote的Server Socket,并發(fā)送/讀取Server Socket的信息璧微。
  • ServerSocket即ZYGOTE_SOCKET作箍,它有Zygote進程創(chuàng)建。

既然client socket連接到Zygote的創(chuàng)建的ServerSocket了前硫,那接下來看下Zygote是如何處理連接的胞得。

Zygote處理客戶端連接


Zygote進程由init進程啟動,Zygote的實現(xiàn)在ZygoteInit.java中屹电,其main方法調(diào)用標志著Zygote的啟動阶剑。

2.1跃巡、main | ZygoteInit.java

public static void main(String argv[]) {
   try {
        //注冊server socket
        //其它進程的創(chuàng)建,通過連接到該Socket后,由zygote孵化出來
        registerZygoteSocket();
        
        ...
        //預(yù)加載android framework類和資源
        preload();
        
        ...
        if (argv[1].equals("start-system-server")) {
            //啟動SystemServer
            startSystemServer();
        } else if (!argv[1].equals("")) {
            throw new RuntimeException(argv[0] + USAGE_STRING);
        }

        //死循環(huán)接收client socket連接牧愁,由此創(chuàng)建新進程
        runSelectLoop();

        closeServerSocket();
    } catch (MethodAndArgsCaller caller) {  //標注加紅素邪,這個異常捕獲是重點!V戆搿兔朦!
        caller.run();
    } catch (RuntimeException ex) {
        closeServerSocket();
        throw ex;
    }
}
  • Zygote進程啟動時,注冊了ServerSocket办龄,即上面提到的ZYGOTE_SOCKET烘绽,就是它來處理client socket的連接。
  • Zygote進程啟動時還啟動了SystemServer俐填,這在第一篇文章中已經(jīng)分析過了安接。
  • 最后調(diào)用runSelectLoop方法,死循環(huán)處理client socket連接英融。

最后需要注意的是:
main方法采用了try-catch的方式盏檐,其中捕獲了異常MethodAndArgsCaller。由后面代碼可知驶悟,通過這種拋異常的方式胡野,使進程跳出死循環(huán),并執(zhí)行caller.run方法痕鳍。

2.2硫豆、runSelectLoop | ZygoteInit.java

上面方法調(diào)用runSelectLoop,死循環(huán)處理client連接笼呆,看下其內(nèi)容熊响。

private static void runSelectLoop() throws MethodAndArgsCaller {
    ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
    //client socket連接列表
    ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
    
    FileDescriptor[] fdArray = new FileDescriptor[4];
    //將server socket添加到fds中
    fds.add(sServerSocket.getFileDescriptor());
    peers.add(null);
    int loopCount = GC_LOOP_COUNT;
    
    while (true) {
        int index;
        ...

        try {
            //將fds轉(zhuǎn)換成數(shù)組
            fdArray = fds.toArray(fdArray);
            //在fdArray中找到可讀的連接
            index = selectReadable(fdArray);
        } catch (IOException ex) {
            throw new RuntimeException("Error in select()", ex);
        }

        if (index < 0) {
            throw new RuntimeException("Error in select()");
        } else if (index == 0) {
            //第一個為Server socket,此時等待client socket連接
            ZygoteConnection newPeer = acceptCommandPeer();
            peers.add(newPeer);
            fds.add(newPeer.getFileDesciptor());
        } else {
            boolean done;
            //處理連接
            done = peers.get(index).runOnce();
            //處理完連接诗赌,從列表中移除
            if (done) {
                peers.remove(index);
                fds.remove(index);
            }
        }
    }
}

代碼邏輯還是比較清楚:

  • Zygote死循環(huán)處理已連接的client socket汗茄,client的連接封裝成ZygoteConnection 表示,并調(diào)用其的runOnce()方法铭若。
  • 如果已連接的client socket都處理完成洪碳,那就等待新的client連接。

2.3叼屠、runOnce | ZygoteConnection.java

看下連接的處理方法runOnce():

boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {
    String args[];
    Arguments parsedArgs = null;
    FileDescriptor[] descriptors;

    try {
        //讀取client socket傳入的參數(shù)列表
        args = readArgumentList();
        descriptors = mSocket.getAncillaryFileDescriptors();
    } 
    ...

    try {
        //轉(zhuǎn)換參數(shù)
        parsedArgs = new Arguments(args);
        ...
        
        //Zygote創(chuàng)建 新進程并負責(zé)虛擬機實例到該進程
        pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
                parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
                parsedArgs.niceName);
    }
    ...

    try {
        //新進程
        if (pid == 0) {
            IoUtils.closeQuietly(serverPipeFd);
            serverPipeFd = null;
            //新進程中調(diào)用
            handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);
            return true;
        }
        ...
}

要劃重點了:
1瞳腌、該方法讀取client socket連接時傳入的參數(shù),并封裝镜雨。
2纯趋、調(diào)用Zygote的forkAndSpecialize創(chuàng)建指定進程。
3、pid=0時即子進程吵冒,在子進程中調(diào)用handleChildProc方法。

這里涉及到以下幾點:

  • 應(yīng)用進程的確是有Zygote進程創(chuàng)建的西剥,包括前文提到的SystemServer進程
  • 新進程時如何與虛擬機關(guān)聯(lián)的痹栖?
    在forkAndSpecialize方法中,Zygote會將創(chuàng)建的Dalvik虛擬機實例復(fù)制到新的應(yīng)用程序進程里面去瞭空,從而使得每一個應(yīng)用程序進程都有一個獨立的Dalvik虛擬機實例揪阿。

好了,新進程已經(jīng)創(chuàng)建好了咆畏,但似乎還沒解決問題南捂,ActivityThread是如何運行在新進程中的呢?接著往下看handleChildProc方法旧找。

2.4溺健、handleChildProc | ZygoteConnection.java

private void handleChildProc(Arguments parsedArgs,
        FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)
        throws ZygoteInit.MethodAndArgsCaller {
  
    //子進程中關(guān)閉client/server socket
    closeSocket();
    ZygoteInit.closeServerSocket();
    ...
    if (parsedArgs.niceName != null) {
        //設(shè)置進程名
        Process.setArgV0(parsedArgs.niceName);
    }

    //是否設(shè)置了runtimeInit參數(shù)
    if (parsedArgs.runtimeInit) {
        if (parsedArgs.invokeWith != null) {
            WrapperInit.execApplication(parsedArgs.invokeWith,
                    parsedArgs.niceName, parsedArgs.targetSdkVersion,
                    pipeFd, parsedArgs.remainingArgs);
        } else {
            //zygoteInit最后仍會創(chuàng)建ActivityThread實例,并調(diào)用其main方法
            RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion,
                    parsedArgs.remainingArgs);
        }
    } else {
        String className;
        try {
            //傳入的類全限定名钮蛛,此處為:“android.app.ActivityThread”
            className = parsedArgs.remainingArgs[0];
        } 
        ...
                
        if (parsedArgs.invokeWith != null) {
            WrapperInit.execStandalone(parsedArgs.invokeWith,
                    parsedArgs.classpath, className, mainArgs);
        } else {
            ClassLoader cloader;
            if (parsedArgs.classpath != null) {
                //ClassLoader
                cloader = new PathClassLoader(parsedArgs.classpath,
                        ClassLoader.getSystemClassLoader());
            } else {
                cloader = ClassLoader.getSystemClassLoader();
            }

            try {
                //反射創(chuàng)建className(此處為ActivityThread)實例鞭缭,并調(diào)用其main方法
                ZygoteInit.invokeStaticMain(cloader, className, mainArgs);
            } 
            ...
        }
    }
}

注意哦,現(xiàn)在已經(jīng)是在新創(chuàng)建的子進程了哦:
1魏颓、子進程關(guān)閉client/server socket連接岭辣,為什么呢?
2甸饱、解析參數(shù)沦童,根據(jù)前面?zhèn)魅氲膮?shù)“android.app.ActivityThread”,調(diào)用ZygoteInit.invokeStaticMain方法叹话,反射創(chuàng)建ActivityThread實例偷遗,并調(diào)用該實例main方法。

回答兩個問題:
1渣刷、子進程為什么要關(guān)閉client/server socket連接鹦肿,關(guān)閉client還好理解,server socket不是在Zygote進程嗎辅柴?
了解Linux的都知道箩溃,通過fork創(chuàng)建的子進程會繼承父進程的代碼、數(shù)據(jù)碌嘀,也就是:子進程除了自己的堆棧地址跟父進程不同外涣旨,其它數(shù)據(jù)都是直接從父進程拷貝過來的。那新創(chuàng)建的子進程股冗,肯定也是有一份ServerSocket實例拷貝霹陡,子進程在創(chuàng)建完成后當(dāng)然需要關(guān)閉。

2、為什么ActivityThread被稱為主線程烹棉?
首先攒霹,ActivityThread不是線程,被稱為主線程是因為:它運行在新進程的主線程中(當(dāng)然了浆洗,主線程就是進程中的第一個線程了催束,此處沒有額外再創(chuàng)建新線程,那肯定是主線程了)伏社。

2.5抠刺、invokeStaticMain | ZygoteInit.java


    static void invokeStaticMain(ClassLoader loader,
            String className, String[] argv)
            throws ZygoteInit.MethodAndArgsCaller {
        Class<?> cl;

        try {
            cl = loader.loadClass(className);
        } 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.
         */
        throw new ZygoteInit.MethodAndArgsCaller(m, argv);
    }

這個方法不寫注釋也能一眼看懂:
1、反射獲得入?yún)?yīng)的Class摘昌,此處入?yún)椋骸癮ndroid.app.ActivityThread”
2速妖、反射獲取main方法對應(yīng)的Method
3、新建MethodAndArgsCaller聪黎,并以異常形式拋出

  • 前面都好理解罕容,關(guān)鍵是為什么要將main方法封裝成異常的形式拋出呢?
    前面提到:子進程實際是從父進程將代碼數(shù)據(jù)拷貝而來挺举,父進程Zygote在死循環(huán)中創(chuàng)建了子進程杀赢,那子進程當(dāng)然也存在這份死循環(huán)的代碼。子進程通過異常拋出的形式湘纵,然后異常被捕獲脂崔,跳出死循環(huán),并在調(diào)用異常的run方法梧喷,在該run方法中調(diào)用被封裝的方法main砌左。

ActivityThread


至此,應(yīng)用進程已經(jīng)創(chuàng)建好了铺敌,并通過反射方式創(chuàng)建了ActivityThread的實例汇歹,然后調(diào)用該實例的main方法。那看下其main方法都做了啥:

3.1偿凭、main | ActivityThread.java

public static void main(String[] args) {
    SamplingProfilerIntegration.start();
    // CloseGuard defaults to true and can be quite spammy.  We
    // disable it here, but selectively enable it later (via
    // StrictMode) on debug builds, but using DropBox, not logs.
    CloseGuard.setEnabled(false);

    Environment.initForCurrentUser();

    // Set the reporter for event logging in libcore
    EventLogger.setReporter(new EventLoggingReporter());

    Security.addProvider(new AndroidKeyStoreProvider());

    Process.setArgV0("<pre-initialized>");
    
    //為當(dāng)前線程創(chuàng)建Looper,并未改Looper創(chuàng)建關(guān)聯(lián)的MessageQueue
    Looper.prepareMainLooper();
    
    //創(chuàng)建實例
    ActivityThread thread = new ActivityThread();
    //入口
    thread.attach(false);
    
    //創(chuàng)建Handler产弹,即:H
    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }

    AsyncTask.init();

    if (false) {
        Looper.myLooper().setMessageLogging(new
                LogPrinter(Log.DEBUG, "ActivityThread"));
    }
    
    //mainLooper循環(huán)從消息隊列中取消息,并交給H處理
    Looper.loop();

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

有代碼知弯囊,main方法創(chuàng)建了主線程關(guān)聯(lián)的Looper已經(jīng)H handler痰哨,這些相信大家都很熟了,但還是廢話幾句吧:

  • main方法在當(dāng)前進程的主線程中運行匾嘱,即主線程創(chuàng)建了Looper斤斧,并為Looper創(chuàng)建了關(guān)聯(lián)的消息隊列,以及handler H霎烙。創(chuàng)建完成后撬讽,調(diào)用Looper的loop方法蕊连,循環(huán)從消息隊列中取出消息交給Looper處理。

    • 關(guān)于Looper游昼、MessageQueue甘苍、Handler以及Message的關(guān)系,大致如下:
      • Looper與線程相關(guān)聯(lián)烘豌,在Looper類中采用線程局部變量保存每個線程關(guān)聯(lián)的Looper羊赵。主線程默認關(guān)聯(lián)一個Looper,即MainLooper扇谣;當(dāng)然也可以為子線程手動創(chuàng)建關(guān)聯(lián)的Looper,即調(diào)用Looper.prepare()方法闲昭。
      • 每個Looper實例中罐寨,存在一個關(guān)聯(lián)的MessageQueue實例,調(diào)用Looper的prepare方法時序矩,會創(chuàng)建Looper實例鸯绿,以及Looper關(guān)聯(lián)的MessageQueue實例。調(diào)用Looper.loop()方法簸淀,Looper會循環(huán)去關(guān)聯(lián)的MessageQueue中取出消息處理瓶蝴。
      • Handler,當(dāng)構(gòu)造Handler的實例時租幕,會獲取當(dāng)前線程關(guān)聯(lián)的Looper對象和MessageQueue對象引用舷手。調(diào)用handler發(fā)送消息時,實際是將消息放入到MessageQueue中劲绪。
      • Message男窟,每個Message對象都關(guān)聯(lián)了一個target,該target就是發(fā)送該消息的Handler實例贾富。當(dāng)Looper從MessageQueue中取出消息時歉眷,會將消息發(fā)送給其關(guān)聯(lián)的target進行處理。(why:因為一個MessageQueue可能被多個handler引用颤枪。)

方法中另外創(chuàng)建了一個ActivityThread的實例汗捡,并調(diào)用其attach方法,開始應(yīng)用的啟動畏纲。

3.2扇住、attach | ActivityThread.java

private void attach(boolean system) {
    sCurrentActivityThread = this;
    mSystemThread = system;
    //普通App進這里
    if (!system) {
        ViewRootImpl.addFirstDrawHandler(new Runnable() {
            @Override
            public void run() {
                ensureJitEnabled();
            }
        });
        android.ddm.DdmHandleAppName.setAppName("<pre-initialized>",
                                                UserHandle.myUserId());
                                                
        //VM中保存該應(yīng)用標志,用于上報錯誤
        //mAppThread是Binder對象霍骄,AMS通過該對象實現(xiàn)與該應(yīng)用進程通信台囱,ActivityThread初始化時會創(chuàng)建該實例
        RuntimeInit.setApplicationObject(mAppThread.asBinder());
        //AMS代理
        IActivityManager mgr = ActivityManagerNative.getDefault();
        try {
            //AMS保存該應(yīng)用進程信息,通過該Binder實現(xiàn)與該應(yīng)用進程通信
            mgr.attachApplication(mAppThread);  //入口
        } catch (RemoteException ex) {
            // Ignore
        }
    } else {
        ......
    }
    ......
}
  • 該方法入?yún)閒alse读整,即普通應(yīng)用app簿训。
  • 將應(yīng)用進程Binder關(guān)聯(lián)到VM,用于上報錯誤信息;
  • 將應(yīng)用進程Binder關(guān)聯(lián)到AMS强品,用于AMS與該應(yīng)用進程通信膘侮;
  • 最后,通過AIDL調(diào)用AMS的attachApplication方法的榛,所以此時又交由AMS來處理了琼了。

什么是應(yīng)用進程Binder?
在上文《沉思曲:Acitivity啟動》中提到夫晌,AMS通過持有應(yīng)用進程的Binder實現(xiàn)與應(yīng)用進程的通信雕薪。該Binder實現(xiàn)了IApplicationThread接口,實現(xiàn)是ApplicaitonThread晓淀。

3.3所袁、attachApplication | ActivityManagerService.java

public final void attachApplication(IApplicationThread thread) {
    synchronized (this) {
        //應(yīng)用進程pid
        int callingPid = Binder.getCallingPid();
        final long origId = Binder.clearCallingIdentity();
        //入口
        attachApplicationLocked(thread, callingPid);
        Binder.restoreCallingIdentity(origId);
    }
}
  • 該方法繼續(xù)調(diào)用attachApplicationLocked方法。
  • attachApplicationLocked方法主要是應(yīng)用進程進行校驗凶掰,并調(diào)用thread的bindApplication方法燥爷。thread就是上一方法中的入?yún)ⅲ簯?yīng)用進程Binder,實現(xiàn)為ApplicaitonThread懦窘,是Binder對象前翎。
  • 由于thread是Binder對象,即應(yīng)用進程在AMS中的Binder對象畅涂,所以此處又由AMS進程切換到應(yīng)用進程中8刍!毅戈!

3.4苹丸、bindApplication | ActivityThread.java

note:ApplicaitonThread是ActivityThread的內(nèi)部類。

public final void bindApplication(String processName,
            ApplicationInfo appInfo, List<ProviderInfo> providers,
            ComponentName instrumentationName, String profileFile,
            ParcelFileDescriptor profileFd, boolean autoStopProfiler,
            Bundle instrumentationArgs, IInstrumentationWatcher instrumentationWatcher,
            IUiAutomationConnection instrumentationUiConnection, int debugMode,
            boolean enableOpenGlTrace, boolean isRestrictedBackupMode, boolean persistent,
            Configuration config, CompatibilityInfo compatInfo, Map<String, IBinder> services,
            Bundle coreSettings) {

        if (services != null) {
            // Setup the service cache in the ServiceManager
            ServiceManager.initServiceCache(services);
        }

        setCoreSettings(coreSettings);

        AppBindData data = new AppBindData();
        data.processName = processName;
        data.appInfo = appInfo;
        data.providers = providers;
        data.instrumentationName = instrumentationName;
        data.instrumentationArgs = instrumentationArgs;
        data.instrumentationWatcher = instrumentationWatcher;
        data.instrumentationUiAutomationConnection = instrumentationUiConnection;
        data.debugMode = debugMode;
        data.enableOpenGlTrace = enableOpenGlTrace;
        data.restrictedBackupMode = isRestrictedBackupMode;
        data.persistent = persistent;
        data.config = config;
        data.compatInfo = compatInfo;
        data.initProfileFile = profileFile;
        data.initProfileFd = profileFd;
        data.initAutoStopProfiler = false;
        
        //發(fā)送消息到H苇经,消息類型為H.BIND_APPLICATION
        queueOrSendMessage(H.BIND_APPLICATION, data);
    }

該方法在應(yīng)用進程中赘理,調(diào)用H handler發(fā)送消息H.BIND_APPLICATION,看下H handler是如何處理該消息的扇单。

3.5商模、 H處理H.BIND_APPLICATION消息 | ActivityThread.java

case BIND_APPLICATION:
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
                AppBindData data = (AppBindData)msg.obj;
                //入口
                handleBindApplication(data);
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                break;
  • 該方法接著調(diào)用 handleBindApplication

3.6、 handleBindApplication | ActivityThread.java

    private void handleBindApplication(AppBindData data) {
        mBoundApplication = data;
        mConfiguration = new Configuration(data.config);
        mCompatConfiguration = new Configuration(data.config);

        mProfiler = new Profiler();
        mProfiler.profileFile = data.initProfileFile;
        mProfiler.profileFd = data.initProfileFd;
        mProfiler.autoStopProfiler = data.initAutoStopProfiler;

        //設(shè)置進程名
        Process.setArgV0(data.processName);
        android.ddm.DdmHandleAppName.setAppName(data.processName,
                                                UserHandle.myUserId());
        //是否開啟硬件加速
        if (data.persistent) {
            if (!ActivityManager.isHighEndGfx()) {
                HardwareRenderer.disable(false);
            }
        }
        
        if (mProfiler.profileFd != null) {
            mProfiler.startProfiling();
        }

        //Honeycomb MR1版本前蜘澜,設(shè)置AsyncTask的底層由線程池實現(xiàn)
        if (data.appInfo.targetSdkVersion <= android.os.Build.VERSION_CODES.HONEYCOMB_MR1) {
            AsyncTask.setDefaultExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
        }

        //設(shè)置TimeZone施流、Locale
        TimeZone.setDefault(null);
        Locale.setDefault(data.config.locale);

        //更新系統(tǒng)設(shè)置,AppBindData中配置默認是最新的
        mResourcesManager.applyConfigurationToResourcesLocked(data.config, data.compatInfo);
        mCurDefaultDisplayDpi = data.config.densityDpi;
        applyCompatConfiguration(mCurDefaultDisplayDpi);
        
        //獲取Package信息鄙信,返回LoadedApk對象瞪醋。先從緩存讀,沒有則創(chuàng)建装诡。
        data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);

        //設(shè)置分辨率信息
        if ((data.appInfo.flags&ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES)
                == 0) {
            mDensityCompatMode = true;
            Bitmap.setDefaultDensity(DisplayMetrics.DENSITY_DEFAULT);
        }
        updateDefaultDensity();
        
        //創(chuàng)建ContextImpl實例
        final ContextImpl appContext = new ContextImpl();
        appContext.init(data.info, null, this);
      
        if (!Process.isIsolated()) {
            final File cacheDir = appContext.getCacheDir();
            if (cacheDir != null) {
                // Provide a usable directory for temporary files
                System.setProperty("java.io.tmpdir", cacheDir.getAbsolutePath());
                setupGraphicsSupport(data.info, cacheDir);
            } else {
                Log.e(TAG, "Unable to setupGraphicsSupport due to missing cache directory");
            }
        }

        //系統(tǒng)應(yīng)用银受,開啟debug log
        if ((data.appInfo.flags &
             (ApplicationInfo.FLAG_SYSTEM |
              ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) != 0) {
            StrictMode.conditionallyEnableDebugLogging();
        }

        //Honeycomb版本以后践盼,不允許在主線程進行網(wǎng)絡(luò)操作
        if (data.appInfo.targetSdkVersion > 9) {
            StrictMode.enableDeathOnNetwork();
        }

        //設(shè)置debug
        ......

        // 是否啟動GL
        if (data.enableOpenGlTrace) {
            GLUtils.setTracingLevel(1);
        }

        // Allow application-generated systrace messages if we're debuggable.
        boolean appTracingAllowed = (data.appInfo.flags&ApplicationInfo.FLAG_DEBUGGABLE) != 0;
        Trace.setAppTracingAllowed(appTracingAllowed);

        //設(shè)置time zone時,走http代理宾巍;通過ConnectivityManager設(shè)置
        IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
        if (b != null) {
            IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
            try {
                ProxyProperties proxyProperties = service.getProxy();
                Proxy.setHttpProxySystemProperty(proxyProperties);
            } catch (RemoteException e) {}
        }
        
        //是否指定trumentation類名咕幻,難道可以指定?顶霞!
        if (data.instrumentationName != null) {
            InstrumentationInfo ii = null;
            try {
                //獲取Instrumentation信息
                ii = appContext.getPackageManager().
                    getInstrumentationInfo(data.instrumentationName, 0);
            }
            ......

            mInstrumentationAppDir = ii.sourceDir;
            mInstrumentationAppLibraryDir = ii.nativeLibraryDir;
            mInstrumentationAppPackage = ii.packageName;
            mInstrumentedAppDir = data.info.getAppDir();
            mInstrumentedAppLibraryDir = data.info.getLibDir();
            
            //創(chuàng)建ApplicationInfo肄程,用來保存應(yīng)用信息
            ApplicationInfo instrApp = new ApplicationInfo();
            instrApp.packageName = ii.packageName;
            instrApp.sourceDir = ii.sourceDir;
            instrApp.publicSourceDir = ii.publicSourceDir;
            instrApp.dataDir = ii.dataDir;
            instrApp.nativeLibraryDir = ii.nativeLibraryDir;
            
            //創(chuàng)建LoadedApk實例,保存應(yīng)用APK信息
            LoadedApk pi = getPackageInfo(instrApp, data.compatInfo,
                    appContext.getClassLoader(), false, true);
            //創(chuàng)建應(yīng)用關(guān)聯(lián)的CotextImpl
            ContextImpl instrContext = new ContextImpl();
            instrContext.init(pi, null, this);

            try {
                java.lang.ClassLoader cl = instrContext.getClassLoader();
                //為應(yīng)用創(chuàng)建指定的Instrumentation實例
                mInstrumentation = (Instrumentation)
                    cl.loadClass(data.instrumentationName.getClassName()).newInstance();
            }
            ......
            //init选浑,保存應(yīng)用信息到Instrumentation
            mInstrumentation.init(this, instrContext, appContext,
                   new ComponentName(ii.packageName, ii.name), data.instrumentationWatcher,
                   data.instrumentationUiAutomationConnection);
            ......
        } else {
            //未指定Instrmentation名稱蓝厌,創(chuàng)建默認Instrumentation
            mInstrumentation = new Instrumentation();
        }

        if ((data.appInfo.flags&ApplicationInfo.FLAG_LARGE_HEAP) != 0) {
            dalvik.system.VMRuntime.getRuntime().clearGrowthLimit();
        }

        //允許application和provider啟動時訪問disk
        final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskWrites();
        try {
            //創(chuàng)建Application實例
            Application app = data.info.makeApplication(data.restrictedBackupMode, null);
            mInitialApplication = app;

            //restricted模式下,c處理provider
            if (!data.restrictedBackupMode) {
                List<ProviderInfo> providers = data.providers;
                if (providers != null) {
                    installContentProviders(app, providers);
                    mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
                }
            }

            try {
                //調(diào)用Instrumentation的onCreate方法古徒,用于Applicaiton啟動onCreate()啟動前的操作
                mInstrumentation.onCreate(data.instrumentationArgs);
            }
            ......  
          
            try {
                //Instrumentation回調(diào)應(yīng)用Applicaiton的onCreate褂始,啟動應(yīng)用
                mInstrumentation.callApplicationOnCreate(app);
            }
            ......
    }

該方法負責(zé)啟動應(yīng)用:

  • 設(shè)置進程名、是否開啟硬件加速
  • 設(shè)置AsyncTask的底層實現(xiàn)方式
  • 設(shè)置TimeZone描函、Locale
  • 設(shè)置分辨率、不允許主線程網(wǎng)絡(luò)操作等
  • 為應(yīng)用進程設(shè)置Instrumentation狐粱,用于管理應(yīng)用進程
  • 通過Instrumentation調(diào)用應(yīng)用Applicationde 的onCreate方法舀寓,實現(xiàn)應(yīng)用的啟動等
    • 設(shè)置timezone、openGl等信息后肌蜻,創(chuàng)建Instrumentation用于管理該應(yīng)用進程互墓,最后并回調(diào)該應(yīng)用的Application的onCreate方法啟動應(yīng)用。

至此蒋搜,本文內(nèi)容就差不多結(jié)束了篡撵,后面應(yīng)用的啟動就不講了,感興趣的可以看源碼豆挽,大同小異。

總結(jié)


三篇文章到此結(jié)束了膛檀,三文粗略的分析了Android系統(tǒng)啟動娘侍、Activity啟動咖刃、ActivityThread創(chuàng)建三部分內(nèi)容。內(nèi)容都是整理之前資料得到的嚎杨,網(wǎng)上關(guān)于這部分內(nèi)容的資料也很多氧腰,但都比較零散刨肃。這里把這部分內(nèi)容整理出來,一來備忘自脯,二來拋磚引玉。本人才疏學(xué)淺膏潮,大部分內(nèi)容都是自己理解加注釋,有不當(dāng)錯誤之處轻纪,懇請指正~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末刻帚,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子崇众,更是在濱河造成了極大的恐慌航厚,老刑警劉巖幔睬,帶你破解...
    沈念sama閱讀 219,427評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異赦抖,居然都是意外死亡辅肾,警方通過查閱死者的電腦和手機矫钓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來盈电,“玉大人杯活,你說我怎么就攤上這事∥兀” “怎么了?”我有些...
    開封第一講書人閱讀 165,747評論 0 356
  • 文/不壞的土叔 我叫張陵颜矿,是天一觀的道長嫉晶。 經(jīng)常有香客問我替废,道長,這世上最難降的妖魔是什么椎镣? 我笑而不...
    開封第一講書人閱讀 58,939評論 1 295
  • 正文 為了忘掉前任状答,我火速辦了婚禮,結(jié)果婚禮上教沾,老公的妹妹穿的比我還像新娘译断。我一直安慰自己孙咪,他們只是感情好巡语,可當(dāng)我...
    茶點故事閱讀 67,955評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著荤堪,像睡著了一般枢赔。 火紅的嫁衣襯著肌膚如雪踏拜。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,737評論 1 305
  • 那天肮塞,我揣著相機與錄音,去河邊找鬼猜欺。 笑死拷窜,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的副瀑。 我是一名探鬼主播恋谭,決...
    沈念sama閱讀 40,448評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼疚颊,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了均抽?” 一聲冷哼從身側(cè)響起其掂,我...
    開封第一講書人閱讀 39,352評論 0 276
  • 序言:老撾萬榮一對情侶失蹤款熬,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后惋鹅,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體殉簸,經(jīng)...
    沈念sama閱讀 45,834評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡般卑,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,992評論 3 338
  • 正文 我和宋清朗相戀三年蝠检,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蝇率。...
    茶點故事閱讀 40,133評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖侧漓,靈堂內(nèi)的尸體忽然破棺而出监氢,到底是詐尸還是另有隱情,我是刑警寧澤纵揍,帶...
    沈念sama閱讀 35,815評論 5 346
  • 正文 年R本政府宣布泽谨,位于F島的核電站特漩,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏雄卷。R本人自食惡果不足惜蛤售,卻給世界環(huán)境...
    茶點故事閱讀 41,477評論 3 331
  • 文/蒙蒙 一悴能、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦佑女、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽紊选。三九已至道逗,卻和暖如春滓窍,著一層夾襖步出監(jiān)牢的瞬間巩那,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評論 1 272
  • 我被黑心中介騙來泰國打工噪生, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留东囚,地道東北人舔庶。 一個月前我還...
    沈念sama閱讀 48,398評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像瞧甩,于是被迫代替她去往敵國和親弥鹦。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,077評論 2 355

推薦閱讀更多精彩內(nèi)容