【Android組件核心面試題】介紹一下Android中的Context?

介紹一下Android中的Context?

這道題想考察什么?
  1. 是否了解Context的相關(guān)知識(shí)?
考察的知識(shí)點(diǎn)
  1. Context是什么
  2. Context的結(jié)構(gòu)
  3. Context的注意事項(xiàng)
考生應(yīng)該如何回答
一、Context是什么

Context 是 Android 中用的十分常見的一種概念,常被翻譯成上下文,這個(gè)概念在其他的技術(shù)中也有運(yùn)用。Android 官方對(duì)它的解釋,可以理解為應(yīng)用程序環(huán)境中全局信息的接口,它整合了相當(dāng)多系統(tǒng)級(jí)的服務(wù),可以用來得到應(yīng)用中的類、資源,以及可以進(jìn)行應(yīng)用程序級(jí)的調(diào)起操作,比如啟動(dòng) Activity、Service等等,而且 Context 這個(gè)類是 抽象abstract 的,不含有具體的函數(shù)實(shí)現(xiàn)。

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

Context 是維持 Android 程序中各組件能夠正常工作的一個(gè)核心功能類。

Context 本身是一個(gè)抽象類,其主要實(shí)現(xiàn)類為 ContextImpl,另有直系子類兩個(gè):

  • ContextWrapper
  • ContextThemeWrapper

這兩個(gè)子類是 Context 的代理類,它們繼承關(guān)系如下:

context.jpg
ContextImpl類介紹

ContextImpl 是 Context API 的十分常見實(shí)現(xiàn),它為 Activity 和其他應(yīng)用程序組件提供基本上下文對(duì)象,說白了就是 ContextImpl 實(shí)現(xiàn)了抽象類的方法,我們?cè)谑褂?Context 的時(shí)候的方法就是它實(shí)現(xiàn)的。

ContextWrapper類介紹

ContextWrapper 類代理 Context 的實(shí)現(xiàn),將其所有調(diào)用簡(jiǎn)單地代理給另一個(gè) Context 對(duì)象(ContextImpl),可以被分類為修飾行為而不更改原始 Context 的類,其實(shí)就 Context 類的修飾類。真正的實(shí)現(xiàn)類是 ContextImpl,ContextWrapper 里面的方法執(zhí)行也是執(zhí)行 ContextImpl 里面的方法。

ContextThemeWrapper

就是一個(gè)帶有主題的封裝類,比 ContextWrapper 多了主題,它的一個(gè)直接子類就是 Activity。

通過Context的繼承關(guān)系圖結(jié)合我們幾個(gè)開發(fā)中比較常見的類,Activity、Service、Application,所以Context 一共有三種類型,分別是 Application、Activity 和Service,他們分別承擔(dān)不同的責(zé)任,都屬于 Context,而他們具有 Context 的功能則是由ContextImpl 類實(shí)現(xiàn)的。

三、Context的數(shù)量

其實(shí)根據(jù)上面的 Context 類型我們就已經(jīng)可以得出答案了。Context 一共有 Application、Activity 和 Service 三種類型對(duì)象,因此一個(gè)應(yīng)用程序中Context 數(shù)量的計(jì)算公式就可以這樣寫:

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

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

四、Context注意事項(xiàng)

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

最簡(jiǎn)單的例子比如說使用了 Context 的錯(cuò)誤的單例模式:

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;
    }
}

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

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

  • 當(dāng) Application 的 Context 能完成需要的情況下,并且生命周期長(zhǎng)的對(duì)象,優(yōu)先使用 Application 的 Context。

  • 不要讓生命周期長(zhǎng)于 Activity 的對(duì)象持有到 Activity 的引用。

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

五、如何正確回復(fù)以上面試題
  1. 面試官:Android 中有多少類型的 Context,它們有什么區(qū)別?

回答總共有 Activity 、Service、Application 這些 Context 。

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

不同點(diǎn):它們有各自不同的生命周期;在功能上,只有 Activity 顯示界面,正因?yàn)槿绱?,Activity 繼承的是 ContextThemeWrapper 提供一些關(guān)于主題、界面顯示的能力,間接繼承了 ContextWrapper ;而 Applicaiton 、Service 都是直接繼承 ContextWrapper ,所以我們注意一點(diǎn),但凡跟 UI 有關(guān)的,都應(yīng)該用 Activity 作為 Context 來處理,不然要么會(huì)報(bào)錯(cuò),要么 UI 會(huì)使用系統(tǒng)默認(rèn)的主題。

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

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

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

上面的1代表著 Application 的數(shù)量,因?yàn)橐粋€(gè)App中可以有多個(gè)Activity和多個(gè) Service,但是只能有一個(gè) Application。

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

Context 就等于 Application 的大管家,主要負(fù)責(zé):

  • 四大組件的信息交互,包括啟動(dòng) 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ù)庫路徑等。

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

  1. 面試官:ContextImpl 實(shí)例是什么時(shí)候生成的,在 Activity 的 onCreate 里能拿到這個(gè)實(shí)例嗎?

