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系统中的使用到此就介绍完毕了,关于这类知识的使用在开发中或许使用不多,但在插件化过程当中须要替换系统资源时就必须了解其内部原理和关系了。

相关文章
相关标签/搜索