Android小知識-ActivityManagerService詳解(APP啟動過程)

顧林海發表於2019-03-03

本平臺的文章更新會有延遲,大家可以關注微信公眾號-顧林海,包括年底前會更新kotlin由淺入深系列教程,目前計劃在微信公眾號進行首發,如果大家想獲取最新教程,請關注微信公眾

前言

AMS(ActivityManagerService)的啟動是在SystemServer程式中啟動的,它的職責是用於和所有APP的四大元件進行通訊,Activity的啟動過程就是APP端與AMS端進行通訊,首先理解的一點是APP端與AMS是在兩個不同的程式中,因此APP端與AMS是通過跨程式通訊的。

從Launcher啟動APP

Launcher就是指手機的螢幕,同時它也是一個APP,只不過這是由製造商開發的,手指點選螢幕的某個APP,這時APP啟動並開啟首頁的介面,這一系列操作需要和AMS進行通訊才能完成,APP安裝(啟動)時,PackageManagerService從APK包的AndroidManifest檔案中讀取四大元件的資訊並儲存下來。

下圖是Launcher與AMS的通訊時序圖:

app1.png

Launcher與APP是在兩個不同的程式中,他們之間的通訊是通過Binder完成的,點選Launcher上的某個APP,這時會呼叫Launcher的startActivitySafely方法。

public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {
    ...
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);//1
    ...
    startActivity(intent, optsBundle);//2
    ...
}
複製程式碼

上面程式碼我省略了一些不太重要的,重點看上面兩行程式碼,第一行給intent設定Flag為Intent.FLAG_ACTIVITY_NEW_TASK,Activity會在新的任務棧中啟動,第二行程式碼呼叫startActivity方法,很簡單,就是啟動APP中的Activity。

最終會呼叫Activity的startActivity方法,Intent中攜帶的就是需要啟動的APP的Activity資訊。startActivity方法最終會呼叫startActivityForResult方法,程式碼如下:

@Override
public void startActivity(Intent intent) {
   this.startActivity(intent, null);
}

@Override
public void startActivity(Intent intent, @Nullable Bundle options) {
   if (options != null) {
       startActivityForResult(intent, -1, options);
   } else {
       // Note we want to go through this call for compatibility with
       // applications that may have overridden the method.
       startActivityForResult(intent, -1);
   }
}



public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
                                  @Nullable Bundle options) {
   ...
   Instrumentation.ActivityResult ar =
           mInstrumentation.execStartActivity(
                   this, mMainThread.getApplicationThread(), mToken, this,
                   intent, requestCode, options);
   ...
}
複製程式碼

Activity內部會保持一個對Instrumentation的引用,Instrumentation主要用來監控應用程式和系統的互動,在startActivityForResult方法中會呼叫Instrumentation的execStartActivity方法,在execStartActivity方法的第二個引數中,可以看到一個mMainThread的變數,這是一個ActivityThread型別的變數,ActivityThread就是主執行緒,也就是我們平常所說的UI執行緒,它是在APP啟動時建立的,代表APP應用程式,ActivityThread裡面有個main函式,是APP啟動時的入口。

execStartActivity方法傳遞了兩個很重要的引數,mMainThread.getApplicationThread(Binder物件)將Launcher所在的程式傳遞了過去,這樣AMS知道是哪個程式;還有一個比較重要的是mToken,它是Binder物件,代表Launcher這個Activity也通過Instrumentation傳給AMS,AMS查詢時,就知道誰向AMS發起了請求。

Instrumentation的execStartActivity程式碼如下:

public Instrumentation.ActivityResult execStartActivity(
        Context who, IBinder contextThread, IBinder token, Activity target,
        Intent intent, int requestCode, Bundle options) {
   ...
    try {
        ...
        int result = ActivityManager.getService()
                .startActivity(whoThread, who.getBasePackageName(), intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                        token, target != null ? target.mEmbeddedID : null,
                        requestCode, 0, null, options);
        ...
    } catch (RemoteException e) {
        throw new RuntimeException("Failure from system", e);
    }
    return null;
}
複製程式碼

這裡通過ActivityManager的getService方法來獲取AMS的代理物件(Android 7.0是通過ActivityManagerNative的getDefault來獲取AMS的代理物件),返回一個型別為IActivityManager,IActivityManager是一個介面,內部定義了四大元件的生命週期,

public static IActivityManager getService() {
    return IActivityManagerSingleton.get();
}

private static final Singleton<IActivityManager> IActivityManagerSingleton =
        new Singleton<IActivityManager>() {
            @Override
            protected IActivityManager create() {
                final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
                final IActivityManager am = IActivityManager.Stub.asInterface(b);
                return am;
            }
        };
