深入理解Context
- 深入理解Context【收费】
 
概述
Context作用
- 四大组件相关
- 绑定/开启 
Service - 启动
Activity - 获取
ContentResolver - 注册/发送广播
 
 - 绑定/开启 
 - 检查权限
 - 文件/
SharedPreferences相关 - 获取
Resource相关 - 文件缓存目录/数据库相关
 - 获取各种
SystemService 
Context,字面意思:语境、环境、上下文,在 Android 系统中,可以理解为当前对象在应用程序中所处的工作环境。其内部定义很多访问应用程序环境中全局信息的接口,通过它可以访问到应用程序的资源有关的类,如:Resources、AssetManager、Package 及权限相关信息等。还可以通过它调用应用程序级的操作,如:启动 Activity 和 Service、发送广播等。  
1  | /**  | 
翻译:Context 提供了关于应用环境全局信息的接口。它是一个抽象类,它的执行被 Android 系统所提供。它允许获取以应用为特征的资源和类型,是一个统领一些资源(应用程序环境变量等)的上下文。
Context结构
Context 是维持 Android 程序中各组件能够正常工作的一个核心功能类。Context 本身是一个抽象类,其主要实现类为 ContextImpl,另外有直系子类两个:
ContextWrapperContextThemeWrapper
这两个子类都是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 提供的所有功能。
代理模式:属于结构型模式,是指为其他对象提供一种代理以控制对这个对象的访问,代理模式又分为静态代理和动态代理。
装饰器模式:又叫包装模式,也是结构型模式,是指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式。
Context 数量
根据上面的 Context 类型我们可以知道。Context 一共有 Application、Activity 和 Service 三种类型,因此在一个应用程序中 Context 数量的计算公式可以这样写:
1  | Context 数量 = Activity 数量 + Service 数量 + 1  | 
上面的 1 代表着 Application 的数量,因为一个应用程序中可以有多个 Activity 和多个 Service,但是只能有一个 Application。
Context 详解
前面讲到 Context 的体系结构时,了解到其最终实现类有:Application、Service 和 Activity,它们都持有 ContextImpl 这个 Context 抽象类的真正实现,接下来对这三个实现类分别进行讨论分析。
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 的原则,也违背设计模式中的单一职责原则。
自定义 Application 实例
1  | open class TestApplication : Application() {  | 
继承 Application 并重写 onCreate() 方法,在 Application 创建的时候调用,一般用于全局初始化,如第三方 SDK 的初始化、环境的配置等等,同时可以通过 TestApplication # context 来获取 Application 类型的全局 Context 对象。
获取 Application 实例
1  | class TestActivity : Activity() {  | 
获取 Application 的方法一般有两个:
Activity # getApplication()或Service # getApplication()Context # getApplicationContext()
通过getApplication()和getApplicationContext()都可以获取到Application,那它们区别是什么呢?
通过日志输出可以看到,它们获取到的是同一个对象,但有同学要问了,那为什么还要提供两个功能一样的方法?因为 getApplication() 方法更加直观,但只能在 Activity 和 Service 场景中调用。getApplicationContext() 方法适用范围更广,任意场景中通过 Context 对象皆可以调用此方法。
Application Context 创建过程
Application 的 Context 是在应用被创建的时候创建的,要追踪其创建需要从应用程序的启动流程出发来探索,即从点击桌面应用图标开始到应用第一个界面展示出来的过程中的某一步,具体哪一步创建的,可以之前应用进程的启动分析。
简述一下过程:
ActivityThread类作为应用初始化类,在其入口方法main()方法中调用ActivityThread # attach()方法中,然后通过Binder通信跨进程调用到system_server进程中AMS的attachApplication()方法,并将ApplicationThread作为参数传递过去。- 通过传进来的 
ApplicationThread,跨进程通信调用应用进程中ApplicationThread # bindApplication()方法绑定Application。 ApplicationThread # bindApplication()方法中,构建AppBindData对象,然后通过内部类 H 发送BIND_APPLICATION类型的Handler消息,进而调用到ActivityThread # handleBindApplication()方法创建并绑定Application。
时序图


源码解析
通过过程简述与时序图可知,Application 的 Context 的创建是在 ActivityThread # handleBindApplication() 方法中创建的,跟踪查看源码进行详细解析。
ActivityThread # handleBindApplication()
1  | ActivityThread.class (api 30)  | 
ActivityThread # handleBindApplication() 方法的参数 AppBindData 是 AMS 传给应用程序的启动信息,其中包含 LoadedApk、ApplicationInfo 等,然后通过 LoadedApk 实例对象创建 ContextImpl 和 Application 实例对象。
LoadedApk # makeApplication()
1  | LoadedApk.java (api 30)  | 
执行流程如下:
- 首先判断 
LoadedApk对象中的mApplication是否存在,如果已经存在则直接返回。如果不存在,则先获取AndroidMenifest中application标签中name属性指定的Application类名。然后获取ClassLoader,创建ContextImpl实例。 - 通过类加载器 
ClassLoader创建Application类实例,并将Application实例赋值给ContextImpl的成员变量mOuterContext,以便ContextImpl通过mOuterContext访问Application实例。同时将Application实例赋值给mApplication,调用Context # getApplicationContext()方法获取的就是该实例对象。 
ContextImpl 创建
1  | ContextImpl.java (api 30)  | 
创建一个 ContextImpl 实例对象,同时给 ContextImpl 赋值访问系统资源相关的 “权限” 对象 – ActivityThread、LoadedApk 等。
Application 创建
回头继续看 LoadedApk # makeApplication() 的 Application 类实例的创建,代码如下:
1  | Instrumentation.java (api 30)  | 
获取 LoadedApk 的 AppComponentFactory,然后通过 ClassLoader 加载 Application 类,并创建类的实例对象。接下来将 ContextImpl 实例赋值给创建的 Application 实例对象的 mBase 成员变量。
Application 绑定 ContextImpl
1  | Application.java (api 30)  | 
1  | ContextWrapper.java (api 30)  | 
在 Application 的 attach() 方法中调用 ContextWrapper 的 attachBaseContext() 方法,将 ContextImpl 实例对象赋值给其成员变量 mBase。
Service Context
Service 是 Android 系统框架中四大组件的其中之一,它是一种可以在后台执行长时间运行操作而没有用户界面的应用组件。可由其他应用组件启动(如:Activity),服务一旦被启动将在后台一直运行,即使启动服务的组件(Activity)已销毁也不受影响,接下来分析其 Context 实例的由来。
Service Context 创建过程
Service 的 Context 是在 Service 被其他应用组件启动的时候创建的(如:Activity 中启动),这里不详细分析 Service 的启动过程,也不是本篇文章的重点。具体可以参考之前针对 Service 的启动过程分析。
简述一下过程:
- 通常都是调用 
Context # startService()方法启动Service,通过上面的分析可知,这里将调用其实现类ContextImpl # startService()方法,然后通过Binder通信跨进程调用到system_server进程中AMS的startService()方法,在系统进程中经过一系列调用后,流程走到ApplicationThread的scheduleCreateService()方法,在方法中将AMS进程中创建的ServiceInfo等封装成CreateServiceData对象。 ApplicationThread # scheduleCreateService()方法中将CreateServiceData实例对象通过内部类H发送CREATE_SERVICE类型的Handler消息,进而调用到ActivityThread # handleCreateService()方法创建Service,同时创建Service的Context。
时序图

源码解析
通过过程简述与时序图可知,Service 的 Context 的创建是在 ActivityThread # handleCreateService() 方法中创建的,跟踪查看源码进行详细解析。
ActivityThread # handleCreateService()
1  | ActivityThread.class (api 30)  | 
执行流程如下:
- 获取 
LoadedApk实例对象,用于获取类加载器ClassLoader等,然后创建一个ContextImpl实例对象,即Service的上下文环境,同时给ContextImpl赋值访问系统资源相关的 “权限” 对象 –ActivityThread、LoadedApk等。 - 获取类加载器 
ClassLoader实例对象,并使用它加载Service类并创建实例对象,将创建的Service实例赋值给ContextImpl的成员变量mOuterContext,以便ContextImpl通过mOuterContext访问Service实例。 - 调用 
Service # attach()方法将ContextImpl实例绑定到Service实例对象的mBase成员变量,然后回调Service # onCreate()方法,最后通过跨进程调用AMS # serviceDoneExecuting()方法通知AMS,Service已启动完毕。 
Service 绑定 ContextImpl
1  | Service.java (api 30)  | 
1  | ContextWrapper.java (api 30)  | 
在 Service 的 attach() 方法中继续调用 Service # attachBaseContext() 方法,然后继续调用父类 ContextWrapper # attachBaseContext() 方法将 ContextImpl 实例对象赋值给成员变量 mBase。
Activity Context
Activity 是 Android 系统框架中四大组件中使用频率最多的,是用来给用户展示内容的界面,并与用户直接进行交互的组件。日常开发中 Activity 的 Context 会被经常用到,如通过 Context 启动新的 Activity、启动 Service 及注册广播接收器等,下面一起来看一下 Activity 的 Context 实例的由来。
Activity Context 创建过程
Activity 的 Context 是在 Activity 组件启动的时候创建的,这里不详细分析 Activity 的启动过程,也不是本篇文章的重点,感兴趣的同学可以参考之前分析 Activity 的启动过程。
简述一下过程:
- 通常是调用 
Activity # startActivity()方法来启动Activity,然后通过Binder通信跨进程调用到system_server进程中ATMS的startActivity()方法,在系统进程中经过一系列调用后,流程走到ApplicationThread的scheduleTransaction()方法。 ApplicationThread # scheduleTransaction()方法中根据生命周期状态,来调度启动Activity的事务LaunchActivityItem,在LaunchActivityItem # execute()方法中调用到ActivityThread # handleLaunchActivity()方法来创建并启动Activity,同时创建Activity的Context。
Android P(9.0) 开始 Activity 启动及生命周期有关的逻辑,被解耦成多个 Transaction 事务(如:LaunchActivityItem、ResumeActivityItem 等),通过 ClientLifecycleManager 来调度事务的执行。
时序图

源码解析
通过过程简述与时序图可知,Activity 的 Context 的创建是在 ActivityThread # handleLaunchActivity() 方法中创建的,跟踪查看源码进行详细解析。
ActivityThread # handleLaunchActivity()
1  | ActivityThread.java (api 30)  | 
继续调用 ActivityThread # performLaunchActivity() 执行 Activity 的启动,继续跟踪启动流程。
ActivityThread # performLaunchActivity()
1  | ActivityThread.java (api 30)  | 
执行流程如下:
- 获取 
LoadedApk实例对象,用于获取类加载器ClassLoader等,继续调用ActivityThread # createBaseContextForActivity()方法,该方法中调用ContextImpl # createActivityContext()方法创建ContextImpl实例对象,即Activity的上下文环境Context。 - 调用 
Instrumentation # newActivity()方法加载并新建Activity实例对象,该方法中调用AppComponentFactory # instantiateActivity()方法,然后通过在ActivityThread # performLaunchActivity()方法中获取的类加载器ClassLoader加载并新建Activity实例对象。 - 通过 
LoadApk # makeApplication()方法创建一个Application对象,过程跟加载并新建Activity类似,用到类加载器ClassLoader。 - 执行 
Activity # attach()方法,通过该方法将ContextImpl实例设置给Activity,除此之外,方法中还完成了 Window 实例的创建并建立自己和 Window 的关联,这样当 Window 接收到外部输入事件后就可以将事件传递给Activity。 - 执行 
Instrumentation # callActivityOnCreate()方法,该方法中调用Activity # performCreate()方法,Activity # performCreate()方法中调用Activity # onCreate()方法。 
ActivityThread # createBaseContextForActivity()
1  | ActivityThread.java (api 30)  | 
1  | ContextImpl.java (api 30)  | 
只看主流程有关源代码,调用 ContextImpl # createActivityContext() 创建 Activity 的 Context。
至于主流程中 Activity 和 Application 的加载并新建过程感兴趣的可以跟进源码查看,主要是由类加载器 ClassLoader 加载后新建实例对象,下面主要来查看 Activity 绑定 Context 的流程。
Activity # attach()
1  | ContextImpl.java (api 30)  | 
1  | ContextThemeWrapper.java (api 30)  | 
1  | ContextWrapper.java (api 30)  | 
在 Activity 的 attach() 方法中继续调用 Activity # attachBaseContext() 方法,然后继续调用父类 ContextThemeWrapper # attachBaseContext() 方法,由于 ContextThemeWrapper 继承自 ContextWrapper,因此继续调用 ContextWrapper # attachBaseContext() 方法将 ContextImpl 实例对象赋值给成员变量 mBase。
总结
通过源码的深入分析可知,Application、Service 和 Activity 直接或间接的继承自 ContextWrapper 类,因此也都拥有了一个 Context 类型的成员变量 mBase 指向一个 ContextImpl 对象,ContextImpl 是 Context 类的具体实现类,所以它们也就都拥有了 Context 提供的获取应用环境全局信息的接口功能。
Context 补充知识
前面的章节分析了 Application、Service 和 Activity 等组件的创建以及绑定 ContextImpl 实例对象的流程,有同学会问了,四大组件中的 BroadcastReceiver 和 ContentProvider 创建的过程中没有绑定 ContextImpl 实例对象吗?
其实它们也有绑定的,只不过不是自身继承自 Context,其 Context 实例是需要通过前面所述三者来提供,大致来看一下源码。
BroadcastReceiver 获取 Context 实例
开发中,通常是通过调用 Context # registerReceiver() 方法来注册广播接收器,这里的 Context 根据注册广播接收器时的场景可以是前面所述三者的任意一个来提供,这里以 Activity 场景中注册为例,调用 Context # registerReceiver() 方法注册,由上面的分析可知,此时会调用到 Context 的实现类 ContextImpl # registerReceiver() 方法中,代码如下:
1  | ContextImpl.java (api 30)  | 
注册广播接收器时,继续调用 ContextImpl # registerReceiverInternal() 方法并传入当前所处的上下文环境 - 即 Context,这里通过 ContextImpl # getOuterContext() 获取该 Context 实例,这个方法是不是看着很熟悉,在前面 2.1.5.2 、2.2.3.1 及 2.3.3.2 小节中,通过 ContextImpl # setOuterContext() 方法为其赋值的,这也验证了上面的解析,Context 实例的获取是根据注册广播接收器时所处的场景来决定到底获取的是前面所述三者中的哪一个。
ContentProvider 获取 Context 实例
ContentProvider 是四大组件中被使用频率最低的一个,通常用来做跨进程共享数据,它是伴随着应用程序的启动由系统创建的,但它本身不属于 Context 体系结构,因此创建 ContentProvider 实例时所用的 Context 实例需要由别处获得。既然这样那就先看看在应用程序启动过程中的哪里创建的 ContentProvider 实例?
时序图

应用程序启动的流程这里不做详细解读,可以参考这篇文章的详细分析 – 深度详解 Android R(11.0)Activity 启动过程。应用程序在创建并绑定 Application 后,通过 ActivityThread # installContentProviders() 方法来创建并绑定 Context 的实例,一起探索源码来验证一下。
源码分析
ActivityThread # handleBindApplication()
1  | ActivityThread.class (api 30)  | 
在 ActivityThread # handleBindApplication() 方法中,调用 ActivityThread # installContentProviders() 方法并传入创建好的 Application 实例对象,继续调用 ActivityThread # installProvider() 方法来创建 ContentProvider 实例对象,创建过程跟上面分析的差不多,通过类加载器 ClassLoader 加载并新建 ContentProvider 实例对象,最后调用 ContentProvider # attachInfo() 方法为 ContentProvider 设置合适的上下文环境 - Context。
ContentProvider # attachInfo()
1  | ContentProvider.class (api 30)  | 
ContentProvider # attachInfo() 方法中将 Context 实例对象赋值给 ContentProvider 的成员变量 mContext,这样 ContentProvider 就可以使用 Context 提供的获取应用环境全局信息的接口功能,而这个 Context 也正是一开始 ActivityThread # handleBindApplication() 方法中传进来的 Application 实例对象(注意:成员变量 mContext 只允许设置一次)。
注意:在 ContentProvider # installProvider() 方法中会根据不同使用场景,获取对应场景下的 Context 实例对象,我们这里是分析的是同一个应用程序内,所以 ContentProvider 的成员变量 mContext 被赋值为传进来的 Application 实例对象。如果跨进程或者通过 Intent # setPackage() 指定了其它应用的包名等,则需要获取对应场景下的 Context 实例对象。
总结
本节内容补充了四大组件中 BroadcastReceiver 和 ContentProvider 是如何获取到 Context 实例对象的,它们虽是系统组件,但不是 Context 体系结构中的一员,但身为系统组件,它们同样需要用到 Context 所提供的获取应用环境全局信息的接口功能,因此抱着深入学习的态度,还是细细的把源码流程研读了一遍,梳理其创建及获取 Context 实例对象的流程。
总结
结合本文的讲解和源码解析,这里来看一下那些面试中问到过的问题,加深一下理解。
问题一:Context 会导致内存泄露吗?
一般 Context 导致的内存泄漏,几乎都是当 Context 销毁的时候,却因为被引用导致 GC 销毁失败,而 Application 的 Context 对象可以理解为随着应用进程存在的,所以这里总结给出使用 Context 时的一些建议:
- 当 
Application的Context能搞定的情况下,且生命周期较长的对象,优先使用Application的Context。 - 不要让生命周期长于 
Activity的对象持有Activity的引用。 - 尽量不要在 
Activity中使用非静态内部类,因为非静态内部类会隐式持有外部类实例的引用,推荐使用静态内部类,将外部实例引用作为弱引用持有。 
问题二:getContext(),getBaseContxet(),getApplication() 及 getApplicationContext() 的区别?
在文章中已经解析过,getApplication() 和 getApplicationContext() 这俩个方法获取到的是同一个实例对象,只是使用场景范围的不同。getApplication() 方法更加直观,但只能在 Activity 和 Service 场景中调用。getApplicationContext() 方法适用范围更广,任意场景中通过 Context 对象皆可以调用此方法。那为何是同一个对象呢?简单看一下源码:
1  | Activity.class (api 30)  | 
1  | Service.class (api 30)  | 
首先看到 getApplication() 方法返回的是 Activity 和 Service 调用 attach() 方法时传入的 Application 实例对象。还记得文章前面的分析不,Activity # attach() 方法的调用,参见 2.3.3.2 ActivityThread # performLaunchActivity(),而 Service # attach() 方法的调用,参见 2.2.3.1 ActivityThread # handleCreateService() ,在这两个方法中传给 attach() 方法的 Application 实例对象都是通过 LoadedApk # makeApplication() 方法来创建获取的。
再来看一下 getApplicationContext() 方法的返回值,虽然调用到 ContextWrapper,但最终还是委托给实现类 ContextImpl 中实现的,源码如下:
1  | ContextImpl.java (api 30)  | 
这里根据 mPackageInfo 是否为空,分别调用了 mPackageInfo # getApplication() 方法和 mMainThread # getApplication() 方法,那就来看看这两个方法,首先在 LoadedApk 中看一下 getApplication() 方法的返回值,代码如下:
1  | LoadedApk.java (api 30)  | 
首先判断 LoadedApk 中的 mApplication 是否为空 (保证对象的单例),不为空直接返回,如果为空的话新建了一个 Application 实例对象然后赋值给 mApplication。接着在 ActivityThread 中看一下 getApplication() 方法的返回值,代码如下:
1  | ActivityThread.java (api 30)  | 
ActivityThread # getApplication() 方法返回的 mInitialApplication 也是 LoadedApk # makeApplication() 方法返回的,所以可以得出 getApplicationContext() 方法在上述两种情况下返回的是同一个 Application 实例对象。
由于 getApplication() 方法返回的 Application 实例对象也是通过 LoadedApk # makeApplication() 方法来创建获取的,所以说 getApplication() 和 getApplicationContext() 这俩个方法返回的是同一个 Application 实例对象。
getContext()、getBaseContxet() 和 getApplicationContext() 方法的区别?
首先 getBaseContxet() 方法获取的是前面分析的赋值给 mBase 的 Context 的实现类的实例对象。getApplicationContext() 方法返回的 LoadedApk # makeApplication() 方法创建的 Application 实例对象。并且 Application、Activity 和 Service 都有 getBaseContxet() 和 getApplicationContext() 这两个方法。而 getContext() 方法是在 Fragment 或 View 中用来获取其宿主对象的。