本平臺的文章更新會有延遲,大家可以關注微信公眾號-顧林海,包括年底前會更新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的通訊時序圖:
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“我要啟動了,你可以暫停了“,時序圖如下:
中間通過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這個代理物件。
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啟動完畢。
搜尋微信“顧林海”公眾號,定期推送優質文章。