Android Context

Context是什么?

Activity mActivity =new Activity()

作為Android開發(fā)者,不知道你有沒有思考過這個問題,Activity可以new嗎?Android的應(yīng)用程序開發(fā)采用JAVA語言,Activity本質(zhì)上也是一個對象,那上面的寫法有什么問題呢?估計很多人說不清道不明。Android程序不像Java程序一樣,隨便創(chuàng)建一個類,寫個main()方法就能運行,Android應(yīng)用模型是基于組件的應(yīng)用設(shè)計模式,組件的運行要有一個完整的Android工程環(huán)境,在這個環(huán)境下,Activity、Service等系統(tǒng)組件才能夠正常工作,而這些組件并不能采用普通的Java對象創(chuàng)建方式,new一下就能創(chuàng)建實例了,而是要有它們各自的上下文環(huán)境,也就是我們這里討論的Context??梢赃@樣講,Context是維持Android程序中各組件能夠正常工作的一個核心功能類。

源碼中的注釋是這么來解釋Context:Context提供了關(guān)于應(yīng)用環(huán)境全局信息的接口。它是一個抽象類,它的執(zhí)行被Android系統(tǒng)所提供。它允許獲取以應(yīng)用為特征的資源和類型,是一個統(tǒng)領(lǐng)一些資源(應(yīng)用程序環(huán)境變量等)的上下文。
Context描述一個應(yīng)用程序環(huán)境的信息(即上下文);Android提供了該抽象類的具體實現(xiàn)類;通過它我們可以獲取應(yīng)用程序的資源和類(包括應(yīng)用級別操作,如啟動Activity,發(fā)廣播,接受Intent等)。

Context有什么作用?

a.四大組件的交互,包括啟動 Activity、Broadcast、Service,獲取 ContentResolver 等。
b.獲取系統(tǒng)/應(yīng)用資源,包括 AssetManager、PackageManager、Resources、System Service 以及 color、string、drawable 等。
c.文件,包括獲取緩存文件夾、刪除文件、SharedPreference 相關(guān)等。
d.數(shù)據(jù)庫(SQLite)相關(guān),包括打開數(shù)據(jù)庫、刪除數(shù)據(jù)庫、獲取數(shù)據(jù)庫路徑等。

Context結(jié)構(gòu)

Context結(jié)構(gòu)

a.ContextImpl類

ContextImpl繼承Context抽象類,實現(xiàn)了Context類中的抽象方法,是Context的具體實現(xiàn)類;它為Activity和其他應(yīng)用程序組件提供基本上下文對象,應(yīng)用中使用 Context的時候的方法就是它實現(xiàn)的。

b.ContextWrapper

ContextWrapper繼承Context抽象類,作為Context類的包裝類,其內(nèi)部維護了一個Context類型的成員變量mBase,mBase最終會指向一個ContextImpl對象,ContextWrapper的方法其內(nèi)部依賴mBase,ContextWrapper是Context類的修飾類(裝飾器模式),真正的實現(xiàn)類是 ContextImpl,ContextWrapper 里面的方法調(diào)用也是調(diào)用 ContextImpl 里面的方法。

c.ContextThemeWrapper

ContextThemeWrapper繼承ContextWrapper,因此也擁有一個Context類型的成員變量mBase,mBase最終會指向一個ContextImpl對象,它的一個直接子類就是 Activity,所以Activity也就擁有了Context提供的所有功能。

d.Context類型

通過 Context 的繼承關(guān)系圖可以看到,Activity、Service、Application都是Context的子類,可以認為Context一共有三種類型,分別是 Application、Activity 和Service,他們分別承擔(dān)不同的作用,但是都屬于 Context,而他們具有 Context 的功能則是由ContextImpl 類實現(xiàn)的。
1.Application:繼承ContextWrapper,因此也擁有一個Context類型的成員變量mBase,mBase最終會指向一個ContextImpl對象,ContextImpl是Context的具體實現(xiàn)類,所以Application也就擁有了Context提供的所有功能。
2.Service:繼承ContextWrapper,因此也擁有一個Context類型的成員變量mBase,mBase最終會指向一個ContextImpl對象,ContextImpl是Context的具體實現(xiàn)類,所以Service也就擁有了Context提供的所有功能。
3.Activity:繼承ContextThemeWrapper,ContextThemeWrapper繼承ContextWrapper,因此也擁有一個Context類型的成員變量mBase,mBase最終會指向一個ContextImpl對象,ContextImpl是Context的具體實現(xiàn)類,所以Activity也就擁有了Context提供的所有功能。

Context作用域

Context作用域

只有 Activity 顯示界面,正因為如此,Activity 繼承的是 ContextThemeWrapper 提供一些關(guān)于主題,界面顯示的能力,間接繼承了 ContextWrapper ;一般來說,凡是跟 UI 有關(guān)的,都應(yīng)該用 Activity 作為 Context 來處理,否則要么會報錯,要么 UI 會使用系統(tǒng)默認的主題。

如何獲取Context

