Android中Context认识
基于SDK29
1、Context作用
Context,意为上下文,阅读理解中常常有联系上下文理解的说法,这里可以认为Context是一个特定的范围,提供了整个环境的一些数据,比如Application作为Context可以注册Activity生命周期监听、获取应用进程名、获取应用资源等等,Activity作为Context可以获取资源、启动Activity、加载View等等, 具体使用见下图:
(以上图片来自博客:Android Context 上下文 你必须知道的一切)
2、Context分析
2.1、Context继承树
Context继承结构如下所示:
java.lang.Object:
android.content.Context:
android.app.ContextImpl (Activity和Application中mBase具体操作实现类)
android.content.ContextWrapper:
android.app.Application
android.app.Service
android.view.ContextThemeWrapper:
android.app.Activity:
androidx.core.app.ComponentActivity:
androidx.activity.ComponentActivity:
androidx.fragment.app.FragmentActivity:
androidx.appcompat.app.AppCompatActivity
四大组件中Activity继承自ContextThemeWrapper,Service和Application继承自ContextWrapper。
2.2、ContextImpl类
ContextImpl类直接继承自Context类,位于android.app包下,提供了Context抽象方法的直接实现, 包括getAssets、getResources、getPackageManager等。
2.3、ContextWrapper分析
ContextWrapper使用静态代理的模式来管理Context,内部所有的操作都是通过Context类型的mBase来具体实现,如getResources()等。通过构造方法或者attachBaseContext方法对mBase赋值。
public class ContextWrapper extends Context {
Context mBase;
public ContextWrapper(Context base) {
mBase = base;
}
protected void attachBaseContext(Context base) {
if (mBase != null) {
throw new IllegalStateException("Base context already set");
}
mBase = base;
}
public Context getBaseContext() {
return mBase;
}
@Override
public Resources getResources() {
return mBase.getResources();
}
...
}
2.4、ContextThemeWrapper分析
ContextThemeWrapper与ContextWrapper主要区别在于:前者能够直接处理主题,根据主题样式的id(mThemeResource)生成对应的主题Theme(mTheme),而后者需要由内部的mBase进行处理。
public class ContextThemeWrapper extends ContextWrapper {
private int mThemeResource;
private Resources.Theme mTheme;
public ContextThemeWrapper() {
super(null);
}
public ContextThemeWrapper(Context base, @StyleRes int themeResId) {
super(base);
mThemeResource = themeResId;
}
public ContextThemeWrapper(Context base, Resources.Theme theme) {
super(base);
mTheme = theme;
}
@Override
protected void attachBaseContext(Context newBase) {
super.attachBaseContext(newBase);
}
@Override
public void setTheme(int resid) {
if (mThemeResource != resid) {
mThemeResource = resid;
initializeTheme();
}
}
public void setTheme(@Nullable Resources.Theme theme) {
mTheme = theme;
}
@Override
public int getThemeResId() {
return mThemeResource;
}
@Override
public Resources.Theme getTheme() {
if (mTheme != null) {
return mTheme;
}
mThemeResource = Resources.selectDefaultTheme(mThemeResource,
getApplicationInfo().targetSdkVersion);
initializeTheme();
return mTheme;
}
private void initializeTheme() {
final boolean first = mTheme == null;
if (first) {
mTheme = getResources().newTheme();
final Resources.Theme theme = getBaseContext().getTheme();
if (theme != null) {
mTheme.setTo(theme);
}
}
onApplyThemeResource(mTheme, mThemeResource, first);
}
...
}
3、Activity以及Application创建过程
3.1、Activity创建过程
Activity创建在ActivityThread类的performLaunchActivity方法中,部分代码如下:
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);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
r.intent.prepareToEnterProcess();
if (r.state != null) {
r.state.setClassLoader(cl);
}
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to instantiate activity " + component
+ ": " + e.toString(), e);
}
}
try {
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
...
if (activity != null) {
...
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);
...
r.activity = activity;
}
r.setState(ON_CREATE);
...
} catch (SuperNotCalledException e) {
throw e;
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to start activity " + component
+ ": " + e.toString(), e);
}
}
return activity;
}
以上代码可以简单概括如下:
首先创建ContextImpl的实例appContext, 然后通过Instrumentation类的newActivity方法生成Activity,最后调用Activity的attach方法将appContext关联到Activity的mBase,从而将Activity中有关Context的操作委托给ComtextImpl类的实例。
3.2、Application创建过程
和Activity一样,Application内部的mBase也是ContextImpl,创建Activity时会检查Application是否存在,不存在则创建。
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
try {
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
} catch (SuperNotCalledException e) {
throw e;
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to start activity " + component
+ ": " + e.toString(), e);
}
}
...
}
其中r.packageInfo为LoadApk的实例, makeApplication方法也会先创建一个ComtextImpl的实例appContext,然后调用Instrumentation的newApplication创建Application,并调用Application的attach方法将appContext关联到Application的mBase,将Application中有关Context的操作委托给ComtextImpl类的实例。部分代码如下:
public Application makeApplication(boolean forceDefaultAppClass,
Instrumentation instrumentation) {
if (mApplication != null) {
return mApplication;
}
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "makeApplication");
Application app = null;
String appClass = mApplicationInfo.className;
if (forceDefaultAppClass || (appClass == null)) {
appClass = "android.app.Application";
}
try {
...
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
app = mActivityThread.mInstrumentation.newApplication(
cl, appClass, appContext);
appContext.setOuterContext(app);
} catch (Exception e) {
...
}
mActivityThread.mAllApplications.add(app);
mApplication = app;
...
return app;
}
4、总结
除了显示Dialog、启动Activity或者加载View,应用中用到Context的地方都可以使用Application作为Context,这样可以避免内存泄露的情况。