Android插件化——深入理解Context机制

2,331 阅读6分钟

1、Context介绍

Context在Android中表示上下文对象,也是开发中经常使用的类,如资源的获取、View的创建、窗口创建添加等,在Android的四大组件中也随处可见Context的身影,也是Context使用的主战场,可以说Context的重要程度非一般类可比,但很多人对其内部结构并不是很熟悉,最基本的将、经常使用的却不一定熟悉,是不是有点灯下黑的感觉,本篇文章就针对context在Android中的使用进行学习;

按照实际开发的使用场景来说,Context一般分两种:

  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的方法;

2、Application中Context创建和获取

Android进阶知识树——Android四大组件启动过程知道,程序中Activity在启动过程会执行到ActivityThread.performLaunchActivity()中,在performLaunchActivity()中完成Activity主要的创建和初始化过,程其中就包含创建Application对象

Application app = r.packageInfo.makeApplication(false, mInstrumentation); //创建了Application并调用onCreate()初始化
2.1、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对象;

  • 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.2、Context获取过程

开发中使用getApplicaitonContext()获取程序中的Application的Context对象,在getApplicaitonContext是ContextWrapper中方法,其内部直接调用父类的mBase方法获取对象,此处的mBase就是上面赋值的ContextImpl,在ContextImpl.getApplicationContext()中根据mPackageInfo获取创建的Application对象;

 @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并返回。

3、Activity中Context的创建

由Activity的启动过程知道performLaunchActivity()中不仅完成Application中Context的创建过程,还实例化了Activity对象并且创建了Activity中的Context

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对象
    }

4、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
    }

5、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
    }

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