Android中Context認識
阿新 • • 發佈:2021-03-25
[toc]
基於SDK29
# 1、Context作用
Context,意為上下文,閱讀理解中常常有聯絡上下文理解的說法,這裡可以認為Context是一個特定的範圍,提供了整個環境的一些資料,比如Application作為Context可以註冊Activity生命週期監聽、獲取應用程序名、獲取應用資源等等,Activity作為Context可以獲取資源、啟動Activity、載入View等等, 具體使用見下圖:
(以上圖片來自部落格:[Android Context 上下文 你必須知道的一切](https://blog.csdn.net/lmj623565791/article/details/40481055))
# 2、Context分析
## 2.1、Context繼承樹
Context繼承結構如下所示:
```yml
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,這樣可以避免記憶體洩露的