一、Context概览
Context
在Android
中,代表着当前应用程序运行环境的上下文,通过前面对Android应用程序的启动流程和四大组件的启动流程分析。发现只有Application
、Activity
、Service
三者才会创建上下文Context
。所以,我们也可以理解为Context
是Application
、Activity
、Service
运行环境的上下文。所谓上下文,即可以根据一些变量、类来帮助我们获得相关资源、信息。类似聊天中,我们有时候需要前面沟通的信息,来理解当前对话的内容。从Android Studio继承窗口,我们找出Context
的子类。
再根据前面分析启动流程中,ContextWrapper
内部有一个指向Context
类型的属性mBase
,再实际运行会被赋值为ContextImpl
实例。根据这些,我们画出主要的类图信息。
Context
本身是一个抽象类,定义了通用的接口,这样我们在使用的时候,不会感知具体调用了哪些子类。即面向抽象,而不是具体。而Context
的两个直接子类ContextWrapper
和ContextImpl
,我们在研究Applicaton
、Activity
、Service
的启动流程,都会发现创建一个ContextImpl
实例,并和三者绑定,也就是实例设置给ContextWrapper
的mBase
属性。而我们调用Context
的一些能力,基本都会在ContextWrapper
通过mBase
转给ContextImpl
对象去实现。ContextWrapper
在这里理解为装饰者。而不需要主题相关的Application
和Service
直接继承ContextWrapper
,需要主题相关的Activity
再经一层ContextThemeWrapper
修饰。
本文分析的内容,可能和之前流程分析有些重复,但每篇文章的主题侧重点不用,分析的内容也不尽相同。
二、Application上下文
Application与Context的创建和关联
伴随着应用程序进程的启动,在执行主线程ActivityThread
主函数main
的时候,会执行ActivityThread
的attach
函数。
//ActivityThrad#main
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
1、ActivityThread.attach
通过ContextImpl
的静态函数createAppContext
创建ContextImpl
实例context
,然后通过context
去创建Application
实例mInitialApplication
,并回调mInitialApplication
的onCreate
生命周期函数。
private void attach(boolean system, long startSeq) {
...
ContextImpl context = ContextImpl.createAppContext(
this, getSystemContext().mPackageInfo);
mInitialApplication = context.mPackageInfo.makeApplication(true, null);
mInitialApplication.onCreate();
...
}
ContextImpl.createAppContext
createAppContext
有两个重载函数。第2个重载函数直接new
了一个ContextImpl
实例。
static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) {
return createAppContext(mainThread, packageInfo, null);
}
static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo,
String opPackageName) {
...
ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, null,0, null, opPackageName);
context.setResources(packageInfo.getResources());
context.mIsSystemOrSystemUiContext = isSystemOrSystemUI(context);
return context;
}
关注一下ContextImpl
的构造函数,然后ContextImpl
的实例拥有应用程序相关资源的引用,成为调用其他资源的入口。
private ContextImpl(@Nullable ContextImpl container, @NonNull ActivityThread mainThread,
@NonNull LoadedApk packageInfo, @Nullable String attributionTag,
@Nullable String splitName, @Nullable IBinder activityToken, @Nullable UserHandle user,
int flags, @Nullable ClassLoader classLoader, @Nullable String overrideOpPackageName) {
//未与Application,或者是Activity、Service绑定,将mOuterContext先指向自己,
mOuterContext = this;
...
//接下来,将应用程序运行的相关信息,引用赋值给ContextImpl的属性
//这样子,通过Context这个入口,就可以访问到相关资源
mMainThread = mainThread;
mToken = activityToken;
mFlags = flags;
if (user == null) {
user = Process.myUserHandle();
}
mUser = user;
//LoadedApk赋值
mPackageInfo = packageInfo;
mSplitName = splitName;
mClassLoader = classLoader;
mResourcesManager = ResourcesManager.getInstance();
String opPackageName;
if (container != null) {
mBasePackageName = container.mBasePackageName;
opPackageName = container.mOpPackageName;
setResources(container.mResources);
mDisplay = container.mDisplay;
mIsAssociatedWithDisplay = container.mIsAssociatedWithDisplay;
mIsSystemOrSystemUiContext = container.mIsSystemOrSystemUiContext;
} else {
mBasePackageName = packageInfo.mPackageName;
ApplicationInfo ainfo = packageInfo.getApplicationInfo();
if (ainfo.uid == Process.SYSTEM_UID && ainfo.uid != Process.myUid()) {
opPackageName = ActivityThread.currentPackageName();
} else {
opPackageName = mBasePackageName;
}
}
mOpPackageName = overrideOpPackageName != null ? overrideOpPackageName : opPackageName;
mAttributionTag = attributionTag;
//前面研究ContentProvider启动流程,mContentResolver初始化的地方
mContentResolver = new ApplicationContentResolver(this, mainThread);
}
2、LoadedApk.makeApplication
回到attach
函数,调用了context
的LoadedApk
类型的mPackageInfo
属性makeApplication
函数。其中mPackageInfo
在ContextImpl
的构造函数中被赋值。说实话,我确实不知道这样为啥要先创建ContextImpl
实例context
,然后再通过context
去调LoadedApk
实例的makeApplication
函数。
分析一:创建Application
的上下文ContextImpl
类型的appContext
。
分析二:创建Application
实例app
和app
绑定appContext
。
分析三:appContext
绑定app
。
//LoadedApk#makeApplication xxm
public Application makeApplication(boolean forceDefaultAppClass,
Instrumentation instrumentation) {
if (mApplication != null) {
return mApplication;
}
Application app = null;
String appClass = mApplicationInfo.className;
//没有自定义Application,就使用默认的
if (forceDefaultAppClass || (appClass == null)) {
appClass = "android.app.Application";
}
...
final java.lang.ClassLoader cl = getClassLoader();
...
//这里才是真正创建Application上下文的地方,所以上面创建Context实例的作用是?
//分析一
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
...
//分析二
app = mActivityThread.mInstrumentation.newApplication(
cl, appClass, appContext);
//分析三
appContext.setOuterContext(app);
...
mActivityThread.mAllApplications.add(app);
//通过AMS创建返回的赋值给mApplication
mApplication = app;
if (instrumentation != null) {
...
instrumentation.callApplicationOnCreate(app);
...
}
return app;
}
从makeApplication
开始,贴一下时序图。
分析一,创建ContextImpl
的实例可以参考第1小节。
3、Instrumentation.newApplication
在分析二中调用了Instrumentation
的newApplication
函数创建了Application
的实例app
,并关联上下文context
。
public Application newApplication(ClassLoader cl, String className, Context context)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
Application app = getFactory(context.getPackageName())
.instantiateApplication(cl, className);
app.attach(context);
return app;
}
getFactory
函数返回的是AppComponentFactory
类型的实例,其instantiateApplication
函数,通过反射创建Application
实例。
public @NonNull Application instantiateApplication(@NonNull ClassLoader cl,
@NonNull String className)
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
return (Application) cl.loadClass(className).newInstance();
}
而在创建的Application
类型的app
之后,newApplication
函数调用的app
的attach
绑定上下文context
。因为Application
本身继承自ContentWrapper
,所以自身也是个上下文。
Application的attach
函数的主要作用就是将makeApplication
函数创建的ContextImpl
实例appContext
一路传递进来,设置给Application
的父类ContextWrapper
的mBase
属性。也就是说,现在Application
关联到了ContextImpl
类型的上下文mBase
。后续我们通过Application
调用Context
相关的能力,就会转移到了ContextImpl
的实例mBase
。
final void attach(Context context) {
attachBaseContext(context);
mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
}
protected void attachBaseContext(Context base) {
if (mBase != null) {
throw new IllegalStateException("Base context already set");
}
mBase = base;
}
4、ContextImpl.setOuterContext
makeApplication
函数分析三中调用了appContext
的setOuterContext
函数。ContextImpl
类有一个Context
类型的mOuterContext
属性。在调用ContextImpl
构造函数的时候,该属性指向了自己。而setOuterContext
函数将该属性指向Application
实例。
final void setOuterContext(Context context) {
mOuterContext = context;
}
到这里,Application
和ContextImpl
就互相关联。
ApplicationContext的使用
我们在ContextWrapper
的子类Application
、Activity
、Service
都可以通过getApplicationContext
函数来获取Application
的上下文。
public Context getApplicationContext() {
return mBase.getApplicationContext();
}
通过上面的分析,我们知道mBase
的实际类型是ContextImpl
。
public Context getApplicationContext() {
return (mPackageInfo != null) ?
mPackageInfo.getApplication() : mMainThread.getApplication();
}
这里的LoadedApk
类型的mPackageInfo
肯定不为null
,调用了LoadedApk
实例的getApplication
函数。
Application getApplication() {
return mApplication;
}
mApplication
正是前面调用LoadedApk.makeApplication
函数,创建的Applicaiton
实例,Application
本身也继承Context
。所以我们通过getApplicationContext
获得了应用的上下文,调用相关Context
的能力都会转给Application
持有的实际是ContextImpl
类型的mBase
对象。
三、Activity的上下文
在研究Activity的启动流程过程,最终会走到ActivityThread
的performLaunchActivity
函数。
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
//分析一:创建ContextImpl实例
ContextImpl appContext = createBaseContextForActivity(r);
Activity activity = null;
...
java.lang.ClassLoader cl = appContext.getClassLoader();
//分析二:创建Activity实例
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
...
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
//分析三:appContext关联Activity
appContext.setOuterContext(activity);
//分析四:activity关联appContext
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);
...
return activity;
}
Activity
上下文的创建和Application
上下文的创建是类似的。分析一通过createBaseContextForActivity
创建ContextImpl
的实例appContext
;分析二创建Activity的实例activity
,这部分可以参考Activity
的启动流程最后部分。分析三:将activity
设置给appContext,这里appContext
的mBase
就指向了activity
。分析四调用activity
的attach
方法,关联appContext
。
ActivityThread.createBaseContextForActivity
分析一:通过createBaseContextForActivity
函数为当前Activity
创建一个ContextImpl
类型的上下文实例appContext
。调用了createActivityContext
函数。
private ContextImpl createBaseContextForActivity(ActivityClientRecord r) {
...
ContextImpl appContext = ContextImpl.createActivityContext(
this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig);
...
return appContext;
}
ContextImpl.createActivityContext
createActivityContext函数同样是通过ContextImpl构造函数创建ContextImpl实例,然后多设置了更多属性。
static ContextImpl createActivityContext(ActivityThread mainThread,LoadedApk packageInfo, ActivityInfo activityInfo, IBinder activityToken, int displayId, Configuration overrideConfiguration) {
...
ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null,
activityInfo.splitName, activityToken, null, 0, classLoader, null);
context.mIsUiContext = true;
context.mIsAssociatedWithDisplay = true;
context.mIsSystemOrSystemUiContext = isSystemOrSystemUI(context);
displayId = (displayId != Display.INVALID_DISPLAY) ? displayId : Display.DEFAULT_DISPLAY;
final CompatibilityInfo compatInfo = (displayId == Display.DEFAULT_DISPLAY)
? packageInfo.getCompatibilityInfo()
: CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
final ResourcesManager resourcesManager = ResourcesManager.getInstance();
context.setResources(resourcesManager.createBaseTokenResources(activityToken,
packageInfo.getResDir(),
splitDirs,
packageInfo.getOverlayDirs(),
packageInfo.getApplicationInfo().sharedLibraryFiles,
displayId,
overrideConfiguration,
compatInfo,
classLoader,
packageInfo.getApplication() == null ? null
: packageInfo.getApplication().getResources().getLoaders()));
context.mDisplay = resourcesManager.getAdjustedDisplay(displayId,
context.getResources());
return context;
}
Activity.attach
分析四:在创建ContextImpl
实例appContext
和Activity
实例activity
后,将activity
关联到appContext
。然后调用activity
的attach
函数,将appContext
关联到activity
中。
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor,
Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken) {
...
attachBaseContext(context);
...
}
调用了attachBaseContext
函数。
protected void attachBaseContext(Context newBase) {
super.attachBaseContext(newBase);
}
//ContextThemeWrapper xxm
protected void attachBaseContext(Context newBase) {
super.attachBaseContext(newBase);
...
}
//ContextWrapper xxm
protected void attachBaseContext(Context base) {
...
mBase = base;
}
appContext
经过一路传递最终还是设置给了Activity
的父类ContextWrapper
的mBase
变量。
到这里Activity
和Context
就相关关联了。后续在Activity
关于Context
的相关操作都会传递到实际类型是ContextImpl的mBase
中去 。
四、Service的上下文
Service
的上下文,其实在Service的启动流程已经分析过了,为了Context
的完整性,摘抄过来。
在Service
的启动流程的最后,调用了ActivityThread
的handleCreateService
函数。看完handleCreateService
函数之后,其实已经不用再进一步仔细分析了,和Application
与Activity
的上下文太惊人的相似。
private void handleCreateService(CreateServiceData data) {
...
Service service = null;
//分析一
ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
Application app = packageInfo.makeApplication(false, mInstrumentation);
java.lang.ClassLoader cl = packageInfo.getClassLoader();
service = packageInfo.getAppFactory()
.instantiateService(cl, data.info.name, data.intent);
...
//分析二
context.setOuterContext(service);
//分析三
service.attach(context, this, data.info.name, data.token, app,
ActivityManager.getService());
...
}
分析一:通过ContextImpl
的静态函数createAppContext
返回了一个ContextImpl
类型的context
。createAppContext
又调用了重载函数createAppContext
。直接新建了ContextImpl
实例context
,构造函数传递了ActivityThread
类型的mainThread
和LoadedApk
类型的packageInfo
。并给context
设置了资源环境和是否Syetem
属性。
static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) {
return createAppContext(mainThread, packageInfo, null);
}
static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo,
String opPackageName) {
if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, null,
0, null, opPackageName);
context.setResources(packageInfo.getResources());
context.mIsSystemOrSystemUiContext = isSystemOrSystemUI(context);
return context;
}
回到handleCreateService
函数的分析二,在创建好Service
对象service
之后,将service
作为参数传递给了context.setOuterContext
函数。Service
本身继承自ContextWrapper
,ContextWrapper
又是Context
的子类。这时候的setOuterContext
函数将service
设置给了context
的mOuterContext
属性。意味着当前上下文context
持有当前新建的service
引用。
在分析三,调用了service.attach
函数,context
并作为第一个参数被传入。attach
函数又调用了attachBaseContext
函数。后面的分析就省略了。
public final void attach(
Context context,
ActivityThread thread, String className, IBinder token,
Application application, Object activityManager) {
attachBaseContext(context);
...
}
五、总结
一个应用程序程序在运行的过程上下文的数量=Application+Activity+Service的数量。之所以能称为上下文,是因为Context
的实现类ContextImpl
持有应用程序运行过程相关资源类应用,Context
作为一个入口,通过Application
、Activity
、Service
的修饰下,更加方便的访问这些资源。