Context是什么?Android应用的上下文,既然是上下文,就说明其所拥有的功能,是要贯穿整个app的生命周期的,比如说APK资源,系统服务等
在Android中,对于系统创建的对象,咱们能从其身上拿到Context的主要有5个,四大组件和Application对象
- Activity,Service,Application都派生自ContextWrapper,说明其本身就是一个代理Context,宿主Context会在组件创建后调用类似attach传入
- BroadcastReceiver在onReceive时会传入context
- ContextProvider会在创建后调用attachInfo传入context
那每个组件使用的Context又有什么区别呢?很简单,直接看组件创建时,这些Context是哪里来的就清楚了
Application
直接看LoaedApk.makeApplication中创建application的代码
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
app = mActivityThread.mInstrumentation.newApplication(
cl, appClass, appContext);
appContext.setOuterContext(app);
ContextImpl.createAppContext:
static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) {
if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
return new ContextImpl(null, mainThread,
packageInfo, null, null, 0, null, null, Display.INVALID_DISPLAY);
}
然后看mInstrumentation.newApplication:
static public Application newApplication(Class<?> clazz, Context context)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
Application app = (Application)clazz.newInstance();
app.attach(context);
return app;
}
看到了,application的宿主context实际上就是其在被创建时对应创建的ContextImpl对象
Activity
private Context createBaseContextForActivity(ActivityClientRecord r, final Activity activity) {
int displayId = Display.DEFAULT_DISPLAY;
try {
displayId = ActivityManagerNative.getDefault().getActivityDisplayId(r.token);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
ContextImpl appContext = ContextImpl.createActivityContext(
this, r.packageInfo, r.token, displayId, r.overrideConfig);
appContext.setOuterContext(activity);
Context baseContext = appContext;
final DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
// For debugging purposes, if the activity's package name contains the value of
// the "debug.use-second-display" system property as a substring, then show
// its content on a secondary display if there is one.
String pkgName = SystemProperties.get("debug.second-display.pkg");
if (pkgName != null && !pkgName.isEmpty()
&& r.packageInfo.mPackageName.contains(pkgName)) {
for (int id : dm.getDisplayIds()) {
if (id != Display.DEFAULT_DISPLAY) {
Display display =
dm.getCompatibleDisplay(id, appContext.getDisplayAdjustments(id));
baseContext = appContext.createDisplayContext(display);
break;
}
}
}
return baseContext;
}
接着看ContextImpl.createActivityContext
static ContextImpl createActivityContext(ActivityThread mainThread,
LoadedApk packageInfo, IBinder activityToken, int displayId,
Configuration overrideConfiguration) {
if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
return new ContextImpl(null, mainThread, packageInfo, activityToken, null, 0,
null, overrideConfiguration, displayId);
}
Service
在ActivityThread.handleCreateService中:
ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
context.setOuterContext(service);
Application app = packageInfo.makeApplication(false, mInstrumentation);
service.attach(context, this, data.info.name, data.token, app,
ActivityManagerNative.getDefault());
service.onCreate();
mServices.put(data.token, service);
代码中很明显,service被创建的时,同同时创建一个app context传入,作为contextwrapper的宿主对象
ContentProvider
直接看ActivityThread中的installProvider的代码
final java.lang.ClassLoader cl = c.getClassLoader();
localProvider = (ContentProvider)cl.
loadClass(info.name).newInstance();
provider = localProvider.getIContentProvider();
if (provider == null) {
Slog.e(TAG, "Failed to instantiate class " +
info.name + " from sourceDir " +
info.applicationInfo.sourceDir);
return null;
}
// XXX Need to create the correct context for this provider.
localProvider.attachInfo(c, info);
传入的c是installProvider传入的,从代码追溯可以发现,这个c就是mInitialApplication,最后看mInitialApplication是怎么被创建的:
ContextImpl context = ContextImpl.createAppContext(
this, getSystemContext().mPackageInfo);
mInitialApplication = context.mPackageInfo.makeApplication(true, null);
mInitialApplication.onCreate();
看到没,它就是contentprovider所属的applicaiton对象本身
BroadcastReceiver
还是看ActivityThread中handleReceiver部分代码
ContextImpl context = (ContextImpl)app.getBaseContext();
sCurrentBroadcastIntent.set(data.intent);
receiver.setPendingResult(data);
receiver.onReceive(context.getReceiverRestrictedContext(),
data.intent);
看到没,其拿的是application的宿主context,然后从context中拿到ReceiverRestrictedContext,这个ReceiverRestrictedContext其实也没什么,同样派生自ContextWrapper,其宿主是Application,不同的时,ReceiverRestrictedContext做了registerreceiver限制
ApplicationContext
还有就是我们经常使用的Context.getApplicationContext了,它其实就是Application本身
@Override
public Context getApplicationContext() {
return (mPackageInfo != null) ?
mPackageInfo.getApplication() : mMainThread.getApplication();
}
总结
- Application,Activity,Service都是派生自ContextWrapper,他们在创建时都会同时创建对应的ContextImpl,它才是真实被挂载Context宿主
- context提供了贯穿整个app相关资源的获取,那能整个App只使用一个ContextImpl宿主吗?答案是不行的,因为ContextImpl做不到全局独立,比如其内部的Resource查找资源,根据Android的特性,会随着Activity的显示属性的变化,需要变更查找维度的
- ContentProvider attach的context其实就是application
- applicationContext就是application本身