Android问道--理解上下文Context

 

Context就是上下文对象,是Android常用的类

四大组件都会涉及到Context,理解了Context会更有助于学习四大组件的原理

Context的关联类

Context是一个应用程序环境信息的接口,在开发过程中我们经常使用Context,其使用场景分为两大类

  • 使用Context调用方法,比如启动Activity,访问资源,调用系统服务都是需要通过Context调用
  • 调用方法时传入Context,比如创建Dialog或弹Toast

Activity,Service,Application都间接继承于Context,我们可以计算出一个应用程序进程有多少个Context, 该数量等同于Activity和Service的总数+1,额外的+1是Application

Context是一个抽象类,其内部定义了很多的方法和静态变量,该抽象类的具体实现为ContextImpl

和Context关联的类,除了ContextImpl还有ContextWrapper,ContextThemeWrapper和Activity等,具体关系如下

classDiagram
    class ContextWrapper{
        mBase : ContextImpl
    }
    ContextThemeWrapper <|-- Activity
    ContextWrapper <|-- ContextThemeWrapper
    ContextWrapper <|-- Service
    ContextWrapper <|-- Application
    Context <|-- ContextWrapper 
    Context <|-- ContextImpl 

ContextImpl和ContextWrapper都继承于Context

ContextWrapper内部包含了Context类型的mBase对象,mBase具体指向ContextImpl

ContextImpl提供了很多功能,但是外界需要使用并扩展ContextImpl的功能,因此使用了装饰器模式

装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。

ContextWrapper就是装饰类,该类对ContextImpl进行了包装,起到了方法传递的作用,ContextWrapper类中的所有方法基本都是调用ContextImpl的相对应方法来实现的

ContextThemeWrapper,Service,Application都是继承于ContextWrapper,这样他们都可以通过mBase来使用Context方法,同时自身也是装饰类,可以在ContextWrapper的基础上添加不同的功能

Context的关联类采用了装饰模式,主要有以下优点

  • 使用者(如Service)能方便地使用Context
  • 如果ContextImpl发生了变化,装饰类ContextWrapper也不需要做任何修改
  • ContextImpl的实现不会暴露给使用者,使用者也不需要关系实现
  • 通过组合而非继承的方式拓展ContextImpl的功能,运行时选择不同的装饰类,实现不同的功能

为了理解Context的关联类的设计理念,需要理解Application,Activity,Service的Context创建过程

Application Context的创建过程

我们通常使用getApplicationContext方法来获取应用程序全局的ApplicationContxt

在应用程序启动完成后,应用程序就会有一个全局的ApplicationContext

创建过程的时序图如下

sequenceDiagram
	调用 ->> ApplicationThread : scheduleLauncherActivity
	activate ApplicationThread
	ApplicationThread ->> ActivityThread : sendMessage
	deactivate ApplicationThread 
	activate ActivityThread
	ActivityThread ->> H : handleMessage
	deactivate ActivityThread
	activate H
	H ->> ActivityThread : handleLauncherActivity
	deactivate H
	activate ActivityThread
	ActivityThread ->> ActivityThread : performLauncherActivity
	ActivityThread ->> LoadedApk : makeApplication
	deactivate ActivityThread
	activate LoadedApk
	LoadedApk ->> Instrumentation : newApplication
	deactivate LoadedApk
	activate Instrumentation
	Instrumentation ->> Application : attach
	deactivate Instrumentation
	activate Application
	Application ->> ContextWrapper : attachBaseContext
	deactivate Application
	activate ContextWrapper
	deactivate ContextWrapper

ActivityThread作为应用程序进程的主线程管理类,会调用其内部类ApplicationThread的scheduleLauncherActivity方法来启动Activity

在scheduleLauncherActivity方法中执行了以下代码

sendMessage(H.LAUNCH_ACTIVITY, r);

ApplicationThread的scheduleLauncherActivity方法中向着H类发送了LAUNCH_ACTIVITY类型的消息,目的是将启动的Activity的逻辑

放在主线程的消息队列中,这样启动Activity的逻辑会在主线程中执行

H类的handleMessage方法中接收消息,接收到LAUNCH_ACTIVITY类型的消息会执行

case LAUNCH_ACTIVITY: {
    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
    final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
    //
	r.packageInfo = getPackageInfoNoCheck(r.activityInfo.applicationInfo, r.compatInfo);
    //
	handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
	Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}

H类继承于Handler是ActivityThread的内部类,通过getPackageInfoNoCheck方法来获得LoadedApk类型的对象,并将该对象赋值给了ActivityClientRecordpackageInfo变量, LoadedApk是用来描述已加载的APK文件

之后调用了ActivityThread.handleLaunchActivity方法

Activity a = performLaunchActivity(r, customIntent);

handleLaunchActivity又调用了performLaunchActivity方法,获得了一个Activity对象

Application app = r.packageInfo.makeApplication(false, mInstrumentation);