複製程式碼

Singleton是一個單例類,內部通過ServiceManager取出一個activity的物件,通過IActivityManager.Stub.asInterface將它包裝成一個ActivityManagerProxy物件(AMP),IActivityManager.Stub.asInterface這段程式碼是不是很熟悉,就是AIDL遠端代理,這裡獲取到的是AMS的代理物件。

AMP告訴AMS啟動哪個APP,並且啟動的是哪個Activity,AMS會檢查APP中的AndroidManifest檔案,看看是否存在要啟動的Activity,如果不存在,就會丟擲一個Activity not found的錯誤。AMS檢查到啟動的Activity存在,這時會告訴Launcher:“我要啟動Activity了”,Launcher會將它所在的程式傳給AMS,AMS會將它儲存為一個ActivityRecord物件,這個物件裡面有一個ApplicationThreadProxy,是一個Binder代理,AMS想要傳送訊息給Launcher,可以通過ApplicationThreadProxy(ATP)來傳送訊息,ATP是APP端ApplicationThread(APT)的代理物件,用於AMS與APP端的通訊。

既然AMS知道了啟動的Activity,接下來就應該啟動Activity,在啟動Activity之前,AMS需要告訴Launcher“我要啟動了,你可以暫停了“,時序圖如下:

app2.png

中間通過ApplicationThreadProxy向APP端的ApplicationThread傳送訊息,ApplicationThread接受到AMS的訊息後,呼叫ActivityThread的sendMessage方法,向Launcher的主執行緒訊息佇列傳送一個PAUSE_ACTIVITY訊息。

@Override
public void handlePauseActivity(IBinder token, boolean finished, boolean userLeaving,
                                int configChanges, PendingTransactionActions pendingActions, String reason) {
    //獲取Launcher的Activity
    ActivityClientRecord r = mActivities.get(token);
    if (r != null) {
        if (userLeaving) {
            performUserLeavingActivity(r);
        }

        r.activity.mConfigChangeFlags |= configChanges;
        performPauseActivity(r, finished, reason, pendingActions);

        // Make sure any pending writes are now committed.
        if (r.isPreHoneycomb()) {
            QueuedWork.waitToFinish();
        }
        mSomeActivitiesChanged = true;
    }
}
複製程式碼

handlePauseActivity方法中從mActivities集合中,獲取Launcher的Activity並讓他休眠。

到這裡Launcher與AMS之間的通訊就結束了,接下來的事情就是啟動APP中的Activity,因為是首次啟動,APP的程式不存在,需要建立一個新的程式,需要呼叫Process.start方法,並且指定了ActivityThread的main函式為入口函式:

int pid=Process.start("android.app.ActivityThread",
        mSimpleProcessManagement ? app.processName:gid,
        debugFlags,
        null);
複製程式碼

為新程式建立ActivityThread物件,也就是UI執行緒,同時執行入口函式main,其中建立一個主執行緒Looper,也就是MainLooper。

另外建立Application,主執行緒序會收到BIND_APPLICATION訊息:

public void handleMessage(Message msg) {
    ...
    switch (msg.what) {
        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;
            ...
    }
    ...
}
複製程式碼

根據傳遞過來的ApplicationInfo建立一個對應的LoadedApk物件,然後建立ContextImpl物件,接著通過反射建立目標Application,並呼叫attach方法,將ContextImpl物件設定為目標Application的上下文環境,最後呼叫Application的onCreate函式。

建立完APP後,APP端通知AMS建立完畢同時把ActivityThread物件傳送給AMS,同時AMS端把ActivityThread物件轉換成一個ActivityThreadProxy物件,之後AMS可以向APP端傳送訊息,通過ActivityThreadProxy這個代理物件。

app3.png
public void callActivityOnCreate(Activity activity, Bundle icicle) {
    prePerformCreate(activity);
    activity.performCreate(icicle);
    postPerformCreate(activity);
}
複製程式碼

ActivityThread接受到AMS的訊息,在H中傳送LAUNCH_ACTIVITY訊息,呼叫handleLaunchActivity方法,在該方法中通過Instrumentation的newActivity方法,建立要啟動的Activity例項,為這個Activity建立一個上下文Context物件,並與Activity關聯,通過Instrumentation的callActivityOnCreate方法,執行Activity的onCreate方法,從而啟動Activity,至此APP啟動完畢。


838794-506ddad529df4cd4.webp.jpg

搜尋微信“顧林海”公眾號,定期推送優質文章。

相關文章