官方定义
官方文档中,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.
- 一个应用环境的全局信息,字面意思上下文;
- Context是一个抽象类;
- 允许通过Context获取各种资源、服务,或去启动一个Activity,发送一个广播,等等;
其实Context就是给Android应用程序提供了一个可实现各种操作的土壤环境,Context为Android提供了各种资源、功能、服务。若说编写一个Android程序像搭建一座房子,那Context就为Android提供了土地、木材和染料(启动一个Activity,弹出一个Dialog),且能提供呼叫各种将房屋建得更完善的其他帮助(发送一个广播,启动一个服务等)。
继承关系
Context是抽象类,来看看常见的子类

Context直接子类为ContextIml(实现类)和ContextWrapper(包装类)
再看看ContextWrapper的子类有什么,看到熟悉的Service和Application了吧,不过看到这里你一定有个疑问,为何Activity和他们哥俩不在一个继承层级呢?而是Activity又继承了ContextThemeWrapper,那么ContextWrapper和ContextThemeWrapper的区别在哪里呢?
看到这两个类的名字,相信你心里已经有了答案,对,区别在Theme。
该类内部包含了主题(Theme)相关的接口,即android:theme属性指定的。
只有Activity需要主题,Service不需要主题,
所以Service直接继承于ContextWrapper类。而Activity因为含有Theme属性的缘故,所以继承自ContextThemeWrapper。
**所以说,Context所调用的资源是不同的了?**保留这个疑问,继续向下看。
作用

Context几乎包含了所有能想到的,一个Android程序需要的资源和操作,Context就像一个App一样,启动Activity、Service,发送Broadcast,拿到assets下的资源,获取SharedPreferences等等。
Android中的Context是一个抽象类,它是Android应用程序与系统之间的桥梁。Context类提供了许多方法,可以访问应用程序的资源,如应用程序的资源文件、SQLite数据库、SharedPreferences、应用程序的类和系统服务等。
在Android中,每个Activity、Service、BroadcastReceiver、ContentProvider都是Context的子类,它们都可以使用Context提供的方法来访问应用程序的资源。例如,通过调用getResources()方法可以访问应用程序的资源文件,通过调用getSystemService()方法可以访问系统服务。
此外,Context还具有一些其他的作用,例如可以创建新的Activity、启动Service、发送广播、获取应用程序的包名等。因此,在Android开发中,Context是非常重要的一个类,是许多操作的基础。
四大组件里的Context
ContextWrapper
成员变量
只有个成员变量:
Context mBase;//该属性指向一个ContextIml实例
成员方法
ContextWrapper重写Context的方法,内部依靠mBase调用。

mBase实际上就是ContextImpl类型的,来看看它的内容
ContextImpl
成员变量
//ContentResolver
private final ApplicationContentResolver mContentResolver;
//通过ResourcesManager管理Resource
private final @NonNull ResourcesManager mResourcesManager;
//Resource引用
private @NonNull Resources mResources;
//主题资源
private int mThemeResource = 0;
//主题
private Resources.Theme mTheme = null;
//缓存context.getSystemService 获取的实例
final Object[] mServiceCache = SystemServiceRegistry.createServiceCache();
//省略...
成员方法
ContextImpl成员方法是Context方法具体实现的地方。

Context/ContextWrapper/ContextImpl 三者关系:ContextWrapper、ContextImpl继承自Context,ContextWrapper作为ContextImpl 代理。