可以。我們開發(fā)的時(shí)候,經(jīng)常會(huì)在 onCreate 里拿到 Application,如果用 getApplicationContext 取,最終調(diào)用的就是 ContextImpl 的 getApplicationContext 方法,如果調(diào)用的是 getApplication 方法,雖然沒調(diào)用到 ContextImpl ,但是返回 Activity 的成員變量 mApplication 和 ContextImpl 的初始化時(shí)機(jī)是一樣的。 再說下它的原理,Activity 真正開始啟動(dòng)是從 ActivityThread.performLaunchActivity 開始的,這個(gè)方法做了這些事:

  • 通過 ClassLoader 去加載目標(biāo) Activity 的類,從而創(chuàng)建 對(duì)象。
  • 從 packageInfo 里獲取 Application 對(duì)象。
  • 調(diào)用 createBaseContextForActivity 方法去創(chuàng)建 ContextImpl。
  • 調(diào)用 activity.attach ( contextImpl , application) 這個(gè)方法就把 Activity 和 Application 以及 ContextImpl 關(guān)聯(lián)起來了,就是上面結(jié)論里說的時(shí)機(jī)一樣。
  • 最后調(diào)用 activity.onCreate 生命周期回調(diào)。

通過以上的分析,我們知道了 Activity 是先創(chuàng)建類,再初始化 Context ,最后調(diào)用 onCreate , 從而得出問題的答案。不僅 Activity 是這樣, Application 、Service 里的 Context 初始化也都是這樣的。

  1. 面試官:ContextImpl 、ContextWrapper、ContextThemeWrapper 有什么區(qū)別?
  • ContextWrapper、ContextThemeWrapper 都是 Context 的代理類,二者的區(qū)別在于 ContextThemeWrapper 有自己的 Theme 以及 Resource,并且 Resource 可以傳入自己的配置初始化。
  • ContextImpl 是 Context 的主要實(shí)現(xiàn)類,Activity、Service 和 Application 的 Base Context 都是由它建立的,即 ContextWrapper 代理的就是 ContextImpl 對(duì)象本身。
  • ContextImpl 和 ContextThemeWrapper 的主要區(qū)別是, ContextThemeWrapper 有 Configuration 對(duì)象,Resource 可以根據(jù)這個(gè)對(duì)象來初始化。
  • Service 和 Application 使用同一個(gè) Recource,和 Activity 使用的 Resource 不同。
  1. 面試官:Activity Context、Service Context、Application Context、Base Context 有什么區(qū)別?
  • Activity、Service 和 Application 的 Base Context 都是由 ContextImpl 創(chuàng)建的,且創(chuàng)建的都是 ContextImpl 對(duì)象,即它們都是 ContextImpl 的代理類 。
  • Service 和 Application 使用相同的Recource,和 Activity 使用的 Resource 不同。
  • getApplicationContext 返回的就是 Application 對(duì)象本身,一般情況下它對(duì)應(yīng)的是應(yīng)用本身的 Application 對(duì)象,但也可能是系統(tǒng)的某個(gè) Application。
  1. 面試官:為什么不推薦使用 BaseContext?
  • 對(duì)于 Service 和 Application 來說,不推薦使用 Base Context,是擔(dān)心用戶修改了 Base Context 而導(dǎo)致出現(xiàn)錯(cuò)誤。
  • 對(duì)于 Activity 而言,除了擔(dān)心用戶的修改之外,Base Context 和 Activity 本身對(duì)于 Reource 以及 Theme 的相關(guān)行為是不同的(如果應(yīng)用了 Configuration 的話),使用 Base Context 可能出現(xiàn)無法預(yù)期的現(xiàn)象。
  1. 面試官:ContentProvider 里的 Context 是什么時(shí)候初始化的呢?

ContentProvider 不是 Context ,但是它有一個(gè)成員屬性 mContext ,是通過構(gòu)造函數(shù)傳入的。那么這個(gè)問題就變成了,ContentProvider 什么時(shí)候創(chuàng)建。應(yīng)用創(chuàng)建 Application 是通過執(zhí)行 ActivityThread.handleBindApplication 方法,這個(gè)方法的相關(guān)流程有:

  • 創(chuàng)建 Application
  • 初始化 Application 的 Context
  • 執(zhí)行 installContentProviders 并傳入剛創(chuàng)建好的 Application 來創(chuàng)建 ContentProvider
  • 執(zhí)行 Application.onCreate

得出結(jié)論,ContentProvider 的 Context 是在 Applicaiton 創(chuàng)建之后,但是 onCreate 方法調(diào)用之前初始化的。

  1. 面試官:BroadcastReceiver 里的 Context是哪來的?

廣播接收器,分動(dòng)態(tài)注冊(cè)和靜態(tài)注冊(cè)。

  • 動(dòng)態(tài)注冊(cè)很簡(jiǎn)單,在調(diào)用 Context.registerReceiver 動(dòng)態(tài)注冊(cè) BroadcastReceiver 時(shí),會(huì)生成一個(gè) ReceiverDispatcher 會(huì)持有這個(gè) Context ,這樣當(dāng)有廣播分發(fā)到它時(shí),執(zhí)行 onReceiver 方法就可以把 Context 傳遞過去了。當(dāng)然,這也是為什么不用的時(shí)候要 unregisterReceiver 取消注冊(cè),不然這個(gè) Context 就泄漏了哦。
  • 靜態(tài)注冊(cè)時(shí),在分發(fā)的時(shí)候最終執(zhí)行的是 ActivityThread.handleReceiver ,這個(gè)方法直接通過 ClassLoader 去創(chuàng)建一個(gè) BroadcastReceiver 的對(duì)象,而傳遞給 onReceiver 方法的 Context 則是通過 context.getReceiverRestrictedContext() 生成的一個(gè)以 Application 為 mBase 的 ContextWrapper。注意這邊的 Context 不是 Application。

最后

有需要面試題的朋友可以關(guān)注一下哇哇,以上都可以分享?。?!

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

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

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