Android----Context
一.简介
Context 翻译为上下文环境,是一个应用程序环境信息的接口。
如果以 Android 系统角度来看可以理解为某一与操作系统的交互的具体场景,比如 Activity 的具体功能,Service 的后台运行等。如果以程序的角度看,Context 是一个抽象类,维持 Android 程序中各组件能够正常工作的一个核心功能类。通过 Context 可以获取应用程序的资源和类,也可以进行一些应用程序的操作。
Context 的设计采用了代理模式,它的功能的具体实现类是 ContextImpl ,而间接的实现类是 Activity ,Service 和 Application。因此一个应用程序 Context 的实例的对象的个数就是 Activity , Service 和一个 Application 这三者的总数。它们的具体关系如图:
image.png可以看到在代理模式中 ContextWrapper 的变量 mBase 是一个 ContextImpl 类型,通常Activity , Service 和 Application 中的操作最终都会通过 mBase 交给 ContextImpl 去实现,最后在通过 mOuterContext 对结果进行放回,显然这个变量实际上就是Context 的三个具体的实现。下面就看这三种 Context 是如何创建并与 ContextImpl 关联的。
二.创建 Context
1.Application Context
一个应用程序启动时候总是会创建 Application,具体是由 LoadedApk 实现。
Application app = r.loadedApk.makeApplication(false, mInstrumentation);
// LoadedApk 中
public Application makeApplication(boolean forceDefaultAppClass,
Instrumentation instrumentation) {
...
Application app = null;
...
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
app = mActivityThread.mInstrumentation.newApplication(
cl, appClass, appContext);
appContext.setOuterContext(app);
在这里可以看到一个大致的框架,这里可以分为三个步骤去看;
- 首先会去创建 Application 的 ContextImpl
- 然后把这个 ContextImpl 作为一个参数传到 newApplcation 方法中去创建 Application
- 最后将 ContextImpl 和 Application 关联。
第一步想看 ContextImpl 的静态方法 createAppContext
static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk loadedApk) {
...
ContextImpl context = new ContextImpl(null, mainThread, loadedApk, null, null, null, 0,
null);
context.setResources(loadedApk.getResources());
return context;
}
很明显就是创建 ContextImp,并设置应用的资源。接着看第二步,创建 Application。
//在 Instrumentation 类中
static public Application newApplication(Class<?> clazz, Context context)
...
Application app = (Application)clazz.newInstance();
app.attach(context);//这里的Context 就是ContextImpl
return app;
}
// 在 Application 中
/* package */ final void attach(Context context) {
attachBaseContext(context);
mLoadedApk = ContextImpl.getImpl(context).mLoadedApk;
}
// 在 ContextWrapper 类中
protected void attachBaseContext(Context base) {
if (mBase != null) {
throw new IllegalStateException("Base context already set");
}
mBase = base;
}
可以看到首先就是通过类加载去创建 Application,然后就将 ContextImpl 赋值给 mBase ,这样 Application 和 ContextImpl 的关联就建立。
最后看第三步。
appContext.setOuterContext(app);
//在 ContextImpl 中
final void setOuterContext(Context context) {
mOuterContext = context;
}
将返回的 Application 赋值给 ContextImpl 的变量 mOuterContext,这样 Context 的代理模式完成关联,Application Context 也创建完毕。
流程如图:
image.png
2.Activity Context
Activity Context 的创建可以定位到 Activity 启动过程,在 ActivityThread 中。
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
ContextImpl appContext = ContextImpl 作为参数
关联ContextImpl(r);
Activity activity = null;
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
...
appContext.setOuterContext(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);
}
整个过程和 Application 比较相似,不过有一点不同,这里分为 4 步来看:
- 创建 ContextImpl 对象
- 创建 Activity 实例
- ContextImpl 关联 Activity
- Activity 关联 ContextImpl
先看第一步过程
private ContextImpl createBaseContextForActivity(ActivityClientRecord r) {
...
ContextImpl appContext = ContextImpl.createActivityContext(
this, r.loadedApk, r.activityInfo, r.token, displayId, r.overrideConfig);
...
static ContextImpl createActivityContext(ActivityThread mainThread,
...
ContextImpl context = new ContextImpl(null, mainThread, loadedApk, activityInfo.splitName,
activityToken, null, 0, classLoader);
...
与 application 不同的是,这里创建的是对应的 Activity 的 ContextImpl ,具有不一样的功能实现。接着实例化 Activity
public Activity newActivity(ClassLoader cl, String className,
Intent intent)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
return (Activity)cl.loadClass(className).newInstance();
}
同样也是通过类加载创建 Activity。在第三四步中
//第三步,在ContextImpl 中
final void setOuterContext(Context context) {
mOuterContext = context;
}
//第四步,在 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) {
...
attachBaseContext(context);
...
}
@Override
protected void attachBaseContext(Context newBase) {
super.attachBaseContext(newBase);
...
}
//在 ContextWrapper 中
protected void attachBaseContext(Context base) {
if (mBase != null) {
throw new IllegalStateException("Base context already set");
}
mBase = base;
}
通过 ContextImpl 和 Activity 的相互关联,Activity Context 的创建也就完成了,以后 Activity 的任务大多就是通过 ContextImpl 实现。
流程如图:
image.png
3.Service Context
Service 的Context 的创建和 Activity 的十分相似.
private void handleCreateService(CreateServiceData data) {
LoadedApk loadedApk = getLoadedApkNoCheck(
data.info.applicationInfo, data.compatInfo);
Service service = null;
try {
java.lang.ClassLoader cl = loadedApk.getClassLoader();
service = (Service) cl.loadClass(data.info.name).newInstance();
} catch (Exception e) {
...
}
ContextImpl context = ContextImpl.createAppContext(this, loadedApk);
context.setOuterContext(service);
...
service.attach(context, this, data.info.name, data.token, app,
ActivityManager.getService());
整个流程没有大的变化,只是 Service 会先实例在去创建 ContextImpl ,而且这里的 ContextImpl 和 Activity 是不一样的。
流程如图:
image.png
三.Context 的使用
前面说过 Context 的实现就是 Activity ,Service 和 Application ,因此如果对 Context 使用不当的话很容易发生内存泄漏,比如下面这两段代码
public class A
{
private static Context mContext;
public static void setContext(Context context){
mContext = context;
}
}
public class B
{
private static B sInstance;
private Context mContext;
private B(Context context)
{
this.mContext = context;
}
public static synchronized B getInstance(Context context)
{
if (sInstance == null)
{
sInstance = new B(context);
}
return sInstance;
}
}
在第一段代码中有一个静态的 Context ,在第二段是一个单例模式,一个静态实例拥有一个 Context 变量。在这两种情况下,Context 的周期就和应用程序一样,这是如果赋值时 Activity 或者 Service 就会使得 Activity 和 Service 在退出后不能正常被回收,因为还有 Context 引用。因此建议尽量 Application Context ,因为 Application Context 的周期就是整个应用程序,所以不用担心内存泄漏。但是在某些情况比如创建 Dialog, 或者启动组建的时候就只能使用 Activity Context 或者 Service Context ,这个时候就要注意内存泄漏问题了。