在performLaunchActivity方法中有很多重要逻辑,和Context相关的逻辑就是调用了ActivityClientRecord类型的r对象的成员变量packageInfomakeApplication方法,packageInfo是LoadedApk类型,接下来来到LoadedApk.makeApplication方法中

  • 判断mApplication变量是否为空(也就是是否第一次启动),如果不为空就返回mApplication变量,为空就继续向下执行

    if (mApplication != null) {
    	return mApplication;
    }
    
  • 使用ContextImpl的createAppContext方法创建ContextImpl

    ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
    
  • 创建Application

    app = mActivityThread.mInstrumentation.newApplication(cl, appClass, appContext);
    
  • 设置ContextImpl的context类型的mOuterContext变量为之前创建的Application类型app对象,让ContextImpl保持对Application的引用

    appContext.setOuterContext(app);
    
  • 将创建Application对象app保存为LoadedApk的成员变量mApplication,用来表示ApplicationContext

    mApplication = app;
    

接下来来到Instrumentation的newApplication方法,该方法有两个重载方法

public Application newApplication(ClassLoader cl, String className, Context context)
            throws InstantiationException, IllegalAccessException, 
            ClassNotFoundException {
		//使用传递进来的类加载器,加载className参数的类然后将该类和context作为参数传递到下一个重载方法
        return newApplication(cl.loadClass(className), context);
    }

static public Application newApplication(Class<?> clazz, Context context)
            throws InstantiationException, IllegalAccessException, 
            ClassNotFoundException {
        //实例化加载器生成的类,并强制转换为Application类型
        Application app = (Application)clazz.newInstance();
		//将ContextImpl传进去
        app.attach(context);
        return app;
    }

调用了Application的attach方法,并将ContextImpl作为参数传递进去

final void attach(Context context) {
        attachBaseContext(context);
        mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
    }

在Application.attach方法中调用了attachBaseContext方法,该方法在Application的父类ContextWrapper类中实现

protected void attachBaseContext(Context base) {
        if (mBase != null) {
            throw new IllegalStateException("Base context already set");
        }
        mBase = base;
    }

base指的就是ContextImpl,是Context的实现类

将ContextImpl赋值给ContextWrapper的成员变量mBase,这样就可以在ContextWrapper中使用Context的方法

Application继承于ContextWrapper,同样也可以使用Context方法

Application的attach方法的作用就是使Application可以使用Context方法,这样Application可以用来代表ApplicationContext

Application Context的获取过程

我们已经了解了Application Context的创建过程,其获取过程也很好理解

我们使用getApplicationContext方法来获得Application Context ,该方法在ContextWrapper中实现

@Override
public Context getApplicationContext() {
        return mBase.getApplicationContext();
    }

base是ContextImpl,我们来到ContextImpl.getApplicationContext方法中

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

如果LoadedApk类型的mPackageInfo对象不null,那就返回LoadedApk.getApplication,不然返回ActivityThread.getApplication

由于程序已经启动了,LoadedApk不会为null,所以调用了LoadedApk.getApplication()方法

Application getApplication() {
        return mApplication;
    }

该方法返回了之前makeApplication方法中赋值了Context的全局变量mApplication,于是便获得了Context

Activity Context的创建过程

想要在Activity中使用Context提供的方法,首先需要创建Context,Activity的Context会在Activity启动的过程中被创建

Activity的Context创建的时序图

sequenceDiagram
	调用 ->> ApplicationThread : scheduleLaunchActivity
	activate ApplicationThread
	ApplicationThread ->> ActivityThread : sendMessage
	deactivate ApplicationThread
	activate ActivityThread 
	ActivityThread ->> H : handleMessage
	deactivate ActivityThread
	activate H
	H ->> ActivityThread : handleLaunchActivity
	deactivate H
	activate ActivityThread
	ActivityThread ->> ActivityThread : proformLaunchActivity 
	ActivityThread ->> Activity : attach
	deactivate ActivityThread
	activate Activity
	Activity ->> ContextThemeWrapper : attachBaseContext
	deactivate Activity
	activate ContextThemeWrapper
	ContextThemeWrapper ->> ContextWrapper : attachBaseContext
	deactivate ContextThemeWrapper
	activate ContextWrapper
	deactivate ContextWrapper

ActivityThread是应用程序进程的主线程管理类,其内部类ApplicationThread会调用scheduleLaunchActivity方法来启动Activity

  • scheduleLaunchActivity方法将启动Activity的参数封装为ActivityClientRecord对象

    ActivityClientRecord r = new ActivityClientRecord();
    
  • 使用sendMessage向H类发送H.LAUNCH_ACTIVITY类型的消息,并且将ActivityClientRecord对象传进去

    sendMessage(H.LAUNCH_ACTIVITY, r);
    

    该方法的主要目的是将启动Activity的逻辑放在主线程中执行

    H类的HandleMessgae方法会对LAUNCH_ACTIVITY类型的消息进行处理,消息处理时的调用链为

    flowchart LR
    	H.handleMessage --> ActivityThread.handleLaunchActivity
    	ActivityThread.handleLaunchActivity --> ActivityThread.performLaunchActivity
    

