聊聊Context

在上篇文章《設計模式之裝飾模式》中咱們談到了裝飾模式,在 Android 中關於 Context 的設計就用到了裝飾模式。這篇文章咱們就來聊一聊 Context。java

關於 Context,做爲 Android 開發人員再熟悉不過了。啓動 Actiivty、Service 須要 Context,獲取資源須要 Context。離開 Context 整個系統都玩不轉了,可見 Context 在 Android 中多麼重要。android

先來回憶一下上篇文章中裝飾模式的結構圖設計模式

1.png

再來對比一下 Context 的繼承結構圖 bash

2.png

看着這兩張圖咱們來找一下對應關係微信

Componment -> Contextapp

ConcreteComponment -> ContextImpl框架

Decorator -> ContextWrapperide

ConcreteDecorator -> ContextThemeWraper、Activity、Service、Applicationui

再來具體看一下代碼this

Context.java

public abstract void startActivity(@RequiresPermission Intent intent);

public abstract ComponentName startService(Intent service);

public abstract Resources getResources();

複製代碼

Context 是一個抽象類,定義了咱們經常使用的大部分抽象方法。

ContextImpl.java

@Override
public void startActivity(Intent intent) {
	warnIfCallingFromSystemProcess();
	startActivity(intent, null);
}
複製代碼
@Override
public void startActivity(Intent intent, Bundle options) {

    //省略代碼
    //啓動activity的入口
    mMainThread.getInstrumentation().execStartActivity(
            getOuterContext(), mMainThread.getApplicationThread(), null,
            (Activity) null, intent, -1, options);
}

複製代碼
@Override
public ComponentName startService(Intent service) {
    warnIfCallingFromSystemProcess();
    return startServiceCommon(service, false, mUser);
}

複製代碼

ContextImpl 是 Context 的具體實現,也就是咱們實際使用的對象。

ContextWrapper.java

public class ContextWrapper extends Context {
    Context mBase;

    public ContextWrapper(Context base) {
        mBase = base;
    }
    
    protected void attachBaseContext(Context base) {
        if (mBase != null) {
            throw new IllegalStateException("Base context already set");
        }
        mBase = base;
    }
    // 省略代碼
    @Override
    public void startActivity(Intent intent) {
        mBase.startActivity(intent);
    }

}

複製代碼

ContextWrapper 這個類比較簡單,裏面有一個 Context(mBase) 的引用,也就是 ContextImpl 類的對象。還有一個 attachBaseContext 方法下面會提到。是給引用的 ContextImpl(mBase)賦值的地方。下面咱們看下 ContextImpl 這個對象是何時建立和賦值給 mBase 的。

要理解 ContextImpl 是如何建立的就不得不提到 Activity、Service、Application 的建立流程,因爲涉及的代碼比較多,咱們只看關鍵部分。

主要分析 ActivityThread 這個類,看一下 performLaunchActivity 方法

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    ActivityInfo aInfo = r.activityInfo;
    // 省略代碼
    //在這裏建立了ContextImpl
    ContextImpl appContext = createBaseContextForActivity(r);
    Activity activity = null;
    activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);

    if (activity != null) {
		 // 這個方法參數不少,如今咱們只關心第一個appContext
        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);

        if (customIntent != null) {
            activity.mIntent = customIntent;
        }
        // 省略代碼
        r.activity = activity;
    }
	// 省略代碼
    return activity;
}

複製代碼

Activity 建立關鍵代碼,注意 attach 這個方法

Activity.java

final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window, ActivityConfigCallback activityConfigCallback) {
       
        attachBaseContext(context);
		 
		 // 省略代碼
    }


複製代碼

看到這能夠發現,在 attach 方法中,第一個參數 appContext 也就是 ContextImpl 類的對象,在 attach 方法中又調用了 ContextWrapper 的attachBaseContext(context),最終把 appContext 賦值給 mBase。

Service建立關鍵代碼

private void handleCreateService(CreateServiceData data) {
   
    // 省略代碼
    Service service = null;
    java.lang.ClassLoader cl = packageInfo.getClassLoader();
    service = packageInfo.getAppFactory()
            .instantiateService(cl, data.info.name, data.intent);
   
    // 省略代碼
    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();
    mServices.put(data.token, service);
       
    // 省略代碼
}


複製代碼

Service.java關鍵代碼

public final void attach(
        Context context,
        ActivityThread thread, String className, IBinder token,
        Application application, Object activityManager) {
   
    attachBaseContext(context);
    
    // 省略代碼
}

複製代碼

能夠看到,一樣是在 attach 方法中,經過調用 attachBaseContext(context) 把 ContextImpl 的對象賦值給了 mBase。其餘的建立過程再也不分析,有興趣能夠了解下 ActivityThread 源碼。至此整個過程就串起來了,這就是裝飾模式在 Context 中的實現。

最後再來看一下 RePlugin 中 PluginContext 和 HostContext 是如何獲取的,直接上代碼。

RePlugin.java

// 獲取插件的Context
public static Context getPluginContext() {
    return RePluginEnv.getPluginContext();
}

// 獲取宿主的Context
public static Context getHostContext() {
        return RePluginEnv.getHostContext();
}

複製代碼

能夠看到兩個方法都是調用了 RePluginEnv 中的方法,再去看下 RePluginEnv 的代碼

RePluginEnv.java

// 獲取插件的Context
public static Context getPluginContext() {
        return sPluginContext;
}

// 獲取宿主的Context
public static Context getHostContext() {
        return sHostContext;
}

複製代碼

以上兩個 Context 都是在 init 方法中賦值的。

static void init(Context context, ClassLoader cl, IBinder manager) {
    sPluginContext = context;

    // 確保獲取的必定是主程序的Context
    sHostContext = ((ContextWrapper) context).getBaseContext();
}

複製代碼

再往下看, init 方法是在 Entry 的 create 方法調用。

Entry.java

public class Entry {

    public static final IBinder create(Context context, ClassLoader cl, IBinder manager) {
        // 初始化插件框架
        RePluginFramework.init(cl);
        // 初始化Env
        RePluginEnv.init(context, cl, manager);
        return new IPlugin.Stub() {
            @Override
            public IBinder query(String name) throws RemoteException {
                return RePluginServiceManager.getInstance().getService(name);
            }
        };
    }
}

複製代碼

Entry 的 create 又是在 Loader.java 中經過反射調用的,看下代碼

Loader.java

mPkgContext = new PluginContext(mContext, android.R.style.Theme, mClassLoader, mPkgResources, mPluginName, this);

複製代碼
IBinder b = (IBinder) mCreateMethod2.invoke(null, mPkgContext, getClass().getClassLoader(), manager);

複製代碼

能夠看到, create 方法的第一個參數就是 mPkgContext(PluginContext對象),它就是插件的 Context,有本身的 classloader 和 resource。同時 PluginContext 的構造方法接受一個 Context,也就是宿主的 Context,它也就是mBase。因此在上面的代碼中就能夠拿到宿主的 sHostContext 了。

sHostContext = ((ContextWrapper) context).getBaseContext();

複製代碼

至此,RePlugin 插件中獲取的宿主和插件 Context 分析完畢。

關注微信公衆號,最新技術乾貨實時推送

image src=
相關文章
相關標籤/搜索