Android四大组件系列11 深入理解Context

深入理解Android Context

一 概述

Context 相信几乎所有的 Android 开发人员基本上都非常熟悉,因为它太常见了。有大量的场景都离不开 Context,下面列举部分常见场景:
在这里插入图片描述

Context,字面意思:语境、环境、上下文,在 Android 系统中,可以理解为当前对象在应用程序中所处的工作环境。其内部定义很多访问应用程序环境中全局信息的接口,通过它可以访问到应用程序的资源有关的类,如:Resources、AssetManager、Package 及权限相关信息等。还可以通过它调用应用程序级的操作,如:启动 Activity 和 Service、发送广播等。

/**
 * Interface to global information about an application environment.  This is
 * an abstract class whose implementation is provided by
 * the Android system.  It
 * allows access to application-specific resources and classes, as well as
 * up-calls for application-level operations such as launching activities,
 * broadcasting and receiving intents, etc.
 */
public abstract class Context {
   
   ...}

翻译:Context 提供了关于应用环境全局信息的接口。它是一个抽象类,它的执行被 Android 系统所提供。它允许获取以应用为特征的资源和类型,是一个统领一些资源(应用程序环境变量等)的上下文。

1.1 Context 结构

Context 是维持 Android 程序中各组件能够正常工作的一个核心功能类。

Context 本身是一个抽象类,其主要实现类为 ContextImpl,另外有直系子类两个:

  • ContextWrapper
  • ContextThemeWrapper

这两个子类都是 Context 的代理类,它们继承关系如下:
在这里插入图片描述

  • Context 是一个抽象类,定义一系列与系统交互的接口
  • ContextImpl 继承自 Context 抽象类,实现了 Context 类中的抽象方法,是 Context 类的具体实现类。它为 Activity 及其它应用组件提供上下文环境,应用中使用到的 Context 的方法就是其实现的
  • ContextWrapper 继承自 Context 抽象类,是 Context 类的包装类(装饰器模式),内部维护一个 Context 类型的成员变量 mBase 指向一个 ContextImpl 对象,ContextWrapper 里面的方法调用是通过 mBase 来调用 ContextImpl 里面的方法,这里用到了代理模式
  • ContextThemeWrapper继承自 ContextWrapper 类,在 ContextWrapper 的基础上增加与主题 Theme 相关的逻辑,即可以指定 Theme 的 Context 包装类,用于在 View 构造时为其提供 Theme 属性集

通过 Context 的继承关系图并结合我们开发中比较熟悉的类:Activity、Service、Application,所以我们可以认为 Context 一共有三种类型,分别是 Application、Activity 和 Service,他们分别承担不同的作用,但是都属于 Context,而他们具有 Context 的功能则是由 ContextImpl 类实现的。

简单流程是:Application,Activity 或 Service 通过 attach() 调用父类 ContextWrapper 的 attachBaseContext(),从而设置父类成员变量 mBase 为 ContextImpl 对象,从而核心的工作都交给 ContextImpl 来处理。

ContextImpl 实现类中涉及的主要核心类是:ActivityThread、LoadedApk、PackageManager 和
ResourcesManager,这几个类都是单例的,一个应用程序进程中是共用同一个对象的。
Contextlmpl 是一种轻量级类,而 LoadedApk 是一个重量级类,Contextlmpl 中的大多数进行包操作的重量级函数实际上都是转向了 LoadedApk
对象相应的方法。

Activity 继承自 ContextThemeWrapper,Application、Service 继承自
ContextWrapper,它们直接或间接的继承自 ContextWrapper 类,因此也拥有了一个 Context 类型的成员变量
mBase 指向一个 ContextImpl 对象,ContextImpl 是 Context 类的具体实现类,所以也都拥有了
Context 提供的所有功能。

代理模式:属于结构型模式,是指为其他对象提供一种代理以控制对这个对象的访问,代理模式又分为静态代理和动态代理。
装饰器模式:又叫包装模式,也是结构型模式,是指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式。

1.2 Context数量

根据上面的 Context 类型我们可以知道。Context 一共有 Application、Activity 和 Service 三种类型,因此在一个应用程序中 Context 数量的计算公式可以这样写:

  • Context 数量 = Activity 数量 + Service 数量 + 1