接下来来到ActivityThread.performLaunchActivity方法

  • 使用createBaseContextForActivity方法创建Activity的ContextImpl

    ContextImpl appContext = createBaseContextForActivity(r);
    
  • 创建Activity的实例

    activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
    
  • 将创建的Activity设置给ContextImpl的mOuterContext变量,让ContextImpl也有访问Activity的能力

    appContext.setOuterContext(activity);
    
  • 将ContextImpl传入Activity的attach方法

    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);
    
  • 调用mInstrumentation的callActivityOnCreate方法从而调用Activity的Create方法

    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
    

查看performLaunchActivity方法中创建Activity的ContextImpl的createBaseContextForActivity方法发现,该方法调用了ContextImpl的createActivityContext方法来创建ContextImpl

 ContextImpl appContext = ContextImpl.createActivityContext(
                this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig);

接下来回到performLaunchActivity方法中的调用activity.attach方法

  • 调用attachBaseContext方法,该方法在ContextThemeWrapper中实现

    attachBaseContext(context);
    
  • 创建PhoneWindow,也就是应用程序窗口,PhoneWindow在运行的时候会间接触发很多事件,点击或屏幕焦点变化,这些事件需要转发给和PhoneWindow关联的Activity

    mWindow = new PhoneWindow(this, window, activityConfigCallback);
    
  • 将当前Activity通过setCallback设置给PhoneWindow

    mWindow.setCallback(this);
    
  • 为PhoneWindow设置WindowManager

    mWindow.setWindowManager(
                    (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                    mToken, mComponent.flattenToString(),
                    (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
    
  • 获得WIndowManager并且赋值给Activity的成员变量mWindowManager

    mWindowManager = mWindow.getWindowManager();
    

接下来来到attachBaseContext方法中,该方法调用的是ContextThemeWrapper.attachBaseContext

@Override
protected void attachBaseContext(Context newBase) {
        super.attachBaseContext(newBase);
    }

这个方法调用的是其父类的同名方法

protected void attachBaseContext(Context base) {
        if (mBase != null) {
            throw new IllegalStateException("Base context already set");
        }
        mBase = base;
    }

这里的base就是一路传进来的Activity的ContextImpl

赋值给了ContextWrapper的变量mBase,这样ContextWrapper的功能就可以交给ContextImpl来处理

比如调用Activity的getResources方法,就是调用了ContextImpl的getResources方法

@Override
public Resources getResources() {
        return mBase.getResources();
    }

ActivityContext的创建过程可以总结为以下几点

  1. 启动Activity的过程中创建ContextImpl
  2. 将创建的ContextImpl赋值给ContextWrapper的成员变量mBase
  3. Activity继承自ContextWrapper的子类ContextThemeWrapper,这样Activity中就可以使用Context中定义的方法

Service的Context创建过程

Service的Context创建过程和Activity的创建过程类似,是在Service的启动过程中被创建,我们从ActivityThread.ApplicationThread.scheduleCreateService方法开始解析

public final void scheduleCreateService(IBinder token,
                ServiceInfo info, CompatibilityInfo compatInfo, int processState) {
            updateProcessState(processState, false);
            CreateServiceData s = new CreateServiceData();
            s.token = token;
            s.info = info;
            s.compatInfo = compatInfo;
            sendMessage(H.CREATE_SERVICE, s);
        }

使用sendMessage方法向H类发送CREATE_SERVICE类型的消息,H类的handleMessage会对CREATE_SERVICE类型的消息进行处理

case CREATE_SERVICE:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, ("serviceCreate: " + String.valueOf(msg.obj)));
                    handleCreateService((CreateServiceData)msg.obj);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;

处理消息逻辑调用了handleCreateService((CreateServiceData)msg.obj);这个方法

  • 使用ContextImpl的CreateAppContext方法创建ContextImpl

    ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
    
  • 将创建的ContextImpl传入service的attach方法中

    service.attach(context, this, data.info.name, data.token, app,ActivityManager.getService());
    

Service.attach方法如下所示

public final void attach(
            Context context,
            ActivityThread thread, String className, IBinder token,
            Application application, Object activityManager) {
    	//调用ContextWrapper的attachBaseContext方法
        attachBaseContext(context);
        mThread = thread;           // NOTE:  unused - remove?
        mClassName = className;
        mToken = token;
        mApplication = application;
        mActivityManager = (IActivityManager)activityManager;
        mStartCompatibility = getApplicationInfo().targetSdkVersion
                < Build.VERSION_CODES.ECLAIR;
    }

在该方法中调用了ContextWrapperattachBaseContext方法,如下所示

protected void attachBaseContext(Context base) {
        if (mBase != null) {
            throw new IllegalStateException("Base context already set");
        }
        mBase = base;
    }

这里的base是一直传进来的ContextImpl,将ContextImpl赋值给ContextWrapper的成员变量mBase,这样就可以在ContextWrapper中使用Context的方法,Service继承于ContextWrapper,也可以使用Context方法

小结

本篇文章学习了

  • Context的关联类
  • Application,Activity,Service的Context创建过程

本篇文章让我更好地理解Context的关联类的设计理念