<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                【工匠若水?[http://blog.csdn.net/yanbober](http://blog.csdn.net/yanbober)?轉載煩請注明出處,尊重分享成果】 ### 1 背景 今天突然想起之前在上家公司(做TV與BOX盒子)時有好幾個人問過我關于Android的Context到底是啥的問題,所以就馬上要誕生這篇文章。我們平時在開發App應用程序時一直都在使用Context(別說你沒用過,訪問當前應用的資源、啟動一個activity等都用到了Context),但是很少有人關注過這玩意到底是啥,也很少有人知道getApplication與getApplicationContext方法有啥區別,以及一個App到底有多少個Context等等的細節。 更為致命的是Context使用不當還會造成內存泄漏。所以說完全有必要拿出來單獨分析分析(基于Android 5.1.1 (API 22)源碼分析)。 【工匠若水?[http://blog.csdn.net/yanbober](http://blog.csdn.net/yanbober)?轉載煩請注明出處,尊重分享成果】 ### 2 Context基本信息 #### 2-1 Context概念 先看下源碼Context類基本情況,如下: ~~~ /** * Interface to global information about an application environment. This is * an abstract class whose implementation is provided by * the Android system. It * allows access to application-specific resources and classes, as well as * up-calls for application-level operations such as launching activities, * broadcasting and receiving intents, etc. */ public abstract class Context { ...... } ~~~ 從源碼注釋可以看見,Context提供了關于應用環境全局信息的接口。它是一個抽象類,它的執行被Android系統所提供。它允許獲取以應用為特征的 資源和類型,是一個統領一些資源(應用程序環境變量等)的上下文。 看見上面的Class OverView了嗎?翻譯就是說,它描述一個應用程序環境的信息(即上下文);是一個抽象類,Android提供了該抽象類的具體實現類;通過它我們可以獲取應用程序的資源和類(包括應用級別操作,如啟動Activity,發廣播,接受Intent等)。 既然上面Context是一個抽象類,那么肯定有他的實現類咯,我們在Context的源碼中通過IDE可以查看到他的子類如下: ![這里寫圖片描述](https://box.kancloud.cn/2016-03-16_56e8da7970f2f.jpg "") 嚇尿了,737個子類,經過粗略瀏覽這些子類名字和查閱資料發現,這些子類無非就下面一些主要的繼承關系。這737個類都是如下關系圖的直接或者間接子類而已。如下是主要的繼承關系: ![這里寫圖片描述](https://box.kancloud.cn/2016-03-16_56e8da798370c.jpg "") 從這里可以發現,Service和Application的類繼承類似,Activity繼承ContextThemeWrapper。這是因為Activity有主題(Activity提供UI顯示,所以需要主題),而Service是沒有界面的服務。 所以說,我們從這張主要關系圖入手來分析Context相關源碼。 #### 2-2 Context之間關系源碼概述 有了上述通過IDE查看的大致關系和圖譜之后我們在源碼中來仔細看下這些繼承關系。 先來看下Context類源碼注釋: ~~~ /** * Interface to global information about an application environment. This is * an abstract class whose implementation is provided by * the Android system. It * allows access to application-specific resources and classes, as well as * up-calls for application-level operations such as launching activities, * broadcasting and receiving intents, etc. */ public abstract class Context { ...... } ~~~ 看見沒有,抽象類Context ,提供了一組通用的API。 再來看看Context的實現類ContextImpl源碼注釋: ~~~ /** * Common implementation of Context API, which provides the base * context object for Activity and other application components. */ class ContextImpl extends Context { private Context mOuterContext; ...... } ~~~ 該類實現了Context類的所有功能。 再來看看Context的包裝類ContextWrapper源碼注釋: ~~~ /** * Proxying implementation of Context that simply delegates all of its calls to * another Context. Can be subclassed to modify behavior without changing * the original Context. */ public class ContextWrapper extends Context { Context mBase; public ContextWrapper(Context base) { mBase = base; } /** * Set the base context for this ContextWrapper. All calls will then be * delegated to the base context. Throws * IllegalStateException if a base context has already been set. * * @param base The new base context for this wrapper. */ protected void attachBaseContext(Context base) { if (mBase != null) { throw new IllegalStateException("Base context already set"); } mBase = base; } ...... } ~~~ 該類的構造函數包含了一個真正的Context引用(ContextImpl對象),然后就變成了ContextImpl的裝飾著模式。 再來看看ContextWrapper的子類ContextThemeWrapper源碼注釋: ~~~ /** * A ContextWrapper that allows you to modify the theme from what is in the * wrapped context. */ public class ContextThemeWrapper extends ContextWrapper { ...... } ~~~ 該類內部包含了主題Theme相關的接口,即android:theme屬性指定的。 再來看看Activity、Service、Application類的繼承關系源碼: ~~~ public class Activity extends ContextThemeWrapper implements LayoutInflater.Factory2, Window.Callback, KeyEvent.Callback, OnCreateContextMenuListener, ComponentCallbacks2, Window.OnWindowDismissedCallback { ...... } ~~~ ~~~ public abstract class Service extends ContextWrapper implements ComponentCallbacks2 { ...... } ~~~ ~~~ public class Application extends ContextWrapper implements ComponentCallbacks2 { ...... } ~~~ 看見沒有?他們完全符合上面我們繪制的結構圖與概述。 #### 2-3 解決應用Context個數疑惑 有了上面的Context繼承關系驗證與分析之后我們來看下一個應用程序到底有多個Context? Android應用程序只有四大組件,而其中兩大組件都繼承自Context,另外每個應用程序還有一個全局的Application對象。所以在我們了解了上面繼承關系之后我們就可以計算出來Context總數,如下: ~~~ APP Context總數 = Application數(1) + Activity數(Customer) + Service數(Customer); ~~~ 到此,我們也明確了Context是啥,繼承關系是啥樣,應用中Context個數是多少的問題。接下來就有必要繼續深入分析這些Context都是怎么來的。 【工匠若水?[http://blog.csdn.net/yanbober](http://blog.csdn.net/yanbober)?轉載煩請注明出處,尊重分享成果】 ### 3 各種Context在ActivityThread中實例化過程源碼分析 在開始分析之前還是和[《Android異步消息處理機制詳解及源碼分析》](http://blog.csdn.net/yanbober/article/details/45936145)的3-1-2小節及[《Android應用setContentView與LayoutInflater加載解析機制源碼分析》](http://blog.csdn.net/yanbober/article/details/45970721)的2-6小節一樣直接先給出關于Activity啟動的一些概念,后面會寫文章分析這一過程。 Context的實現是ContextImpl,Activity與Application和Service的創建都是在ActivityThread中完成的,至于在ActivityThread何時、怎樣調運的關系后面會寫文章分析,這里先直接給出結論,因為我們分析的重點是Context過程。 #### 3-1 Activity中ContextImpl實例化源碼分析 通過startActivity啟動一個新的Activity時系統會回調ActivityThread的handleLaunchActivity()方法,該方法內部會調用performLaunchActivity()方法去創建一個Activity實例,然后回調Activity的onCreate()等方法。所以Activity的ContextImpl實例化是在ActivityThread類的performLaunchActivity方法中,如下: ~~~ private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { ...... //已經創建好新的activity實例 if (activity != null) { //創建一個Context對象 Context appContext = createBaseContextForActivity(r, activity); ...... //將上面創建的appContext傳入到activity的attach方法 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); ...... } ...... return activity; } ~~~ 看見上面performLaunchActivity的核心代碼了嗎?通過`createBaseContextForActivity(r, activity);`創建appContext,然后通過activity.attach設置值。 具體我們先看下createBaseContextForActivity方法源碼,如下: ~~~ private Context createBaseContextForActivity(ActivityClientRecord r, final Activity activity) { //實質就是new一個ContextImpl對象,調運ContextImpl的有參構造初始化一些參數 ContextImpl appContext = ContextImpl.createActivityContext(this, r.packageInfo, r.token); //特別特別留意這里!!! //ContextImpl中有一個Context的成員叫mOuterContext,通過這條語句就可將當前新Activity對象賦值到創建的ContextImpl的成員mOuterContext(也就是讓ContextImpl內部持有Activity)。 appContext.setOuterContext(activity); //創建返回值并且賦值 Context baseContext = appContext; ...... //返回ContextImpl對象 return baseContext; } ~~~ 再來看看activity.attach,也就是Activity中的attach方法,如下: ~~~ 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) { //特別特別留意這里!!! //與上面createBaseContextForActivity方法中setOuterContext語句類似,不同的在于: //通過ContextThemeWrapper類的attachBaseContext方法,將createBaseContextForActivity中實例化的ContextImpl對象傳入到ContextWrapper類的mBase變量,這樣ContextWrapper(Context子類)類的成員mBase就被實例化為Context的實現類ContextImpl attachBaseContext(context); ...... } ~~~ 通過上面Activity的Context實例化分析再結合上面Context繼承關系可以看出: Activity通過ContextWrapper的成員mBase來引用了一個ContextImpl對象,這樣,Activity組件以后就可以通過這個ContextImpl對象來執行一些具體的操作(啟動Service等);同時ContextImpl類又通過自己的成員mOuterContext引用了與它關聯的Activity,這樣ContextImpl類也可以操作Activity。 SO,由此說明一個Activity就有一個Context,而且生命周期和Activity類相同(記住這句話,寫應用就可以避免一些低級的內存泄漏問題)。 #### 3-2 Service中ContextImpl實例化源碼分析 寫APP時我們通過startService或者bindService方法創建一個新Service時就會回調ActivityThread類的handleCreateService()方法完成相關數據操作(具體關于ActivityThread調運handleCreateService時機等細節分析與上面Activity雷同,后邊文章會做分析)。具體handleCreateService方法代碼如下: ~~~ private void handleCreateService(CreateServiceData data) { ...... //類似上面Activity的創建,這里創建service對象實例 Service service = null; try { java.lang.ClassLoader cl = packageInfo.getClassLoader(); service = (Service) cl.loadClass(data.info.name).newInstance(); } catch (Exception e) { ...... } try { ...... //不做過多解釋,創建一個Context對象 ContextImpl context = ContextImpl.createAppContext(this, packageInfo); //特別特別留意這里!!! //ContextImpl中有一個Context的成員叫mOuterContext,通過這條語句就可將當前新Service對象賦值到創建的ContextImpl的成員mOuterContext(也就是讓ContextImpl內部持有Service)。 context.setOuterContext(service); Application app = packageInfo.makeApplication(false, mInstrumentation); //將上面創建的context傳入到service的attach方法 service.attach(context, this, data.info.name, data.token, app, ActivityManagerNative.getDefault()); service.onCreate(); ...... } catch (Exception e) { ...... } } ~~~ 再來看看service.attach,也就是Service中的attach方法,如下: ~~~ public final void attach( Context context, ActivityThread thread, String className, IBinder token, Application application, Object activityManager) { //特別特別留意這里!!! //與上面handleCreateService方法中setOuterContext語句類似,不同的在于: //通過ContextWrapper類的attachBaseContext方法,將handleCreateService中實例化的ContextImpl對象傳入到ContextWrapper類的mBase變量,這樣ContextWrapper(Context子類)類的成員mBase就被實例化為Context的實現類ContextImpl attachBaseContext(context); ...... } ~~~ 可以看出步驟流程和Activity的類似,只是實現細節略有不同而已。 SO,由此說明一個Service就有一個Context,而且生命周期和Service類相同(記住這句話,寫應用就可以避免一些低級的內存泄漏問題)。 #### 3-3 Application中ContextImpl實例化源碼分析 當我們寫好一個APP以后每次重新啟動時都會首先創建Application對象(每個APP都有一個唯一的全局Application對象,與整個APP的生命周期相同)。創建Application的過程也在ActivityThread類的handleBindApplication()方法完成相關數據操作(具體關于ActivityThread調運handleBindApplication時機等細節分析與上面Activity雷同,后邊文章會做分析)。而ContextImpl的創建是在該方法中調運LoadedApk類的makeApplication方法中實現,LoadedApk類的makeApplication()方法中源代碼如下: ~~~ public Application makeApplication(boolean forceDefaultAppClass, Instrumentation instrumentation) { //只有新創建的APP才會走if代碼塊之后的剩余邏輯 if (mApplication != null) { return mApplication; } //即將創建的Application對象 Application app = null; String appClass = mApplicationInfo.className; if (forceDefaultAppClass || (appClass == null)) { appClass = "android.app.Application"; } try { java.lang.ClassLoader cl = getClassLoader(); if (!mPackageName.equals("android")) { initializeJavaContextClassLoader(); } //不做過多解釋,創建一個Context對象 ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this); //將Context傳入Instrumentation類的newApplication方法 app = mActivityThread.mInstrumentation.newApplication( cl, appClass, appContext); //特別特別留意這里!!! //ContextImpl中有一個Context的成員叫mOuterContext,通過這條語句就可將當前新Application對象賦值到創建的ContextImpl的成員mOuterContext(也就是讓ContextImpl內部持有Application)。 appContext.setOuterContext(app); } catch (Exception e) { ...... } ...... return app; } ~~~ 接著看看Instrumentation.newApplication方法。如下源碼: ~~~ public Application newApplication(ClassLoader cl, String className, Context context) throws InstantiationException, IllegalAccessException, ClassNotFoundException { return newApplication(cl.loadClass(className), context); } ~~~ 繼續看重載兩個參數的newApplication方法,如下: ~~~ static public Application newApplication(Class<?> clazz, Context context) throws InstantiationException, IllegalAccessException, ClassNotFoundException { ...... //繼續傳遞context app.attach(context); return app; } ~~~ 繼續看下Application類的attach方法,如下: ~~~ final void attach(Context context) { //特別特別留意這里!!! //與上面makeApplication方法中setOuterContext語句類似,不同的在于: //通過ContextWrapper類的attachBaseContext方法,將makeApplication中實例化的ContextImpl對象傳入到ContextWrapper類的mBase變量,這樣ContextWrapper(Context子類)類的成員mBase就被實例化為Application的實現類ContextImpl attachBaseContext(context); ...... } ~~~ 可以看出步驟流程和Activity的類似,只是實現細節略有不同而已。 SO,由此說明一個Application就有一個Context,而且生命周期和Application類相同(然而一個App只有一個Application,而且與應用生命周期相同)。 ### 4 應用程序APP各種Context訪問資源的唯一性分析 你可能會有疑問,這么多Context都是不同實例,那么我們平時寫App時通過context.getResources得到資源是不是就不是同一份呢?下面我們從源碼來分析下,如下: ~~~ class ContextImpl extends Context { ...... private final ResourcesManager mResourcesManager; private final Resources mResources; ...... @Override public Resources getResources() { return mResources; } ...... } ~~~ 看見沒,有了上面分析我們可以很確定平時寫的App中context.getResources方法獲得的Resources對象就是上面ContextImpl的成員變量mResources。 那我們追蹤可以發現mResources的賦值操作如下: ~~~ private ContextImpl(ContextImpl container, ActivityThread mainThread, LoadedApk packageInfo, IBinder activityToken, UserHandle user, boolean restricted, Display display, Configuration overrideConfiguration) { ...... //單例模式獲取ResourcesManager對象 mResourcesManager = ResourcesManager.getInstance(); ...... //packageInfo對于一個APP來說只有一個,所以resources 是同一份 Resources resources = packageInfo.getResources(mainThread); if (resources != null) { if (activityToken != null || displayId != Display.DEFAULT_DISPLAY || overrideConfiguration != null || (compatInfo != null && compatInfo.applicationScale != resources.getCompatibilityInfo().applicationScale)) { //mResourcesManager是單例,所以resources是同一份 resources = mResourcesManager.getTopLevelResources(packageInfo.getResDir(), packageInfo.getSplitResDirs(), packageInfo.getOverlayDirs(), packageInfo.getApplicationInfo().sharedLibraryFiles, displayId, overrideConfiguration, compatInfo, activityToken); } } //把resources賦值給mResources mResources = resources; ...... } ~~~ 由此可以看出在設備其他因素不變的情況下我們通過不同的Context實例得到的Resources是同一套資源。 PS一句,同樣的分析方法也可以發現Context類的packageInfo對于一個應用來說也只有一份。感興趣可以自行分析。 ### 5 應用程序APP各種Context使用區分源碼分析 #### 5-1 先來解決getApplication和getApplicationContext的區別 很多人一直區分不開這兩個方法的區別,這里從源碼來分析一下,如下: 首先來看getApplication方法,你會發現Application與Context都沒有提供該方法,這個方法是哪提供的呢?我們看下Activity與Service中的代碼,可以發下如下: ~~~ public class Activity extends ContextThemeWrapper implements LayoutInflater.Factory2, Window.Callback, KeyEvent.Callback, OnCreateContextMenuListener, ComponentCallbacks2, Window.OnWindowDismissedCallback { ...... public final Application getApplication() { return mApplication; } ...... } ~~~ ~~~ public abstract class Service extends ContextWrapper implements ComponentCallbacks2 { ...... public final Application getApplication() { return mApplication; } ...... } ~~~ Activity和Service提供了getApplication,而且返回類型都是Application。這個mApplication都是在各自類的attach方法參數出入的,也就是說這個 mApplication都是在ActivityThread中各自實例化時獲取的makeApplication方法返回值。 所以不同的Activity和Service返回的Application均為同一個全局對象。 再來看看getApplicationContext方法,如下: ~~~ class ContextImpl extends Context { ...... @Override public Context getApplicationContext() { return (mPackageInfo != null) ? mPackageInfo.getApplication() : mMainThread.getApplication(); } ...... } ~~~ 可以看到getApplicationContext方法是Context的方法,而且返回值是Context類型,返回對象和上面通過Service或者Activity的getApplication返回 的是一個對象。所以說對于客戶化的第三方應用來說兩個方法返回值一樣,只是返回值類型不同,還有就是依附的對象不同而已。 #### 5-2 各種獲取Context方法的差異及開發要點提示 可以看出來,Application的Context生命周期與應用程序完全相同。? Activity或者Service的Context與他們各自類生命周期相同。 所以說對于Context使用不當會引起內存泄漏。 譬如一個單例模式的自定義數據庫管理工具類需要傳入一個Context,而這個數據庫管理對象又需要在Activity中使用,如果我們傳遞Activity的Context就可能造成內存泄漏,所以需要傳遞Application的Context。 ### 6 Context分析總結 到此整個Android應用的Context疑惑就完全解開了,同時也依據源碼分析結果給出了平時開發APP中該注意的內存泄漏問題提示與解決方案。相信通過這一篇你在開發APP時對于Context的使用將不再迷惑。 【工匠若水?[http://blog.csdn.net/yanbober](http://blog.csdn.net/yanbober)?轉載煩請注明出處,尊重分享成果】
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看