startService源碼主要流程解析


本文基於的是Android 8.0源碼。

1、Activity中調用startService

我們啓動service的時候都是直接在Activity裏面調用startService方法,這裏實際上調用的是ContextImpl#startService方法。

我們知道,Activity繼承自ContextThemeWrappe,ContextThemeWrapper又繼承自ContextWrapper,ContextWrapper只是Context的靜態代理,Context的實現類就是ContextImpl。在創建Activity之後,系統會調用Activity#attach方法,將創建好的ContextImpl實例對象設置給Activity,所以在Activity中調用的Context對象的方法基本上都會走ContextImpl中的實現。

2、ContextImpl#startService

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

3、ActivityManagerService#startService

我們接着看ActivityManagerService#startService方法,

ActivityManagerService#startService
--> ActiveService#startServiceLocked(根據當前服務對象生成一個ServiceRecord.StartItem對象,添加進pendingStarts中,用於後面的onStartCommand方法調用)
--> ActiveService#startServiceInnerLocked
--> ActiveService#bringUpServiceLocked

這裏的ActiveService#bringUpServiceLocked方法是核心,我們看下關鍵代碼:

這裏我們把代碼分爲四部分,註釋中說明了核心邏輯。

private String bringUpServiceLocked(ServiceRecord r, ...){
    
    // 1
    // 如果條件滿足,說明服務也已經啓動過,因此服務對應的進程已經啓動,該進程對應的ActivityThread也已經準備好,接下來直接通知應用進程調用service的onStartCommand方法即可。
    if (r.app != null && r.app.thread != null) {
        sendServiceArgsLocked(r, execInFg, false);
        return null;
    }

    // 2
    // 走到這裏說明服務沒有啓動過,我們先查看服務所在的進程是否已經啓動
    // 如果進程已經啓動,並且ActivityThread也已經準備好,則啓動服務。
    ProcessRecord app = mAm.getProcessRecordLocked(procName,...);
    if (app != null && app.thread != null) {
       realStartServiceLocked(r, app, execInFg);
    }

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

    // 4 將服務添加進mPendingServices列表中,等待應用啓動之後再啓動服務
    if (!mPendingServices.contains(r)) {
        mPendingServices.add(r);
    }
    
    return null;
}

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

接下來我們講細節,如何啓動進程,以及進程啓動後如何啓動服務

4、啓動進程,執行mPendingServices中的服務

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方法的主要作用就是將傳入的應用進程的啓動參數argsForZygote寫入ZygoteState中,ZygoteState是ZygoteProcess的靜態內部類,用於表示與Zygote進程通信的狀態。

再看下openZygoteSocketIfNeeded方法,該方法內部會調用ZygoteState.connect(mSocket)方法,與Zygote進程建立Socket連接。

Zygote進程的Socket——接收創建進程的消息,fork創建子進程

我們接下來看下Zygote進程裏面啓動的Socket,我們先看ZygoteInit#main函數:
這裏我們只看跟Socket相關的

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

    // 開啓while循環,接收Socket消息並處理
    zygoteServer.runSelectLoop(abiList);

    zygoteServer.closeServerSocket();
}

Socket接收消息的邏輯在ZygoteServer.runSelectLoop方法中,接收到消息後會調用ZygoteConnection#runOnce方法,我們還是看關鍵代碼:

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 {
       ...
    }
}

先調用Zygote#forkAndSpecialize方法啓動進程,fork方法會返回兩次,pid爲0表示是我們關心的子進程,然後調用Zygote#handleChildProc方法進行處理,Zygote#handleChildProc方法裏面會調用ZygoteInit.zygoteInit方法,它的關鍵代碼如下:

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

RuntimeInit#applicationInit方法會調用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方法的作用很清晰,就是調用其入參className的main(argv[])方法,這裏的className的值就是android.app.ActivityThread

ActivityThread#main——通知AMS進程創建完成,初始化Application,

Zygote在啓動我們的應用進程後,會調用進程的入口函數android.app.ActivityThread#main。接下來我們看下ActivityThread#main是如何啓動服務的。

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

ActivityManagerService#attachApplication方法會將應用進程的ActivityThread的Binder對象上報給AMS,這樣AMS和應用進程就可以開始雙向調用了。接着往下看:

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

ActivityThread#handleBindApplication方法中,通過調用LoadedApk#makeApplication來創建Application對象,並調用其生命週期方法,LoadedApk#makeApplication的核心代碼如下:

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

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

    return app;
}

創建Application對象的具體流程如下:

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

至此我們的應用進程和Application均初始化完畢,我們看下如何在進程啓動後啓動之前放置在mPendingServices中的服務的。

ActivityManagerService#attachApplicationLocked——進程啓動後啓動服務

前面說過,進程啓動以後,ActivityThread會向AMS上報,會調用到ActivityManagerService#attachApplicationLocked方法,還是老規矩,我們只看相關的核心內容:

private final boolean attachApplicationLocked(IApplicationThread thread,...) {
    // 調用應用端的ActivityThread#bindApplication方法,完成Application初始化
    thread.bindApplication(processName, ...);

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

ActiveServices#attachApplicationLocked方法中,會遍歷mPendingServices,對每個service都執行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調用ActivityThread#scheduleCreateService方法,告訴應用啓動Service;接着還會調用ActiveServices#sendServiceArgsLocked方法,通過Binder調用ActivityThread#scheduleServiceArgs方法,最終調用Service#onStartCommand方法,下面看下細節。

啓動服務——ActivityThread#scheduleCreateService

調用鏈如下:

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

ActivityThread#handleCreateService方法裏面會初始化Service類,調用其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);
    // 獲取對應的Application對象
    Application app = packageInfo.makeApplication(false, ...);
    // 調用Service#onCreate()方法
    service.onCreate();
}
調用服務的onStartCommand——ActivityThread#scheduleServiceArgs

調用鏈如下:

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

至此第一次啓動應用 + 啓動服務的總體過程完成。

流程總結

至此,一次完整的startService過程的關鍵步驟分析完畢。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章