上面的1代表着 Application 的数量,因为一个应用程序中可以有多个 Activity 和多个 Service,但是只能有一个 Application。

二 Context 详解

前面讲到 Context 的体系结构时,了解到其最终实现类有:Application、Service 和 Activity,它们都持有 ContextImpl 这个 Context 抽象类的真正实现,接下来对这三个实现类分别进行讨论分析。

2.1 Application Context

Application 是 Android 系统框架中的一个系统组件,当 Android 应用程序启动时系统会创建一个 Application 类的对象且只创建一个,用来存储系统的一些信息,即 Application 是单例的。

通常在开发过程中是不需要指定一个 Application 的,系统自动帮开发者创建,如果要创建应用自定义的 Application,只需创建一个类继承 Application 并在 AndroidManifest.xml 文件中的 application 标签中进行注册(只需给 application 标签增加 name 属性,并添加自定义的 Application 的名字即可)。

通常自定义 Application 的目的是在应用程序启动时做一些全局的初始化工作,当应用程序启动时,Application 同步创建并启动,系统会创建⼀个 PID,即进程ID,所有的 Activity 都会在此进程上运⾏,因此都可以取到这些初始化的全局变量的值,且由于 Application 对象在整个应用程序运行期间会一直存在,有开发者就会在 Application 中编写一些工具方法,全局获取使用,但是切记不要这样把 Application 当工具类使用。注意:这严重违背 Google 设计 Application 的原则,也违背设计模式中的单一职责原则。

2.1.1 自定义 Application 实例

open class TestApplication : Application() {
   
   
    // 全局 context
    companion object{
   
   
        lateinit var context: Context
    }
    override fun onCreate() {
   
   
        super.onCreate()
        context = this
        initSDKs() // 全局初始化
    }

    private fun initSDKs() {
   
   ...}
}

继承 Application 并重写 onCreate() 方法,在 Application 创建的时候调用,一般用于全局初始化,如第三方 SDK 的初始化、环境的配置等等,同时可以通过 TestApplication # context 来获取 Application 类型的全局 Context 对象。

2.1.2 获取 Application 实例

class TestActivity : Activity() {
   
   
    private val TAG: String = TestActivity::class.java.simpleName
    override fun onCreate(savedInstanceState: Bundle?) {
   
   
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_test)

        val applicationContext = TestActivity@this.getApplicationContext()
        val application = TestActivity@this.getApplication()

        Log.e(TAG, "application: $application")
        Log.e(TAG, "applicationContext: $applicationContext")
    }
}

获取 Application 的方法一般有两个:

Activity # getApplication() 或 Service # getApplication()
Context # getApplicationContext()
通过 getApplication() 和 getApplicationContext() 都可以获取到 Application,那它们区别是什么呢?

通过日志输出可以看到,它们获取到的是同一个对象,但有同学要问了,那为什么还要提供两个功能一样的方法?因为 getApplication() 方法更加直观,但只能在 Activity 和 Service 场景中调用。getApplicationContext() 方法适用范围更广,任意场景中通过 Context 对象皆可以调用此方法。

2.1.3 Application Context 创建过程

Application 的 Context 是在应用被创建的时候创建的,要追踪其创建需要从应用程序的启动流程出发来探索,即从点击桌面应用图标开始到应用第一个界面展示出来的过程中的某一步,具体哪一步创建的,可以之前应用进程的启动分析。

简述一下过程:

  1. ActivityThread 类作为应用初始化类,在其入口方法 main() 方法中调用 ActivityThread # attach() 方法中,然后通过 Binder 通信跨进程调用到 system_server 进程中 AMS 的 attachApplication() 方法,并将 ApplicationThread 作为参数传递过去。
  2. 通过传进来的 ApplicationThread,跨进程通信调用应用进程中 ApplicationThread # bindApplication() 方法绑定 Application。
  3. ApplicationThread # bindApplication() 方法中,构建 AppBindData 对象,然后通过内部类 H 发送 BIND_APPLICATION 类型的 Handler 消息,进而调用到 ActivityThread # handleBindApplication() 方法创建并绑定 Application。

