Android 上下文Context
源码基于Android8.0 API 26
1 Context
的关联类
在Android
中,Activity
可以直接new
吗?
Activity mActivity = new Activity();
Android
应用程序开发基于Java
语言,Activity
本质上也是一个对象,那么👆的写法有什么问题?Android
应用程序不像Java
应用程序那样,随便创建一个类,写一个main
方法就可以运行,Android
是基于组件的应用设计模式,组件的运行需要有一个完整的Android
工程环境。 只有在这个环境下,Activity
、Service
、BroadcastReceiver
等组件才可以正常工作。这个支持组件运行上下文环境就是Context
。 可以这样讲,Context
是一个维持Android
应用中各个组件能够正常工作的一个核心工程类。
如果想要在Android Studio
中运行一个.java
文件的main
函数,需要在Project Root/.idea/gradle.xml
的标签GradleProjectSettings
添加:
<option name="delegatedBuild" value="false" />
在开发过程中,Context
的使用场景总的来说可以分为两大类:
- 使用
Context
调用方法,比如启动Activity
、启动Service
、发送广播、操作数据库、访问资源、调用系统服务等 - 调用方法时传入
Context
,比如弹出Toast
、创建Dialog
等
这些行为意味着需要访问系统。
那么Context
是从哪来的呢?从AMS
,AMS
是系统级进程,拥有访问系统的权利,应用程序的启动受AMS
的调控,在程序启动的过程中,AMS
会把一个“凭证”通过跨进程通信给应用程序,程序会把这个“凭证”封装程Context
,并提供一系列的接口,这样我程序也就可以很方便的访问系统资源了。 这样做的好处是:系统可以对应用程序的操作进行调控,限制各种情境下的权限,同时也可以防止恶意攻击。
以下为Context
的源码:
/**
* 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.
*
* 提供了关于应用程序环境的全局信息的接口。
* 它是一个被Android系统支持的抽象类。它允许获取和应用程序相关的资源和类,包括应用级别操作,比如启动Activity、
* 发送广播、接收Intent等操作
*/
public abstract class Context {
}
1.1 ContextImpl
和ContextWrapper
既然Context
是抽象类,在它的内部定义了很多方法以及静态常量,它的具体实现类为ContextImpl
。 和Context
相关联的类,除了ContextImpl
,还有ContextWrapper
、ContextThemeWrapper
、Application
、Service
等。
从上图中看,ContextImpl
和ContextWrapper
继承自Context
。
1.1.1 ContextImpl
ContextImp
是Context
功能的实现类,应用程序中所调用的各种Context
类的方法,其实现均来自该类。 因为外界需要使用并拓展ContextImpl
的功能,因此设计上使用了装饰模式。
以下是ContextImpl
的源码:
class ContextImpl extends Context {
}
1.1.2 ContextWrapper
ContextWrapper
是上下文功能的封装类,它对ContextImpl
进行包装,主要起方法传递作用。ContextWrapper
中几乎所有的方法都是调用ContextImpl
来实现的,因此,在ContentWrapper
的构造函数中包含一个真正的Context
的引用——mBase
,mBase
具体指向ContextImpl
,同时ContextWrapper
提供了attachBaseContext()
用于给ContextWrapper
对象中指定真正的Context
对象,调用ContextWrapper
的方法都会被转向给其所包含的真正的Context
对象。
以下是ContextWrapper
的源码:
public class ContextWrapper extends Context {
@UnsupportedAppUsage
Context mBase;
public ContextWrapper(Context base) {
mBase = base;
}
protected void attachBaseContext(Context base) {
if (mBase != null) {
throw new IllegalStateException("Base context already set");
}
mBase = base;
}
public Context getBaseContext() {
return mBase;
}
@Override
public Resources getResources() {
return mBase.getResources();
}
@Override
public ContentResolver getContentResolver() {
return mBase.getContentResolver();
}
@Override
public Looper getMainLooper() {
return mBase.getMainLooper();
}
@Override
public Context getApplicationContext() {
return mBase.getApplicationContext();
}
@Override
public String getPackageName() {
return mBase.getPackageName();
}
@Override
public void startActivity(Intent intent) {
mBase.startActivity(intent);
}
@Override
public void sendBroadcast(Intent intent) {
mBase.sendBroadcast(intent);
}
@Override
public Intent registerReceiver(
BroadcastReceiver receiver, IntentFilter filter) {
return mBase.registerReceiver(receiver, filter);
}
@Override
public void unregisterReceiver(BroadcastReceiver receiver) {
mBase.unregisterReceiver(receiver);
}
@Override
public ComponentName startService(Intent service) {
return mBase.startService(service);
}
@Override
public boolean stopService(Intent name) {
return mBase.stopService(name);
}
@Override
public boolean bindService(Intent service, ServiceConnection conn, int flags) {
return mBase.bindService(service, conn, flags);
}
@Override
public void unbindService(ServiceConnection conn) {
mBase.unbindService(conn);
}
}
其实,在ContextWrapper
中的方法是非常多的,但是,实现都非常统一,就是调用了mBase
对象中对应当前方法名的方法。
ContextWrapper
有三个直接的子类,ContextThemeWrapper
、Service
和Application
。其中,ContextThemeWrapper
是一个带主题的封装类,它有一个直接子类就是Activity
。
ContextThemeWrapper
类,其内容包含了主题Theme
相关的接口,这里所说的主题是指在AndroidManifest.xml
中通过android:theme
为Application
元素或者Activity
元素制定的主题。当然只有Activity
才需要主题,而Service
是不需要主题的,因为Service
是没有界面的后台场景,所以Service
直接继承于ContextWrapper
,Application
同理。
Context
的关联类采用了装饰模式,主要有以下优点:
- 使用者(比如
Service
)能够更方便的使用Context
- 如果
ContextImpl
发生了变化,它的装饰类ContextWrapper
不需要做任何修改 ContextImpl
的实现不会暴露给使用者,使用者也不必关系ContextImpl
的实现- 通过组合而非继承的方式,拓展
ContextImpl
的功能,在运行时选择不同的装饰类,实现不同的功能
总结:Context
的两个子类分工明确,其中ContextImpl
是Context
的具体实现类,ContextWrapper
是Context
的包装类。Activity
,Application
,Service
虽然都继承自ContentWrapper
(Activity
继承自ContextWrapper
的子类ContextThemeWrapper
),但它们初始化的过程中都会创建ContextImpl
对象mBase
,由ContextImpl
实现Context
中的方法。
由此可知,Context
一共有三种类型,分别是Application
、Activity
和Service
。这三个类分别各自承担了不同的作用,而具体的功能则是由ContextImpl
类去实现的。
因此在大多数的场景下,Activity
、Service
和Application
这三种类型的Context
都是可以通用的。不过有几种场景比较特殊,比如启动Activity
,还有弹出Dialog
。出于安全因素的考虑,Android
是不允许Activity
或Dialog
凭空出现的,一个Activity
的启动必须建立在另一个Activity
的基础之上,也就是以此形成的返回栈。而Dialog
则必须在一个Activity
上面弹出(除非是System Alert
类型的Dialog
),因此在这种场景下,只能使用Activity
类型的Context
,否则将会出错。
Activity
常用于与UI
有关的操作,如添加Window
,常规使用可以直接用Activity.this
。Service
可以和Activity
一样直接使用Service.this
来使用Context
,和Activity
不同的是,Service
没有界面,也不需要主题。ContentProvider
使用的是Application
的Context
,Broadcast
使用Activity
的Context
。
1.2 Context
的数量
那么一个应用程序中到底有多少个Context
呢?Context
有Application
、Activity
和Service
三种类型,因此一个应用程序中Context
数量的计算公式可以这样写:Context
数量 = Activity
数量 + Service
数量 + 1
,1
代表Application
的数量, 因为一个应用程序可以有多个Activity
和多个Service
,但只能有一个Application
。
那么在四大组件中,为什么只有Activity
、Service
持有Context
,那么Broadcast Receiver
,Content Provider
并不是Context
的子类,它们所持有的Context
都是其他地方传过去的,所以并不计入Context
总数。
1.3 Context
的作用域
Context
的作用域还是有一些限制的。由于Context
的具体实例是由ContextImpl
类去实现的,因此在绝大多数的场景下,Activity
、Service
和Application
这三种类型的Context
都是可以通用的。不过有几种场景比价特殊,比如启动Activity
、还有弹出Dialog
。出于安全原因的考虑,Android
是不允许Activity
或者Dialog
凭空出现的,一个Activity
的启动必须建立在另一个Activity
的基础之上,也就是以此形成的返回栈。而Dialog
则必须在一个Activity
上面弹出(除非是System Alert
类型的Dialog
),因此在这种场景下,只能用Activity
类型的Context
,否则将会出错。
从上图中可以看出Activity
所持有的Context
的作用域最广,因为Activity
继承自ContextThemeWrapper
,而Application
和Service
继承自ContentWrapper
,很显然ContextThemeWrapper
在ContextWrapper
的基础上又做了一些操作使得Activity
变得更加强大。
2 Application Context
2.1 Application Context
的创建过程
在一个应用程序启动完成后,应用程序就会有一个全局的Application Context
。以下是Application Context
的创建过程的时序图:
ActivityThread
类作为应用程序进程的主线程管理类,它会调用它的内部类ApplicationThread
的scheduleLaunchActivity
方法来启动Activity
。
在scheduleLaunchActivity
方法中向H
类发送LAUNCH_ACTIVITY
类型的消息,目的是将启动Activity
的逻辑放在主线程的消息队列中,这样启动Activity
的逻辑会在主线程中执行。
H
继承自Handler
,是ActivityThread
的内部类。在H
类的handleMessage
方法对LAUNCH_ACTIVITY
类型的消息的处理。
2.2 Application Context
的获取
一个应用程序启动完成后,就会有一个全局的Application Context
。通过getApplicationContext()
方法可以获得应用程序全局的Application Context
,getApplicationContext()
方法在ContextWrapper
中实现,如下所示:
@Override
public Context getApplicationContext() {
return mBase.getApplicationContext();
}
mBase
指的是ContextImpl
,ContextImpl
的getApplicationContext()
方法: