1. 程式人生 > >Android中Context認識

Android中Context認識

[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,這樣可以避免記憶體洩露的