2.1.4 时序图

在这里插入图片描述

2.1.5 源码解析

通过过程简述与时序图可知,Application 的 Context 的创建是在 ActivityThread # handleBindApplication() 方法中创建的,跟踪查看源码进行详细解析。

2.1.5.1 ActivityThread # handleBindApplication()
ActivityThread.class (api 30)
public final class ActivityThread extends ClientTransactionHandler
        implements ActivityThreadInternal {
   
   
    ......
 	@UnsupportedAppUsage
    private void handleBindApplication(AppBindData data) {
   
   
		......
        final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
        updateLocaleListFromAppContext(appContext,
                mResourcesManager.getConfiguration().getLocales());
        ......
        if (ii != null) {
   
   
            ......
            final LoadedApk pi = getPackageInfo(instrApp, data.compatInfo,
                    appContext.getClassLoader(), false, true, false);
            final ContextImpl instrContext = ContextImpl.createAppContext(this, pi,
                    appContext.getOpPackageName());
            try {
   
   
            	// 获取 ClassLoader 加载类文件
                final ClassLoader cl = instrContext.getClassLoader();
                // 获取 Instrumentation 类并构建实例对象
                mInstrumentation = (Instrumentation)
                    cl.loadClass(data.instrumentationName.getClassName()).newInstance();
            }
			......
            final ComponentName component = new ComponentName(ii.packageName, ii.name);
            mInstrumentation.init(this, instrContext, appContext, component,
                    data.instrumentationWatcher, data.instrumentationUiAutomationConnection);
			......
        } 
        ......
        Application app;
		......
        try {
   
   
        	// 创建 Application
            app = data.info.makeApplication(data.restrictedBackupMode, null);
			......
            mInitialApplication = app;
			......
            try {
   
   
                mInstrumentation.onCreate(data.instrumentationArgs);
            }
            ......
            try {
   
   
           		// 内部调用 Application # onCreate() 的方法
                // 故 Application # onCreate() 比 ActivityThread 的 main() 方法慢执行
                // 但是会比所有该应用 Activity 的生命周期先调用,因为此时的 Activity 还没启动
                mInstrumentation.callApplicationOnCreate(app);
            }
            ......
        }
    }
    ......
}

ActivityThread # handleBindApplication() 方法的参数 AppBindData 是 AMS 传给应用程序的启动信息,其中包含 LoadedApk、ApplicationInfo 等,然后通过 LoadedApk 实例对象创建 ContextImpl 和 Application 实例对象。

2.1.5.2 LoadedApk # makeApplication()
LoadedApk.java  (api 30)
public final class LoadedApk {
   
   
	......
   @UnsupportedAppUsage
    public Application makeApplication(boolean forceDefaultAppClass,
            Instrumentation instrumentation) {
   
   
        if (mApplication != null) {
   
   
       		// 如果 mApplication 已经存在则直接返回
            return mApplication;
        }
		......
        Application app = null;
        // 获取 AndroidMenifest 中 application 标签指定的 Application 类
        String appClass = mApplicationInfo.className;
        if (forceDefaultAppClass || (appClass == null)) {
   
   
            appClass = "android.app.Application";
        }

        try {
   
   
        	// 获取 ClassLoader
            final java.lang.ClassLoader cl = getClassLoader();
            ......
            // 创建 ContextImpl 实例
            ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
            ......
            // 利用类加载器 ClassLoader 创建 AndroidMenifest 指定的 Application 类
            app = mActivityThread.mInstrumentation.newApplication(
                    cl, appClass, appContext);
			// 将 Application 实例赋值给 ContextImpl,以便 ContextImpl 可以访问 Application
            appContext.setOuterContext(app);
        }
        ......
        mActivityThread.mAllApplications.add(app);
        // app 赋值给 mApplication,当我们调用 Context.getApplicationContext() 就是获取这个对象
        mApplication = app;
        if (instrumentation != null) {
   
   
            try {
   
   
            	// 由于 instrumentation 此时为空所以不会回调 Application 的 onCreate 方法
                instrumentation.callApplicationOnCreate(app);
            }
            ......
        }
		......
        return app;
    }
    ..
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值