ContextWrapper和ContextImpl关联
思路越来越清晰,现在就是要去寻找,Activity,Service,Application是何时与ContextImpl完成绑定关联的。
ActivityThread的main方法,是整个Android程序的入口,所以去探究ActivityThread类,也是一件非常重要的事。ActivityThread的一个内部类H,里面定义了activity、service等启动、销毁等事件的响应,也就是说activity、service的启动、销毁都是在ActivityThread中进行的。
Application
Application是ContextWrapper子类
#LoadedApk.java
#makeApplication(...)
//创建ContextImpl 实例
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
//创建Application实例,并将mBase指向appContext
//Application.attach(...)->ContextWrapper.attachBaseContext(...)->mBase=appContext
app = mActivityThread.mInstrumentation.newApplication(
cl, appClass, appContext);
//ContextImpl mOuterContext指向app
appContext.setOuterContext(app);
在创建Application的时候,会先构造ContextImpl对象,然后构造Application实例,并将Application里的mBase指向ContextImpl对象,最后将ContextImpl mOuterContext指向app。完成了Application和ContextImpl关联,也即是ContextWrapper和ContextImpl的关联。
Service
ContextWrapper还有另一个常见的子类:Service。来看看Service如何关联ContextImpl的。
#ActivityThread.java
private void handleCreateService(CreateServiceData data) {
//获取LoadedApk
LoadedApk packageInfo = getPackageInfoNoCheck(
data.info.applicationInfo, data.compatInfo);
Service service = null;
try {
java.lang.ClassLoader cl = packageInfo.getClassLoader();
//创建service
service = packageInfo.getAppFactory()
.instantiateService(cl, data.info.name, data.intent);
} catch (Exception e) {
}
try {
//创建ContextImpl
ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
//contextImpl 持有该Service
context.setOuterContext(service);
Application app = packageInfo.makeApplication(false, mInstrumentation);
//初始化Service一些成员变量,关联ContextImpl
service.attach(context, this, data.info.name, data.token, app,
ActivityManager.getService());
//service onCreate方法,一般会重写该方法监听service的创建
service.onCreate();
} catch (Exception e) {
}
}
接着看看service.attach(...)
public final void attach(
Context context,
ActivityThread thread, String className, IBinder token,
Application application, Object activityManager) {
//关联ContextImpl
attachBaseContext(context);
mThread = thread; // NOTE: unused - remove?
mClassName = className;
mToken = token;
mApplication = application;
mActivityManager = (IActivityManager)activityManager;
mStartCompatibility = getApplicationInfo().targetSdkVersion
< Build.VERSION_CODES.ECLAIR;
}
以上,ContextImpl和Service关联起来了
Activity
ContextWrapper还有一个子类ContextThemeWrapper。
ContextThemeWrapper顾名思义,和主题相关的。
{
//主题资源id
private int mThemeResource;
//theme
private Resources.Theme mTheme;
private LayoutInflater mInflater;
private Configuration mOverrideConfiguration;
private Resources mResources;
}
而Activity继承自ContextThemeWrapper,来看看Activity和ContextImpl如何关联上的。
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
//省略...
//创建ContextImp
ContextImpl appContext = createBaseContextForActivity(r);
//创建Activity
Activity activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
//关联ContextImpl和activity
appContext.setOuterContext(activity);
//初始化Activity成员变量
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,
r.assistToken);
//省略
}
类似的,继续看activity.attach(...)

最终也是调用到了ContextWrapper attachBaseContext(...),关联mBase。
BroadcastReceiver
BroadcastReceiver 并没有继承自Context,但可以在onReceive(...)里拿到Context,那么这个Context是怎么来的呢?

Context作为参数传进来的,那么就看看onReceive(...)的调用栈。
#ActivityThread.java
private void handleReceiver(ReceiverData data) {
//省略...
Application app;
BroadcastReceiver receiver;
ContextImpl context;
try {
app = packageInfo.makeApplication(false, mInstrumentation);
//用的是Application的mBase,也就是ContextImpl
context = (ContextImpl) app.getBaseContext();
//构造receiver
receiver = packageInfo.getAppFactory()
.instantiateReceiver(cl, data.info.name, data.intent);
} catch (Exception e) {}
try {
//调用onReceive
receiver.onReceive(context.getReceiverRestrictedContext(),
data.intent);
} catch (Exception e) {
} finally {
}
}
注意到context.getReceiverRestrictedContext():
#ContextImpl.java
final Context getReceiverRestrictedContext() {
//ReceiverRestrictedContext 是ContextWrapper的子类
if (mReceiverRestrictedContext != null) {
return mReceiverRestrictedContext;
}
//该Context是关联Application的,也即是ContextImpl,因此getOuterContext()返回的是
//Application实例。最后ReceiverRestrictedContext的mBase指向Application实例
return mReceiverRestrictedContext = new ReceiverRestrictedContext(getOuterContext());
}
ContentProvider
ContentProvider没有继承自Context,但是其成员变量mContext是Context类型的,那么这个变量是怎么赋值的呢?
在构造ContextImpl时,会初始化ContentResolver
mContentResolver = new ApplicationContentResolver(this, mainThread);
这个this即是ContextImpl自身,传进去赋值给了ContentResolver变量:
private final Context mContext;
当使用ContentResolver查询ContentProvider并且创建ContentProvider的时候,这个mContext就赋值给ContentProvider的mContext。
上面分析了Application和四大组件与Context关系,用图表示:

