Android插件化——深刻理解Context機制

一、Context介紹

Context在Android中表示上下文對象,也是開發中常常使用的類,如資源的獲取、View的建立、窗口建立添加等,在Android的四大組件中也隨處可見Context的身影,也是Context使用的主戰場,能夠說Context的重要程度非通常類可比,但不少人對其內部結構並非很熟悉,最基本的將、常常使用的卻不必定熟悉,是否是有點燈下黑的感受,本篇文章就針對context在Android中的使用進行學習;java

按照實際開發的使用場景來講,Context通常分兩種:android

  1. 使用Context調用方法,如Activity的啓動、ContentProvider等
  2. 調用方法時傳入context,如建立Dialog、View的建立等
  • Context的繼承關係
    在這裏插入圖片描述
    這裏先給出Android中Context的繼承關係圖,總結以下:
  1. ContextImpl和ContextWrapper都繼承了Context,在ContextWrapper內部保存這ContextImpl的對象mBase;
  2. ContextThemeWrapper、Service、Application都繼承於ContextWrapper,它們內部均可以經過mBase使用ContextImpl的方法;
  3. Activity繼承ContextThemeWrapper類,由於ContextThemeWrapper擴展了Context的方法;

二、Application中Context建立和獲取

Android進階知識樹——Android四大組件啓動過程知道,程序中Activity在啓動過程會執行到ActivityThread.performLaunchActivity()中,在performLaunchActivity()中完成Activity主要的建立和初始化過,程其中就包含建立Application對象app

Application app = r.packageInfo.makeApplication(false, mInstrumentation); //建立了Application並調用onCreate()初始化
複製代碼
2.一、Context建立過程
  • makeApplication():建立Application對象,調用onCreate()
public Application makeApplication(boolean forceDefaultAppClass, Instrumentation instrumentation) {
        appClass = "android.app.Application";
    try {
        java.lang.ClassLoader cl = getClassLoader();
        ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
        app = mActivityThread.mInstrumentation.newApplication( //調用Instrumentation建立Application
                cl, appClass, appContext);
        appContext.setOuterContext(app);//
    }
    return app;
}
複製代碼
  • ContextImpl.createAppContext()
static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) {
        ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, 0,
                null);
        context.setResources(packageInfo.getResources());
        return context;
    }
複製代碼

在上面方法中直接建立了ContextImpl對象,並初始化ContextImpl對象中的Resource實例,在建立了ContextImpl後調用了 mInstrumentation.newApplication()方法,傳入appClass、ClassLoader、appContext對象;ide

  • newApplication():建立Application對象並初始化mBase
Application app = getFactory(context.getPackageName())
                .instantiateApplication(cl, className);
app.attach(context);

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

在newApplication()中先使用ClassLoader和appClass建立了Application對象,而後調用attach()將Application與ContextImpl對象進行綁定,attach()中調用了父類ContextWrapper中的attachBaseContext()方法函數

  • attachBaseContext()
protected void attachBaseContext(Context base) {
        mBase = base;
    }
複製代碼

通過上面的過程,就可將建立的ContexImpl對象賦值在Application中的mBase對象,在程序使用中便可直接經過mBase調用ContexImpl中的方法;學習

2.二、Context獲取過程

開發中使用getApplicaitonContext()獲取程序中的Application的Context對象,在getApplicaitonContext是ContextWrapper中方法,其內部直接調用父類的mBase方法獲取對象,此處的mBase就是上面賦值的ContextImpl,在ContextImpl.getApplicationContext()中根據mPackageInfo獲取建立的Application對象;this

@Override
    public Context getApplicationContext() {
        return mBase.getApplicationContext();
    }
// ContextImpl
@Override
    public Context getApplicationContext() {
        return (mPackageInfo != null) ?
                mPackageInfo.getApplication() : mMainThread.getApplication();
    }

//最終返回的是在LoadedApk.makeApplication中建立的mApplication
Application getApplication() {
        return mApplication;
    }
複製代碼

從Application的建立和獲取過程可知道,在建立Application對象前建立了ContextImpl對象,在Application對象建立後調用attach()賦值父類的mBase,將Applicaiton和ContextImpl關聯起來,在使用時經過mBse查找系統的Applicaiton並返回。spa

三、Activity中Context的建立

由Activity的啓動過程知道performLaunchActivity()中不只完成Application中Context的建立過程,還實例化了Activity對象而且建立了Activity中的Context.net

ContextImpl appContext = createBaseContextForActivity(r);
activity = mInstrumentation.newActivity( cl, component.getClassName(), r.intent);

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);
複製代碼

下面這對前面的過程逐步分析:插件

  • createBaseContextForActivity():建立並返回ContextImpl對象
private ContextImpl createBaseContextForActivity(ActivityClientRecord r) {
        ContextImpl appContext = ContextImpl.createActivityContext(
                this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig);
        return appContext;
    }
複製代碼
  • activity.attach():完成Activity中mBase的初始化
