Ⅰ Context是什麼
Context
Context,翻譯是上下文環境,在android應用開發中的地位舉足輕重,甚至源碼也有一句話:everything needs a context(看到過,但是忘了在哪裡了)。
從這個類圖中我們看到,Context是一個抽象類,一邊是Context的具體實現ContextImpl,一邊則是組件的封裝類ContextWrapper,便於二者各司其職,我理解是介面和實現分開,這樣比較符合開閉原則吧。
既然要搞清楚Context是幹嘛的,得先看Context的實例什麼時候創建的。這里有兩個我們經常接觸的組件,Activity和Service,以及用的較少的Application,它們的關系如下:
1.Application:保存應用的全局信息的類,它的實例在應用創建的時候被創建,生命周期最長,應用銷毀了才會被銷毀。
2.Activity:創建一個Activity便對應一個Context的實例,生命周期隨著Activity的周期變化。
3.Service:創建一個Service便對應一個Context的實例,生命周期隨著Service的周期變化。
下面逐一看看它們是什麼時候實例Context類的,我們需要知道,一個ContextWrapper類對應一個ContextImpl類的實例,因為具體實現的地方還是在ContextImpl中,ContextWrapper中的mBase變數指向ContextImpl變數的實例。
(1) Application的Context創建:
如圖:
跳過Process創建過程,在我們的應用被zygote進程孵化出來後,被反射調用main函數,這里會創建ActivityThread類的實例,該類並不是一個線程類的子類,但是它的main函數跑在新創建的進程的主線程。 創建後,調用attach方法,與AMS綁定:
這里傳入的mAppThread變數為ApplicationThread類,它為ActivityThread的內部類,作用就是作為應用端的binder服務端,負責接收AMS的調度,這里我們傳入這個服務端的句柄,讓AMS持有,從而AMS可以通過這個句柄來操控應用的行為。我們向AMS請求,則是通過AMS的句柄。接下來就到了AMS的邏輯:
在ApplicationThread的實現端中,就是把跨進程傳遞過來的各種數據再用一個AppBindData類保存下來,然後調用Handler對象H發送消息BIND_APPLICATION,最後在app線程中處理此邏輯,讓AMS線程可以不必同步阻塞。接下來就到了handleBindApplication:
Instrumentation類是用於管理Acitivty的工具類。這又有一個比較重要的對象出現,LoadedApk,它裡面保存了類載入器ClassLoader,data.info的對象便是它,makeApplication函數中的邏輯:
這里我們看到了ContextImpl的創建時機,就是在Application實例創建的時候:
如此就完成了Application的創建,並且調用attach方法把Application的mBase對象賦值給創建的ContextImpl,至此Application的創建就完成了,getApplicationContext() 返回的便是此處我們創建的Application類的對象。
這里為什麼要去LoadedApk類中利用類載入器,把對象創建出來呢?因為我們的Application類是可以自己拓展的,創建的時候是不確定應用自己是否復寫了Application對象, 利用類載入器就可以動態決定創建什麼類的對象了 ,我們只需要從PMS中獲取到application的具體類名即可,這個類名是寫在Mainfest文件中的,後面Activity也是利用這種機制去創建對象。
(2)Actvity的啟動過程,網上非常多文章分析了,我也就不再重復羅列出來了,大概的過程就是startActivity()被調用,然後檢查目標Acitivity的進程是否創建,沒創建就先向Zygote進程請求fork應用進程,然後一系列初始化過程後,最後在AMS模塊中的ActivityStackSupervisor中realStartActivityLocked()調用IApplicationThread代理類,調用到scheleLaunchActivity(),然後在應用的線程開始執行handleLaunchActivity(),最主要的邏輯在performLaunchActivity:
至於Activity的生命周期後面怎麼走的,這里不在乎,我們只看Context實例化過程。同樣的,這里也會創建ContextImpl對象,在Activity對象創建後,調用Attach(),這個函數挺重要的,我們看看都幹了什麼:
在attach函數中,也會為ContextWrapper中的mBase對象賦值一個ContextImpl對象,我們這里也能猜想到Service的創建估計也會有這個過程,而事實上Service的創建差不多也是這個過程,所以不再贅述了。
我們可以知道Context例的實例化都是在我們在Application,Activity和Service創建的時候會實例化,而這些組件的父類都是ContextWrapper,所以在創建的時候還會先創建一個ContextImpl類對象,然後給自己的父類mBase變數賦值,既然如此,Context的引用對應的就是Application,Activity和Service了,Context的用處就是提供了一組抽象的函數,讓子類去相對應的實現,當你持有一個Context引用的時候,你可以通過這個引用去獲取相對應組件的信息。比如持有一個Activity的Context引用,你可以在別的地方調用startActivity()去啟動一個新的activity。
總結:
1.Context是抽象類,所以實例化要在子類中,Application,Service,Activity是我們實例化的地方,一般應用創建會實例化一個Application類,Service和Activity則是在startService和StartActivity被調用的時候會實例化,它們都會創建一個ContextImpl類實例,給自己的父類ContextWrapper的mBase變數賦值。
2.Context是組件中通用操作的一組集合,當具體的子類實例化後,可以在別的地方通過保存一個Context引用去獲取信息和通用操作等,也就是說,我們可以通過這個引用去獲取到這個應用中的重要信息以及這個應用的通用操作。
Ⅱ 源碼分析->一個應用到底有幾個Context
相信很多人都知道是這樣計算的,那到底為什麼是這樣呢?
源碼分析基於Android28源碼
什麼是Context呢?可以理解為上下文、運行環境,當需要獲取資源、系統服務以及啟動Activity、Service用到,也可以通過它跟系統交互。
通過以下繼承關系可以看出,Activity是繼承ContextWrapper
ContextWrapper內部有一個Context類型的成員變數mBase
mBase是通過attachBaseContext()方法賦值
是創建Activity的關鍵,
主要工作
(1)createBaseContextForActivity()內部實例化ContextImpl 對象;
(2)mInstrumentation.newActivity()內部通過反射實例化Activity對象;
(3)activity.attach()內部會調用attachBaseContext()方法給mBase對象賦值;
通過以下繼承關系可以看出,Application是繼承ContextWrappe
是創建Application的關鍵,
主要工作:
(1)ContextImpl.createAppContext()實例化ContextImpl ;
(2)mActivityThread.mInstrumentation.newApplication(),內部通過反射實例化Application,並把appContext傳遞過去,通過attach()方法給mBase賦值;
跟Activity類似就不再做分析。
經過分析發現:
1.每個Activity,Service,Application都有一個ContextImpl 類型的成員變數mBase,ContextImpl是Context的實現類。
2.細心的讀者可能發現,Activity,Service,Application都是繼承Context,其實他們本身是一個Context,也都實現了Context的抽象方法,
那麼一個Activity是否就擁有兩個Context呢?
是不是
這樣計算比較合適呢?
下面看下Context中常用的三個方法,
ContextImpl繼承Context,並實現了這三個方法,
Activity間接繼承Context,主要是在ContextWrapper實現了以上三個方法,從源碼中可以看出,最終還是調用了ContextImpl的實現。
下圖可以看出這幾個的關系,ContextWrapper顧名思義就是Context的包裝類(有ContextImpl的成員變數),並且實現了Context,這是一種裝飾者設計模式。當在Activity中調用getAsset()時,其實最終是調用mBase的getAsset()。
Activity間接繼承了Context,是為了擁有跟ContextImpl一樣的功能,但真正起作用的是mBase這個成員變數,所以一個Activity其實就只有一個Context起作用,那就是ContextImpl類型的mBase。
這種計算方法應該是沒有問題呢。
或許有人有這樣的疑問,一個應用不是只有一個Application嗎,為什麼計算公式是加上Application個數?單進程應用來說,一個應用確實只有一個Application,而多進程應用,那麼一個應用就有多個Application,所以應該說一個應用有一個或多個Application,一個進程有一個Application。
另外其他關於Context的常見面試題
1.Activity的this跟getBaseContext區別。
前者是Activity對象本身,後者是通過attachBaseContext傳入的ContextImpl對象mBase,兩者功能是一樣的,通過this最終還是會調到mBase對象中。
2.getApplication和geApplicationContext區別。
兩者都是返回Application對象,前者是Activity和Service裡面的方法,後者是Context中定義的方法。
3.應用組件的構造,onCreate、attachBaseContext的執行順序。
先是組件構造化,接著attachBaseContext,傳入ContextImpl對象,最後是onCreate方法。
4.談談你對Context的理解
先是Context的作用,然後是有幾種Context,Application、Service、Activity的Context有什麼區別以及繼承關系,
最後是mBase變數是如何實例化的。
以上分析有不對的地方,請指出,互相學習,謝謝哦!
Ⅲ Android 關於ContextWrapper的問題
ContextWrapper類 路徑 :\frameworks\base\core\java\android\content\ContextWrapper.java
說明: 正如其名稱一樣,該類只是對Context類的一種包裝,該類的構造函數包含了一個真正的Context引用,即ContextIml對象。
Ⅳ Context摘要(一)
ContextImpl和ContextWrapper都是繼承自Context,在ContextWrapper中依賴了ContextImpl對象(mBase)。這里使用了裝飾者模式,ContextWrapper是裝飾類,對ContextImpl進行包裝,通過使用ContextImpl實現功能。
Application、Service、ContextThemeWrapper繼承自ContextWrapper,它們也是通過ContextImpl實現功能,同時在ContextWrapper的基礎上添加了自己的功能。此外ContextThemeWrapper中包含了主題相關的方法,Activity繼承自ContextThemeWrapper。