startService源碼主要流程解析

本文基于的是Android 8.0源碼。

1炫掐、Activity中調(diào)用startService

我們啟動service的時候都是直接在Activity里面調(diào)用startService方法崭歧,這里實際上調(diào)用的是ContextImpl#startService方法。

我們知道秤茅,Activity繼承自ContextThemeWrappe彻秆,ContextThemeWrapper又繼承自ContextWrapper,ContextWrapper只是Context的靜態(tài)代理蛇耀,Context的實現(xiàn)類就是ContextImpl辩诞。在創(chuàng)建Activity之后,系統(tǒng)會調(diào)用Activity#attach方法纺涤,將創(chuàng)建好的ContextImpl實例對象設(shè)置給Activity译暂,所以在Activity中調(diào)用的Context對象的方法基本上都會走ContextImpl中的實現(xiàn)。

2撩炊、ContextImpl#startService

ContextImpl#startService 
--> ContextImpl#startServiceCommon
--> ActivityManagerService#startService(通過AMS的Binder對象調(diào)用)

3外永、ActivityManagerService#startService

我們接著看ActivityManagerService#startService方法,

ActivityManagerService#startService
--> ActiveService#startServiceLocked(根據(jù)當前服務(wù)對象生成一個ServiceRecord.StartItem對象拧咳,添加進pendingStarts中伯顶,用于后面的onStartCommand方法調(diào)用)
--> ActiveService#startServiceInnerLocked
--> ActiveService#bringUpServiceLocked

這里的ActiveService#bringUpServiceLocked方法是核心,我們看下關(guān)鍵代碼:

這里我們把代碼分為四部分呛踊,注釋中說明了核心邏輯砾淌。

private String bringUpServiceLocked(ServiceRecord r, ...){
    
    // 1
    // 如果條件滿足,說明服務(wù)也已經(jīng)啟動過谭网,因此服務(wù)對應(yīng)的進程已經(jīng)啟動汪厨,該進程對應(yīng)的ActivityThread也已經(jīng)準備好,接下來直接通知應(yīng)用進程調(diào)用service的onStartCommand方法即可愉择。
    if (r.app != null && r.app.thread != null) {
        sendServiceArgsLocked(r, execInFg, false);
        return null;
    }

    // 2
    // 走到這里說明服務(wù)沒有啟動過劫乱,我們先查看服務(wù)所在的進程是否已經(jīng)啟動
    // 如果進程已經(jīng)啟動,并且ActivityThread也已經(jīng)準備好锥涕,則啟動服務(wù)衷戈。
    ProcessRecord app = mAm.getProcessRecordLocked(procName,...);
    if (app != null && app.thread != null) {
       realStartServiceLocked(r, app, execInFg);
    }

    // 3
    // 如果服務(wù)所在的進程沒啟動,則啟動進程
    if (app == null) {
        app=mAm.startProcessLocked(procName,...)
    }

    // 4 將服務(wù)添加進mPendingServices列表中层坠,等待應(yīng)用啟動之后再啟動服務(wù)
    if (!mPendingServices.contains(r)) {
        mPendingServices.add(r);
    }
    
    return null;
}

上面的流程可以用下面這個圖概括:

image

接下來我們講細節(jié)殖妇,如何啟動進程,以及進程啟動后如何啟動服務(wù)破花。

4谦趣、啟動進程,執(zhí)行mPendingServices中的服務(wù)

Zygote啟動進程

ActiveService#bringUpServiceLocked方法中座每,啟動進程的代碼如下:

app=mAm.startProcessLocked(procName,...)

接著往下看:

ActivityManagerService#startProcessLocked
--> ActivityManagerService#startProcessLocked(重載方法)
--> ActivityManagerService#startProcessLocked(重載方法)
--> Process.start
--> ZygoteProcess.start
--> ZygoteProcess.startViaZygote

我們看下ZygoteProcess.startViaZygote方法前鹅,代碼如下:

private Process.ProcessStartResult startViaZygote(final String processClass,...){
    ArrayList<String> argsForZygote = new ArrayList<String>();
    ...
    return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
}

zygoteSendArgsAndGetResult方法的主要作用就是將傳入的應(yīng)用進程的啟動參數(shù)argsForZygote寫入ZygoteState中,ZygoteState是ZygoteProcess的靜態(tài)內(nèi)部類峭梳,用于表示與Zygote進程通信的狀態(tài)舰绘。

再看下openZygoteSocketIfNeeded方法,該方法內(nèi)部會調(diào)用ZygoteState.connect(mSocket)方法,與Zygote進程建立Socket連接捂寿。

Zygote進程的Socket——接收創(chuàng)建進程的消息口四,fork創(chuàng)建子進程