看到这里,相信你对Context的理解更进一步了,现在知道了Context是什么,它为Android提供了怎样的资源、功能、和服务,又在什么时候将Application、Activity、Service与ContextImpl相关联,但是所请求的资源是不是同一套资源呢?
在这里你一定说:“当然不是,不同的Context对象明显是有区别的,用法也不同”
但是其实他们访问的,确确实实,是同一套资源。
Context与Resources
之前列举了Context的用处,最常用的莫过于通过Context获取资源文件(Resources),具体情况是怎么样的,接下来分析。
Context并没有Resources类型的成员变量,ContextWrapper也没有,ContextImpl有成员变量:
private @NonNull Resources mResources;
而Context里有获取Resources的成员方法:
public abstract Resources getResources();
最终会调用ContextImpl,返回mResources。因此重点是ContextImpl的mResources如何赋值的。
上面提到过,Application/Activity/Service等关联ContextImpl时,会新构造一个ContextImpl实例,在初始化的时候,会给mResources赋值。而Resources是通过ResourcesManager管理的,最终来看ResourcesManager如何管理Resources的。
作者:小鱼人爱编程
链接:www.jianshu.com/p/bd412e96d…
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
来吧,看看不同Context对象的区别和用法的不同,参见以下表格。

这张表格是不是又支持了你的观点(也就是一直认为的,Context资源对象是不同的),但是还是要再次强调一次,它们所请求的,确确实实是同一块资源,看看上面进行关联的源码,都走进了Context实现类的init方法,拨云见日,去看看init方法吧。
查看ContextImpl类源码可看到,getResources方法直接返回内部的mResources变量
final void init(LoadedApk packageInfo,
IBinder activityToken, ActivityThread mainThread,
Resources container) {
mPackageInfo = packageInfo;
mResources = mPackageInfo.getResources(mainThread);
if (mResources != null && container != null
&& container.getCompatibilityInfo().applicationScale !=
mResources.getCompatibilityInfo().applicationScale) {
if (DEBUG) {
Log.d(TAG, "loaded context has different scaling. Using container's" +
" compatiblity info:" + container.getDisplayMetrics());
}
mResources = mainThread.getTopLevelResources(
mPackageInfo.getResDir(), container.getCompatibilityInfo().copy());
}
mMainThread = mainThread;
mContentResolver = new ApplicationContentResolver(this, mainThread);
setActivityToken(activityToken);
}
mResources又是调用LoadedApk的getResources方法进行赋值。代码如下。
public Resources getResources(ActivityThread mainThread) {
if (mResources == null) {
mResources = mainThread.getTopLevelResources(mResDir, this);
}
return mResources;
}
从代码中可看到,最终mResources的赋值是由AcitivtyThread的getTopLevelResources方法返回。代码如下。
Resources getTopLevelResources(String resDir, CompatibilityInfo compInfo) {
ResourcesKey key = new ResourcesKey(resDir, compInfo.applicationScale);
Resources r;
synchronized (mPackages) {
// Resources is app scale dependent.
if (false) {
Slog.w(TAG, "getTopLevelResources: " + resDir + " / "
+ compInfo.applicationScale);
}
WeakReference<Resources> wr = mActiveResources.get(key);
r = wr != null ? wr.get() : null;
if (r != null && r.getAssets().isUpToDate()) {
if (false) {
Slog.w(TAG, "Returning cached resources " + r + " " + resDir
+ ": appScale=" + r.getCompatibilityInfo().applicationScale);
}
return r;
}
}
AssetManager assets = new AssetManager();
if (assets.addAssetPath(resDir) == 0) {
return null;
}
DisplayMetrics metrics = getDisplayMetricsLocked(false);
r = new Resources(assets, metrics, getConfiguration(), compInfo);
if (false) {
Slog.i(TAG, "Created app resources " + resDir + " " + r + ": "
+ r.getConfiguration() + " appScale="
+ r.getCompatibilityInfo().applicationScale);
}
synchronized (mPackages) {
WeakReference<Resources> wr = mActiveResources.get(key);
Resources existing = wr != null ? wr.get() : null;
if (existing != null && existing.getAssets().isUpToDate()) {
// Someone else already created the resources while we were
// unlocked; go ahead and use theirs.
r.getAssets().close();
return existing;
}
// XXX need to remove entries when weak references go away
mActiveResources.put(key, new WeakReference<Resources>(r));
return r;
}
}
以上代码中,mActiveResources对象内部保存了该应用程序所使用到的所有Resources对象,其类型为WeakReference,所以当内存紧张时,可释放Resources占用的资源,自然这不是探究的重点,ResourcesKey的构造需要resDir和compInfo.applicationScale。resdDir变量的含义是资源文件所在路径,实际指的是APK程序所在路径,比如可是:/data/app/com.haii.android.xxx-1.apk,该apk会对应/data/dalvik-cache目录下的:data@app@com.haii.android.xxx-1.apk@classes.dex文件。
所以结论来了:
若一个应用程序没有访问该程序以外的资源,那么mActiveResources变量中就仅有一个Resources对象。
总结:
当ActivityThread类中创建Application、Service、Activity的同时,完成了与ContextImpl的关联绑定,通过ContextImpl类中init方法,获得了一个唯一的Resources对象,根据上述代码中资源的请求机制,再加上ResourcesManager采用单例模式,这样就保证了不同的ContextImpl访问的是同一套资源。
若这篇博客现在就结束了,你一定会杀了我 - -,现在就来分析下,是什么造成了唯一的这个Resources,却展现出了“不同”。
举个通俗易懂的例子,我和我老妈都拿到同一块土豆,但是因为处理这个土豆的方法有区别,导致这个土豆最后表现出来的也不一样,我想把它做成薯片,我妈妈把它炒成了土豆丝,:-D。
再具体一点,比如除了Activity可创建一个Dialog,其它Context都不可创建Dialog。比如在Application中创建Dialog会报错,还有Application和Service可启动一个Activity,但是需要创建一个新的task。比如你在Application中调用startActivity(intent)时系统也会崩溃报错。
报错的原因并不是因为他们拿到的Context资源不同,拿到的都是一个Resoucres对象,但是在创建Dialog的时候会使用到Context对象去获取当前主题信息,但是知道Application和Service是继承自ContextWrapper,没有实现关于主题的功能,然而Activity是继承自ContextThemeWrapper,该类是实现了关于主题功能的,因此创建Dialog的时候必须依附于Activity的Context引用。
结论:
Application、Service、Activity,它们本身对Resources资源处理方法的不同,造成了这个Resoucres最后表现出来的不一样,这么说大家就都懂了吧!
Context内存泄漏
关于Context的内存泄漏,找到一篇比较不错的文章分享给大家。
写在最后:
Context可能还有更多深层次的知识需要去了解,比如Context这些封装类,是具体如何通过Binder跟ContextImpl进行关联的;资源对象都被存储在ArrayMap,为何ArrayMap中会有可能存在多个资源对象,如何访问其他应用程序的Context资源等等,剩下的这些就靠大家慢慢发掘了~