天天看點

Activity 的啟動流程源碼剖析(一)

簡單分析 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);
        }
           
  1. mParent 代表 ActivityGroup,ActivityGroup 已經在 API 13 中廢棄了,官方推薦使用 Fragment 來代替 ActivityGroup ,是以這個

    mParent == null 會一直成立

  2. ApplicationThread() 是 ActivityThread 的内部類,通過後面的分析你會發現他對 Activity 的啟動過程起着至關重要的作用
  3. 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.java
public 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() 的啟動一樣了

Activity 的啟動流程源碼剖析(一)

歡迎關注我的公衆号

繼續閱讀