聊聊Context

/ 移动技术 / 没有评论 / 148浏览

在上篇文章《设计模式之装饰模式》中我们谈到了装饰模式,在 Android 中关于 Context 的设计就用到了装饰模式。这篇文章我们就来聊一聊 Context。

关于 Context,作为 Android 开发人员再熟悉不过了。启动 Actiivty、Service 需要 Context,获取资源需要 Context。离开 Context 整个系统都玩不转了,可见 Context 在 Android 中多么重要。

先来回忆一下上篇文章中装饰模式的结构图

1.png

再来对比一下 Context 的继承结构图 2.png

看着这两张图我们来找一下对应关系

Componment -> Context

ConcreteComponment -> ContextImpl

Decorator -> ContextWrapper

ConcreteDecorator -> ContextThemeWraper、Activity、Service、Application

再来具体看一下代码

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 分析完毕。