源码分析->一个应用到底有几个Context
应用Context实例个数= Activity个数 +Service个数 + Application个数
相信很多人都知道是这样计算的,那到底为什么是这样呢?
源码分析基于Android28源码
Context
什么是Context呢?可以理解为上下文、运行环境,当需要获取资源、系统服务以及启动Activity、Service用到,也可以通过它跟系统交互。
Activity
通过以下继承关系可以看出,Activity是继承ContextWrapper
Activity继承关系.png
ContextWrapper内部有一个Context类型的成员变量mBase
public class ContextWrapper extends Context {
Context mBase;
public ContextWrapper(Context base) {
mBase = base;
}
......
mBase是通过attachBaseContext()方法赋值
protected void attachBaseContext(Context base) {
if (mBase != null) {
throw new IllegalStateException("Base context already set");
}
mBase = base;
}
AMS.performLaunchActivity
是创建Activity的关键,
主要工作
(1)createBaseContextForActivity()内部实例化ContextImpl 对象;
(2)mInstrumentation.newActivity()内部通过反射实例化Activity对象;
(3)activity.attach()内部会调用attachBaseContext()方法给mBase对象赋值;
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
......
ContextImpl appContext = createBaseContextForActivity(r);
Activity activity = null;
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
......
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);
......
Application
通过以下继承关系可以看出,Application是继承ContextWrappe
Application继承关系.png
LoadedApk.makeApplication
是创建Application的关键,
主要工作:
(1)ContextImpl.createAppContext()实例化ContextImpl ;
(2)mActivityThread.mInstrumentation.newApplication(),内部通过反射实例化Application,并把appContext传递过去,通过attach()方法给mBase赋值;
public Application makeApplication(boolean forceDefaultAppClass,
Instrumentation instrumentation) {
......
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
app = mActivityThread.mInstrumentation.newApplication(
cl, appClass, appContext);
......
Service
跟Activity类似就不再做分析。
经过分析发现:
1.每个Activity,Service,Application都有一个ContextImpl 类型的成员变量mBase,ContextImpl是Context的实现类。
2.细心的读者可能发现,Activity,Service,Application都是继承Context,其实他们本身是一个Context,也都实现了Context的抽象方法,
那么一个Activity是否就拥有两个Context呢?
是不是
应用Context实例个数= (Activity个数 +Service个数 + Application个数)*2
这样计算比较合适呢?
下面看下Context中常用的三个方法,
public AssetManager getAssets() ;
public Resources getResources() ;
public PackageManager getPackageManager();
ContextImpl继承Context,并实现了这三个方法,
class ContextImpl extends Context {
final @NonNull ActivityThread mMainThread;//ActivityThread 对象
final @NonNull LoadedApk mPackageInfo;//包安装信息类
private final @NonNull ResourcesManager mResourcesManager;//资源管理
private @NonNull Resources mResources;
.......
@Override
public AssetManager getAssets() {
return getResources().getAssets();
}
@Override
public Resources getResources() {
return mResources;
}
@Override
public PackageManager getPackageManager() {
if (mPackageManager != null) {
return mPackageManager;
}
IPackageManager pm = ActivityThread.getPackageManager();
if (pm != null) {
// Doesn't matter if we make more than one instance.
return (mPackageManager = new ApplicationPackageManager(this, pm));
}
return null;
}
Activity间接继承Context,主要是在ContextWrapper实现了以上三个方法,从源码中可以看出,最终还是调用了ContextImpl的实现。
@Override
public AssetManager getAssets() {
return mBase.getAssets();
}
@Override
public Resources getResources() {
return mBase.getResources();
}
@Override
public PackageManager getPackageManager() {
return mBase.getPackageManager();
}
下图可以看出这几个的关系,ContextWrapper顾名思义就是Context的包装类(有ContextImpl的成员变量),并且实现了Context,这是一种装饰者设计模式。当在Activity中调用getAsset()时,其实最终是调用mBase的getAsset()。
context继承关系.png
结论
Activity间接继承了Context,是为了拥有跟ContextImpl一样的功能,但真正起作用的是mBase这个成员变量,所以一个Activity其实就只有一个Context起作用,那就是ContextImpl类型的mBase。
应用Context实例个数= Activity个数 +Service个数 + Application个数
这种计算方法应该是没有问题呢。
或许有人有这样的疑问,一个应用不是只有一个Application吗,为什么计算公式是加上Application个数?单进程应用来说,一个应用确实只有一个Application,而多进程应用,那么一个应用就有多个Application,所以应该说一个应用有一个或多个Application,一个进程有一个Application。
补充一点:BroadcastReceiver是没有Context的,onReceiver传进来的Context是注册该广播的Context ,而ContentProvider的Context是Application的Context。
另外其他关于Context的常见面试题
1.Activity的this跟getBaseContext区别。
前者是Activity对象本身,后者是通过attachBaseContext传入的ContextImpl对象mBase,两者功能是一样的,通过this最终还是会调到mBase对象中。
2.getApplication和geApplicationContext区别。
两者都是返回Application对象,前者是Activity和Service里面的方法,后者是Context中定义的方法。
3.应用组件的构造,onCreate、attachBaseContext的执行顺序。
先是组件构造化,接着attachBaseContext,传入ContextImpl对象,最后是onCreate方法。
4.谈谈你对Context的理解
先是Context的作用,然后是有几种Context,Application、Service、Activity的Context有什么区别以及继承关系,
最后是mBase变量是如何实例化的。
以上分析有不对的地方,请指出,互相学习,谢谢哦!