我們接下來看下Zygote進程里面啟動的Socket,我們先看ZygoteInit#main函數(shù):
這里我們只看跟Socket相關(guān)的

public static void main(String argv[]) {
    ZygoteServer zygoteServer = new ZygoteServer();
    String socketName = "zygote";
    
    // 啟動名稱為zygote的Socket
    zygoteServer.registerServerSocket(socketName);

    // 開啟while循環(huán)者蠕,接收Socket消息并處理
    zygoteServer.runSelectLoop(abiList);

    zygoteServer.closeServerSocket();
}

Socket接收消息的邏輯在ZygoteServer.runSelectLoop方法中窃祝,接收到消息后會調(diào)用ZygoteConnection#runOnce方法掐松,我們還是看關(guān)鍵代碼:

boolean runOnce(ZygoteServer zygoteServer) throws Zygote.MethodAndArgsCaller {
    Arguments parsedArgs = null;
    
    pid = Zygote.forkAndSpecialize(parsedArgs.uid,...);
   
    if (pid == 0) {
        // in child
        handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);
        return true;
    } else {
       ...
    }
}

先調(diào)用Zygote#forkAndSpecialize方法啟動進程踱侣,fork方法會返回兩次,pid為0表示是我們關(guān)心的子進程大磺,然后調(diào)用Zygote#handleChildProc方法進行處理抡句,Zygote#handleChildProc方法里面會調(diào)用ZygoteInit.zygoteInit方法,它的關(guān)鍵代碼如下:

public static final void zygoteInit(int targetSdkVersion,...){
    RuntimeInit.commonInit();
    // 給應(yīng)用進程創(chuàng)建Binder線程池
    ZygoteInit.nativeZygoteInit();
    // 調(diào)用進程的ActivityThread#main方法
    RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
}

RuntimeInit#applicationInit方法會調(diào)用RuntimeInit#invokeStaticMain方法杠愧,具體代碼如下:

/**
 * Invokes a static "main(argv[]) method on class "className".
 * Converts various failing exceptions into RuntimeExceptions, with
 * the assumption that they will then cause the VM instance to exit.
 *
 * @param className Fully-qualified class name
 * @param argv Argument vector for main()
 * @param classLoader the classLoader to load {@className} with
 */
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);
    }

    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 Zygote.MethodAndArgsCaller(m, argv);
}

RuntimeInit#invokeStaticMain方法的作用很清晰待榔,就是調(diào)用其入?yún)lassName的main(argv[])方法,這里的className的值就是android.app.ActivityThread流济。

ActivityThread#main——通知AMS進程創(chuàng)建完成锐锣,初始化Application,

Zygote在啟動我們的應(yīng)用進程后绳瘟,會調(diào)用進程的入口函數(shù)android.app.ActivityThread#main雕憔。接下來我們看下ActivityThread#main是如何啟動服務(wù)的。

ActivityThread#main
--> ActivityThread#attach
--> ActivityManagerService#attachApplication(mAppThread) 

ActivityManagerService#attachApplication方法會將應(yīng)用進程的ActivityThread的Binder對象上報給AMS糖声,這樣AMS和應(yīng)用進程就可以開始雙向調(diào)用了斤彼。接著往下看:

ActivityManagerService#attachApplication(mAppThread)
--> ActivityThread#bindApplication(通過Binder調(diào)用ActivityThread的方法)
--> ActivityThread#handleBindApplication(通過Handler切換到主線程)
初始化Application實例

ActivityThread#handleBindApplication方法中,通過調(diào)用LoadedApk#makeApplication來創(chuàng)建Application對象蘸泻,并調(diào)用其生命周期方法琉苇,LoadedApk#makeApplication的核心代碼如下:

public Application makeApplication(...) {
    ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
    app = mActivityThread.mInstrumentation.newApplication(...);
    appContext.setOuterContext(app);

    // 調(diào)用Application#onCreate方法
    instrumentation.callApplicationOnCreate(app);

    return app;
}

創(chuàng)建Application對象的具體流程如下:

LoadedApk#makeApplication
--> Instrumentation#newApplication
--> Instrumentation#newApplication(重載方法)  初始化Application實例
--> Application#attach
--> Application#attachBaseContext 

至此我們的應(yīng)用進程和Application均初始化完畢,我們看下如何在進程啟動后啟動之前放置在mPendingServices中的服務(wù)的悦施。

ActivityManagerService#attachApplicationLocked——進程啟動后啟動服務(wù)

前面說過并扇,進程啟動以后,ActivityThread會向AMS上報抡诞,會調(diào)用到ActivityManagerService#attachApplicationLocked方法穷蛹,還是老規(guī)矩,我們只看相關(guān)的核心內(nèi)容:

private final boolean attachApplicationLocked(IApplicationThread thread沐绒,...) {
    // 調(diào)用應(yīng)用端的ActivityThread#bindApplication方法俩莽,完成Application初始化
    thread.bindApplication(processName, ...);

    // Find any services that should be running in this process...
    // 調(diào)用ActiveServices#attachApplicationLocked方法,執(zhí)行mPendingServices中的服務(wù)
    didSomething |= mServices.attachApplicationLocked(app, processName);
}

ActiveServices#attachApplicationLocked方法中乔遮,會遍歷mPendingServices扮超,對每個service都執(zhí)行ActiveServices#realStartServiceLocked方法,具體代碼如下:

boolean attachApplicationLocked(ProcessRecord proc, String processName){
    if (mPendingServices.size() > 0) {
        ServiceRecord sr = null;
        for (int i=0; i<mPendingServices.size(); i++) {
            sr = mPendingServices.get(i);
            mPendingServices.remove(i);
            i--;
            realStartServiceLocked(sr, proc, sr.createdFromFg);
        }
    }
}

ActiveServices#realStartServiceLocked方法中,會通過Binder調(diào)用ActivityThread#scheduleCreateService方法出刷,告訴應(yīng)用啟動Service璧疗;接著還會調(diào)用ActiveServices#sendServiceArgsLocked方法,通過Binder調(diào)用ActivityThread#scheduleServiceArgs方法馁龟,最終調(diào)用Service#onStartCommand方法崩侠,下面看下細節(jié)。

啟動服務(wù)——ActivityThread#scheduleCreateService

調(diào)用鏈如下:

ActivityThread#scheduleCreateService
--> 發(fā)送`H.CREATE_SERVICE`消息給主線程
--> ActivityThread#handleCreateService

ActivityThread#handleCreateService方法里面會初始化Service類坷檩,調(diào)用其onCreate方法却音。核心代碼如下:

private void handleCreateService(CreateServiceData data) {
    // 初始化Service對象
    java.lang.ClassLoader cl = packageInfo.getClassLoader();
    Service service = (Service) cl.loadClass(data.info.name).newInstance();

    ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
    context.setOuterContext(service);
    // 獲取對應(yīng)的Application對象
    Application app = packageInfo.makeApplication(false, ...);
    // 調(diào)用Service#onCreate()方法
    service.onCreate();
}
調(diào)用服務(wù)的onStartCommand——ActivityThread#scheduleServiceArgs

調(diào)用鏈如下:

ActivityThread#scheduleServiceArgs
--> 發(fā)送`H.SERVICE_ARGS`消息給主線程
--> ActivityThread#handleServiceArgs
--> Service#onStartCommand

至此第一次啟動應(yīng)用 + 啟動服務(wù)的總體過程完成。

流程總結(jié)

image

至此矢炼,一次完整的startService過程的關(guān)鍵步驟分析完畢系瓢。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市句灌,隨后出現(xiàn)的幾起案子夷陋,更是在濱河造成了極大的恐慌,老刑警劉巖胰锌,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件骗绕,死亡現(xiàn)場離奇詭異,居然都是意外死亡资昧,警方通過查閱死者的電腦和手機酬土,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來榛搔,“玉大人诺凡,你說我怎么就攤上這事〖螅” “怎么了腹泌?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長尔觉。 經(jīng)常有香客問我凉袱,道長,這世上最難降的妖魔是什么侦铜? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任专甩,我火速辦了婚禮,結(jié)果婚禮上钉稍,老公的妹妹穿的比我還像新娘涤躲。我一直安慰自己,他們只是感情好贡未,可當我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布种樱。 她就那樣靜靜地躺著蒙袍,像睡著了一般。 火紅的嫁衣襯著肌膚如雪嫩挤。 梳的紋絲不亂的頭發(fā)上害幅,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天,我揣著相機與錄音岂昭,去河邊找鬼以现。 笑死,一個胖子當著我的面吹牛约啊,可吹牛的內(nèi)容都是我干的邑遏。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼棍苹,長吁一口氣:“原來是場噩夢啊……” “哼无宿!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起枢里,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蹂午,沒想到半個月后栏豺,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡豆胸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年奥洼,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片晚胡。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡灵奖,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出估盘,到底是詐尸還是另有隱情瓷患,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布遣妥,位于F島的核電站擅编,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏箫踩。R本人自食惡果不足惜爱态,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望境钟。 院中可真熱鬧锦担,春花似錦、人聲如沸慨削。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至痘煤,卻和暖如春凑阶,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背衷快。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工宙橱, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蘸拔。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓师郑,卻偏偏與公主長得像,于是被迫代替她去往敵國和親调窍。 傳聞我的和親對象是個殘疾皇子宝冕,可洞房花燭夜當晚...
    茶點故事閱讀 44,979評論 2 355

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