理解Context

zerozx發表於2019-01-27

是什麼

從應用場景的角度來說,他是一個場景,一個使用者與系統互動的過程.比如當你看簡訊時,場景包括簡訊的頁面,以及隱藏在後面的資料

提到頁面,我們應該能夠聯想到 Activity

沒錯,Activity,Service都是一個Context

從JAVA語言角度來說,Context是一個抽象類,抽象類中包含了Application環境的一些函式,設計角度而言,Context僅提供某些功能, extends 才是類的本質,即 Activity 的本質是一個 Context ,其所實現的其他介面只是為了擴充 Context 的功能而已,擴充後的類稱之為 Activity 或 Service

有多少Context

  • Application一個Context
  • 多少個Activity就有多少個Context
  • 多少個Service就有多少個Context

Context個數=1 + Activity個數 + Service個數

Application的Context建立

在部落格 ActivityThread.main過程 中分析中可以知道, handleBindApplication 函式中會呼叫 makeApplication

makeApplication會建立Application以及建立 ContextImpl

建立 ContextImpl

ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
複製程式碼

這裡的this是 LoadedApk 物件,該物件是在 handleBinderApplication 中賦值

data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
複製程式碼

在該函式中,會根據 AppBindData(handleBinderApplication中的引數) 中的ApplicationInfo的mPackageName建立一個PackageInfo物件並儲存為ActivityThread類的全域性物件

顯然,一個應用程式中所有Activity或者Application或Servie,他們的mPackageName是一樣的,即為包名,因此ActivityThread只會有一個全域性的PackageInfo物件

在 newApplication的函式中會呼叫 Application 的 attach

attach

final void attach(Context context) {
        attachBaseContext(context);
        mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
    }
複製程式碼

檢視 attachBaseContext

protected void attachBaseContext(Context base) {
        if (mBase != null) {
            throw new IllegalStateException("Base context already set");
        }
        mBase = base;
    }
複製程式碼

這個mBase就是 ContextWrapper 中的 Context

Activity的Context建立

Launcher啟動流程 的分析中可以知道,handleLaunchActivity 會呼叫到 performLaunchActivity,該函式會呼叫 createBaseContextForActivity

    private ContextImpl createBaseContextForActivity(ActivityClientRecord r) {
        final int displayId;
        ...
        ContextImpl appContext = ContextImpl.createActivityContext(
                this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig);
        ...
        return appContext;
    }
複製程式碼

createActivityContext

createActivityContext 中的 packageInfo 資訊和上小節分析的流程基本一致,他也是全域性的

static ContextImpl createActivityContext(ActivityThread mainThread,
            LoadedApk packageInfo, ActivityInfo activityInfo, IBinder activityToken, int displayId,
            Configuration overrideConfiguration) {
        ...

        ContextImpl context = new ContextImpl(null, mainThread, packageInfo, activityInfo.splitName,
                activityToken, null, 0, classLoader);

        ...
        return context;
    }
複製程式碼

attach

建立Context完成後,呼叫 activity 的 attach 函式

activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor, window, r.configCallback);
複製程式碼

attach 函式中做了很多的賦值操作,其中 attachBaseContext 的函式和Application的 attachBaseContext 中作用一致,把context賦值給 ContextWrapper 的 mBase

:::danger 筆記 因此,當我們翻閱Activity原始碼,看到mBase時,就應該去找 ContextImpl 裡的方法 :::

Service的Context建立

Service的啟動和Activity類似,最終同樣會呼叫到ActivityThread裡的函式,為 scheduleCreateService,接著呼叫 handleCreateService

在 handleCreateService 中會建立Context

ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
context.setOuterContext(service);
Application app = packageInfo.makeApplication(false, mInstrumentation);
service.attach(context, this, data.info.name, data.token, app,
                    ActivityManager.getService());
service.onCreate();
複製程式碼

Context的建立方式和Application一致,同樣在建立後會呼叫 attach 進行一些賦值操作,同樣也有之前分析的 mBase

總結

不同Context子類中PackageInfo物件來源

類名 遠端資料類 本地資料類 賦值方式
Application ApplicationInfo AppBindData getPackageInfoNoCheck
Activity ActivityInfo ActivityClientRecord getPackageInfo
Service ServiceInfo CreateServiceData getPackageInfoNoCheck

參考書籍: Android核心剖析

相關文章