final void attach(Context context, ActivityThread aThread, ...... Window window, ActivityConfigCallback activityConfigCallback) {
        attachBaseContext(context);
        }
複製代碼

在attach()中一樣調用父類的attachBaseContext()方法,由上面的分析知道attachBaseContext()賦值了父類的mBase對象,因此在Activity中調用context的方法其真實調用的就是建立的ContextImpl對象;

  • Activity中獲取Context
public Context getBaseContext() {
        return mBase; //直接返回前面賦值的mBase,即ContextImpl對象
    }
複製代碼

四、Sevice中Context的建立

Android進階知識樹——Android四大組件啓動過程知道,Service的啓動過程當中會執行的ActivityThread中的handleCreateService()方法,handleCreateService()中進行了一些列對象的建立和初始化:

ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
service.attach(context, this, data.info.name, data.token, app,
                    ActivityManager.getService());
複製代碼

在handleCreateService()中和Application中建立過程同樣,先調用createAppContext()建立ContextImpl對象,而後調用Service的attach()方法

public final void attach( ...... Application application, Object activityManager) {
        attachBaseContext(context);
        }
複製代碼

Service的attach()中一樣調用父類的attachBaseContext()方法進行父類mBase的初始化,使用時也同樣調用ContextImpl中的方法。

public Context getBaseContext() {
        return mBase; // 獲取mBase
    }
複製代碼

五、ContentProvider中使用

Android進階知識樹——ContentProvider使用和工做過程詳解一文知道,在AMS啓動程序進程後就會進行ContentProvider操做,具體在ActivityThread中的handleBindApplication()中,handleBindApplication中有如下幾行代碼

private void handleBindApplication(AppBindData data) {
      final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);//1
      installContentProviders(app, data.providers);//2
}
複製代碼

在註釋1處和Application中同樣調用createAppContext()建立ContentImpl對象,而後調用installContentProviders()執行ContentProvider初始化

  • installContentProviders()
localProvider = packageInfo.getAppFactory().instantiateProvider(cl, info.name);//1
provider = localProvider.getIContentProvider();

localProvider.attachInfo(c, info); //
複製代碼

註釋1中使用ClassLoader建立ContentProvider對象,在註釋2中調用attachInfo()方法,傳入的第一個參數就是上面建立的appContext對象;

  • attachInfo():將傳入的appContext保存在ContentProvider中的mContext中
private void attachInfo(Context context, ProviderInfo info, boolean testing) {
        if (mContext == null) {
            mContext = context; //賦值mContext
        }
    }
複製代碼
  • ContentProvider中Context的獲取
public final @Nullable Context getContext() {
        return mContext; // 直接返回保存的Context
    }
複製代碼

六、BroadCast Receiver中使用

在BroadCast Receiver的回調方法onReceive()的參數中會回傳Context對象,因此廣播中的Context使用主要是針對此處,由 Android進階知識樹——Android四大組件啓動過程知道廣播的發送最終執行到LoadedApk.ReceiverDispatcher.performReceive()中,而後執行Runnable中方法完成onReceiver()的回調

public final Runnable getRunnable() {
    return () -> {
    final BroadcastReceiver receiver = mReceiver; //執行receiver的onReceiver()
    receiver.setPendingResult(this);
    receiver.onReceive(mContext, intent); // 傳入mContext
  }
}
複製代碼

那麼此處的context是從哪來的呢?跟隨mContext查找會發現它是ReceiverDispatcher中的屬性,在ReceiverDispatcher的構造函數中完成初始化

final Context mContext;
 ReceiverDispatcher(BroadcastReceiver receiver, Context context,
                Handler activityThread, Instrumentation instrumentation,
                boolean registered) {
           mContext = context; //初始化mContext
        }
複製代碼

熟悉四大組件啓動過程的知道,ReceiverDispatcher是在註冊廣播時建立的,由於廣播多是跨進程的因此在ReceiverDispatcher中保存Binder和Receiver的對應關係,具體的註冊在ContextImpl中,最終調用ContextImpl.registerReceiverInternal(),在registerReceiverInternal()中調用getReceiverDispatcher()建立ReceiverDispatcher對象

rd = mPackageInfo.getReceiverDispatcher(
                    resultReceiver, getOuterContext(), scheduler,
                    mMainThread.getInstrumentation(), false);

final Context getOuterContext() {
        return mOuterContext;
    }
複製代碼

在getReceiverDispatcher()中傳入getOuterContext(),getOuterContext()獲取的是ContentImpl的屬性mOuterContext,mOuterContext在Activity的初始化過程當中被賦值;

appContext.setOuterContext(activity); //傳入Activity對象
複製代碼

到此,關於Context的繼承關係和在Android系統中的使用到此就介紹完畢了,關於這類知識的使用在開發中或許使用不多,但在插件化過程當中須要替換系統資源時就必須瞭解其內部原理和關係了。

相關文章
相關標籤/搜索