APP 开发中,我们需要使用 app 的资源,比如文字、图片,Activity、Service或者 broadcastReceiver 等等。时常也会用到 getApplicationContext() 来获取一个 Context 对象。那么这个 Context 到底是什么呢?

Context 类简介

context 含义有语境,上下文,背景,环境等等。 Context 是维持 Android 程序中各组件能够正常工作的一个核心功能类。

Context 是一个抽象类。它是一个 app 全局环境的“接口”,由 Android 系统提供继承类(例如 Activity、Service、Application 等)。它能连接到应用的资源,也能使用应用级的操作,比如启动 activity,广播和接收 intent。

应用程序中Context的总数目为: 总 Context 个数 = Activity 个数 + Service个数 + 1(Application Context)

Context 的子类

简单的继承关系示意

Context
├── ContextImpl
└── ContextWrapper
    ├── Application
    ├── ContextThemeWrapper
    │   └── Activity
    └── Service

从继承关系图中可以看出,Application 类、Service 类和 Activity 类都继承了Context 类。应用程序被启动后,会为应用程序创建一个全局的 Application 对应的 Context 对象。ContextImpl 类是 Context 的真正实现。

ContextWrapper 类是封装类。可以在不改变 ContextImpl 的情况下,为其增加一些自定义操作。ContextWrapper 中的 mBase 实际上是一个 ContextImpl对象。而 ContextImpl 类中的 mOuterContext 是一个 Context 对象,指向相对应的 Activity 或 Service 或 Application。

ContextImpl

Context 是一个抽象类,子类 ContextImpl 实现了Context的方法;为Activity和其他应用组件提供基本的context对象。 ContextImpl.java (frameworks\base\core\java\android\app)

/**
 * Common implementation of Context API, which provides the base
 * context object for Activity and other application components.
 */
class ContextImpl extends Context { /*...*/ }

ContextWrapper

Wrapper 有封装的意思;ContextWrapper 是 Context 的封装类 。这里使用了装饰者模式,构造方法中传入了一个 Context 实例。ContextWrapper 持有 ContextImpl 对象。可以在不改变 ContextImpl 的情况下增加一些操作。

ContextWrapper.java (frameworks\base\core\java\android\content)
/**
 * Proxying implementation of Context that simply delegates all of its calls to
 * another Context.  Can be subclassed to modify behavior without changing
 * the original Context.
 */
public class ContextWrapper extends Context {
    Context mBase;

    public ContextWrapper(Context base) {
        mBase = base;
    }
    // ...
}

具体操作中,Application 类、Activity 和 Service 类与 ContextImpl 产生交集。

ContextThemeWrapper

允许在封装的 context 中修改主题(theme)

ContextThemeWrapper.java (frameworks\base\core\java\android\view)
/**
 * A ContextWrapper that allows you to modify the theme from what is in the
 * wrapped context.
 */
public class ContextThemeWrapper extends ContextWrapper { /* ... */ }

其中提供了关于 theme 的方法,app 开发中 android:theme 与此有关。相同的代码,相同的调用,使用不同的 theme 会有不同的效果。

getApplicationContext() 和 getBaseContext()

public class ContextWrapper extends Context {
    Context mBase;
    ......
    @Override
    public Context getApplicationContext() {
        return mBase.getApplicationContext();
    }
    ......
    /**
     * @return the base context as set by the constructor or setBaseContext
     */
    public Context getBaseContext() {
        return mBase;// Don't use getBaseContext(), just use the Context you have.
    }
    ......
}
getApplicationContext() = android.app.Application@39d42b0e
getBaseContext() = android.app.ContextImpl@1f48c92f

getApplicationContext() 从 application 取得 context。getBaseContext() 从实现类 ContextImpl 那得来。

Context 子类创建流程

Application的创建流程

我们把关注点先放在 application 上,暂时忽略源码中的其他信息。

流程描述:

LoadedApk 先通过 classLoaderloadClass(className) 获取application 的 class,再通过 clazz.newInstance() 创建 Application 实例。接着调用 app.attach(context) 方法完成初始化。

Application 的 attach 方法里调用了自己的 attachBaseContext(context), 把第一步创建的 ContextImpl 实例赋值给 ContextWrappermBase 成员变量。 到此 Application 实例创建就完成了。

Activity的创建 - performLaunchActivity

这里关注的是 Activity 的实例化以及之前的一些准备过程。

流程简析:

主要关注 ActivityThreadperformLaunchActivity 方法。通过ContextImplcreateActivityContext 获得一个 ContextImpl 实例,称为 appContextInstrumentationnewActivity 返回了一个 Activity 实例。

LoadedApk.makeApplication 可以获得当前的 application,如果当前没有则新建一个 application。最后通过 ContextImpl.setOuterContext 和 Activity 的attach 方法,将 ContextImpl 实例与 Activity 实例关联到一起。

使用

自定义Application

新建一个 MyApplication 类,继承自 Application

import android.app.Application;
import android.content.Context;

public class MyApplication extends Application {
    private static Context context;

    public static Context getMyContext() {
        return context;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        context = getApplicationContext();
    }
}

在Manifest中使用MyApplication;

    <application
        android:name="com.rust.aboutview.MyApplication"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
    ......

即可在不同的地方调用 getMyContext() 方法

MyApplication.getMyContext()

Android 零基础入门教程视频参考