通常我們想要獲取Context對象,主要有以下四種方法

  1. View.getContext,返回當(dāng)前View對象的Context對象,通常是當(dāng)前正在展示的Activity對象。

  2. Activity.getApplicationContext,獲取當(dāng)前Activity所在的(應(yīng)用)進程的Context對象,也就是Application對象。通常我們使用Context對象時,要優(yōu)先考慮這個全局的進程Context。

  3. ContextWrapper.getBaseContext():用來獲取一個ContextWrapper進行裝飾之前的Context,可以使用這個方法,這個方法在實際開發(fā)中使用并不多,也不建議使用。

  4. Activity.this 返回當(dāng)前的Activity實例,如果是UI控件需要使用Activity作為Context對象,但是默認的Toast實際上使用ApplicationContext也可以。

Context注意事項

Context 如果使用不恰當(dāng)很容易引起內(nèi)存泄露問題。

最簡單的例子比如說引用了 Context 的錯誤的單例模式:

public class Singleton {
    private static Singleton instance;
    private Context mContext;

    private Singleton(Context context) {
        this.mContext = context;
    }

    public static Synchronized Singleton getInstance(Context context) {
        if (instance == null) {
            instance = new Singleton(context);
        }
        return instance;
    }
}

上述代碼中,我們使得了一個靜態(tài)對象持有 Context 對象,而靜態(tài)數(shù)據(jù)的生命一般是長于普通數(shù)據(jù)的,因此當(dāng) Context 被銷毀(例如假設(shè)這里持有的是 Activity 的上下文對象,當(dāng) Activity 被銷毀的時候),因為 instance 仍然持有 Context 的引用,導(dǎo)致 Context 雖然被銷毀了但是卻無法被GC機制回收,因為造成內(nèi)存泄露問題。

而一般因為Context所造成的內(nèi)存泄漏,基本上都是 Context 已經(jīng)被銷毀后,卻因為被引用導(dǎo)致GC回收失敗。但是 Application 的 Context 對象卻會隨著當(dāng)前進程而一直存在,所以使用 Context 是應(yīng)該注意:

  • 當(dāng) Application 的 Context 能搞定的情況下,并且生命周期長的對象,優(yōu)先使用 Application 的 Context。

  • 不要讓生命周期長于 Activity 的對象持有到 Activity 的引用。

  • 盡量不要在 Activity 中使用非靜態(tài)內(nèi)部類,因為非靜態(tài)內(nèi)部類會隱式持有外部類實例的引用,如果使用靜態(tài)內(nèi)部類,將外部實例引用作為弱引用持有。

常見問題

  1. 面試官:Android 中有哪些類型的 Context,它們有什么區(qū)別?

應(yīng)用有 Activity 、Service、Application 這些 Context 。

共同點:它們都是 ContextWrapper 的子類,而 ContextWrapper 的成員變量 mBase 可以用來存放系統(tǒng)實現(xiàn)的 ContextImpl,這樣我們在調(diào)用如 Activity 的 Context 方法時,都是通過靜態(tài)代理的方式最終調(diào)用到 ContextImpl 的方法。我們調(diào)用 ContextWrapper 的 getBaseContext 方法就能拿到 ContextImpl 的實例。

不同點:它們有各自不同的生命周期;在功能上,只有 Activity 顯示界面,正因為如此,Activity 繼承的是 ContextThemeWrapper 提供一些關(guān)于主題,界面顯示的能力,間接繼承了 ContextWrapper ;而 Applicaiton 、Service 都是直接繼承 ContextWrapper ,所以我們要記住一點,凡是跟 UI 有關(guān)的,都應(yīng)該用 Activity 作為 Context 來處理,否則要么會報錯,要么 UI 會使用系統(tǒng)默認的主題。

  1. 面試官:一個APP應(yīng)用里有幾個 Context 呢?

Context 一共有 Application 、Activity 和 Service 三種類型,因此一個應(yīng)用程序中 Context 數(shù)量的計算公式就可以這樣寫:

Context 數(shù)量 = Activity 數(shù)量 + Service 數(shù)量 + 1

上面的1代表著 Application 的數(shù)量,因為一個應(yīng)用程序中可以有多個Activity和多個 Service,但是只能有一個 Application。

  1. 面試官:Android 開發(fā)過程中,Context 有什么用?

Context 就相當(dāng)于 Application 的大管家,主要負責(zé):

  • 四大組件的交互,包括啟動 Activity、Broadcast、Service,獲取 ContentResolver 等。
  • 獲取系統(tǒng)/應(yīng)用資源,包括 AssetManager、PackageManager、Resources、System Service 以及 color、string、drawable 等。
  • 文件,包括獲取緩存文件夾、刪除文件、SharedPreference 相關(guān)等。
  • 數(shù)據(jù)庫(SQLite)相關(guān),包括打開數(shù)據(jù)庫、刪除數(shù)據(jù)庫、獲取數(shù)據(jù)庫路徑等。

其它輔助功能,比如設(shè)置 ComponentCallbacks,即監(jiān)聽配置信息改變、內(nèi)存不足等事件的發(fā)生

參考鏈接

http://www.itdecent.cn/p/60358c746a91

https://zhuanlan.zhihu.com/p/143210217

https://baijiahao.baidu.com/s?id=1713615374107643571&wfr=spider&for=pc

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容