簡單分析 Activity 的啟動流程(一)
這篇主要分析 startActivity 這個方法
源碼的版本
Android 27
V4 27.1.1
我都是粘的裡面比較關鍵的源碼,還希望配合源碼閱讀
第一步先找到源碼的切入點
我們啟動 Activity 一般都是調用
startActivity()
這個方法 Activity、Context、Fragment 中都有我們分别看一下這幾種調用的具體源碼
一、Activity 的 startActivity()/startActivityForResult()
public void startActivity(Intent intent, @Nullable Bundle options) {
if (options != null) {
startActivityForResult(intent, -1, options);
} else {
startActivityForResult(intent, -1);
}
}
startActivity 是直接調用了 startActivityForResult() 如果不需要請求結果的話 requestCode 直接傳 -1 就可以了
在 startActivityForResult 中發現了很關鍵的代碼
if (mParent == null) {
options = transferSpringboardActivityOptions(options);
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, this,
intent, requestCode, options);
if (ar != null) {
mMainThread.sendActivityResult(
mToken, mEmbeddedID, requestCode, ar.getResultCode(),
ar.getResultData());
}
if (requestCode >= 0) {
mStartedActivity = true;
}
cancelInputsAndStartExitTransition(options);
}
-
mParent 代表 ActivityGroup,ActivityGroup 已經在 API 13 中廢棄了,官方推薦使用 Fragment 來代替 ActivityGroup ,是以這個
mParent == null 會一直成立
- ApplicationThread() 是 ActivityThread 的内部類,通過後面的分析你會發現他對 Activity 的啟動過程起着至關重要的作用
-
Instrumentation 看這個類的注釋解釋這個類的作用是一個儀器測試類,後面分析中你們會發現 Activity 的主要流程都會在這個類中
好了我們繼續檢視 Instrumentation 的 execStartActivity 方法
int result = ActivityManager.getService()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target, requestCode, 0, null, options);
checkStartActivityResult(result, intent);
我們看一下 ActivityManager.getService()
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;
}
};
再以上代碼中我們可以分析出 ActivityManager.getService() 獲得的就是一個 IActivityManager 的 Binder 對象,由此推出這裡的 Activity 的啟動是遠端調用的 ActivityManagerService 也就是我們熟知的 AMS,這裡就是典型的 Binder 的使用,不熟悉的可以了解下 Binder 機制
如何驗證 Context.ACTIVITY_SERVICE 這個 Service 為 ActivityManagerService 呢,看一下 ActivityManagerService 的 setSystemProcess() 就了解了ServiceManager.addService(Context.ACTIVITY_SERVICE, this, true);
我們再額外說一下 checkStartActivityResult 方法,跟進去可以發現全是報錯資訊是吧,平常我們常見的也有好多。就比如說這個
throw new ActivityNotFoundException(
"Unable to find explicit activity class "
+ ((Intent)intent).getComponent().toShortString()
+ "; have you declared this activity in your AndroidManifest.xml?");
二、Context 的 startActivity()
因為 abstract 是一個抽象類,我們找到他的實作類 ContextImpl,那麼為什麼确定是 ContextImpl 呢,我們提前先看一下 ActivityThread.performLaunchActivity() 的方法裡面調用的 createBaseContextForActivity() 方法
ContextImpl appContext = ContextImpl.createActivityContext(
this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig);
關鍵代碼是 ContextImpl.createActivityContext() 方法
static ContextImpl createActivityContext(ActivityThread mainThread,
LoadedApk packageInfo, ActivityInfo activityInfo, IBinder activityToken, int displayId,
Configuration overrideConfiguration) {
if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
String[] splitDirs = packageInfo.getSplitResDirs();
ClassLoader classLoader = packageInfo.getClassLoader();
ContextImpl context = new ContextImpl(null, mainThread, packageInfo, activityInfo.splitName,
activityToken, null, 0, classLoader);
return context;
}
這裡看到了 activity.attach() 傳入的 appContext 實際上就是 ContextImpl 。
我們接下來繼續檢視 ContextImpl 的 startActivity() 方法
if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0
&& options != null && ActivityOptions.fromBundle(options).getLaunchTaskId() == -1) {
throw new AndroidRuntimeException(
"Calling startActivity() from outside of an Activity "
+ " context requires the FLAG_ACTIVITY_NEW_TASK flag."
+ " Is this really what you want?");
}
mMainThread.getInstrumentation().execStartActivity(
getOuterContext(), mMainThread.getApplicationThread(), null,
(Activity) null, intent, -1, options);
又看到了熟悉的代碼,還有一個地方不知道你們發沒發現,使用 Context 會判斷你的 Flags 如果不是 FLAG_ACTIVITY_NEW_TASK 就會報錯,不知道各位觀衆老爺有沒有遇見過
三、Fragment 的 startActivity()/startActivityForResult()
檢視 Fragment 的 startActivity() 方法看到以下關鍵代碼
mHost.onStartActivityFromFragment(this /*fragment*/, intent, -1, options);
這個地方是通過
mHost(FragmentHostCallback)
對象來執行啟動流程的,然而這個類也是抽象的,好歹他就一個實作類
HostCallbacks
,檢視 HostCallbacks 的 onStartActivityFromFragment() 方法
FragmentActivity.this.startActivityFromFragment(fragment, intent, requestCode);
這個類調用的是 FragmentActivity 的 startActivityFromFragment 方法,我們繼續往裡跟
if (requestCode == -1) {
ActivityCompat.startActivityForResult(this, intent, -1, options);
return;
}
checkForValidRequestCode(requestCode);
int requestIndex = allocateRequestIndex(fragment);
ActivityCompat.startActivityForResult(
this, intent, ((requestIndex + 1) << 16) + (requestCode & 0xffff), options);
這個地方挺有意思的一點就是他的 requestCode 經過了簡單的修改,這估計就是為了 Fragment 也能收到 onActivityResult 所作的處理,感興趣可以自己看一下這不是我們這篇文章要講的。
ActivityCompat 是 v4 包的一個相容類,包括我們平時寫代碼的時候也可以用,廢話不多說我們繼續跟
if (Build.VERSION.SDK_INT >= 16) {
activity.startActivityForResult(intent, requestCode, options);
} else {
activity.startActivityForResult(intent, requestCode);
}
又回到了 FragmentActivity 的 startActivityForResult 方法中
super.startActivityForResult(intent, requestCode);
這裡他直接調用了 Activity 的啟動方法
四、Launcher 啟動應用
到這裡我們簡單的講了我們開發中所能用到的啟動 Activity 的方式,我們在看一個我們沒見過的 Launcher 中啟動應用
我們找到源碼
packages/apps/Launcher3/src/com/android/launcher3/Launcher.javapublic boolean startActivitySafely(View v, Intent intent, ItemInfo item) {
//添加 FLAG_ACTIVITY_NEW_TASK Flags
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
if (Utilities.ATLEAST_MARSHMALLOW
&& (item instanceof ShortcutInfo)
&& (item.itemType == Favorites.ITEM_TYPE_SHORTCUT
|| item.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT)
&& !((ShortcutInfo) item).isPromise()) {
// Shortcuts need some special checks due to legacy reasons.
startShortcutIntentSafely(intent, optsBundle, item);
} else if (user == null || user.equals(Process.myUserHandle())) {
// Could be launching some bookkeeping activity
//啟動 Activity
startActivity(intent, optsBundle);
} else {
LauncherAppsCompat.getInstance(this).startActivityForProfile(
intent.getComponent(), user, intent.getSourceBounds(), optsBundle);
}
return true;
} catch (ActivityNotFoundException|SecurityException e) {
}
return false;
}
我們再看一下 他也是直接調用的 Activity 的 startActivity() 方法,後面的就和之前分析的 Activity.startActivity() 的啟動一樣了

歡迎關注我的公衆号