天天看點

Context你真的了解嗎?前言

前言

Context是什麼?

Context提供了關于應用環境全局資訊的接口。它是一個抽象類,它的執行被android系統所提供。它允許擷取以應用為特征的資源和類型,是一個統領一些資源(應用環境變量等)的上下文。

Context原理圖!

Context你真的了解嗎?前言

原理圖分析:

Context是一個抽象類,Context有兩個子類:ContextImpl和ContextWrapper,兩個類的差別ContextImpl類是具體實作功能的類,而ContextWrapper隻是用來進行一些包裝等,但是會看到ContextThemeWrapper和Application還有Service都是繼承了ContextWrapper,不是說ContextImpl是具體實作内容的類嗎?因為那三個内容初始化過程中都會建立ContextImpl對象,由ContextImpl實作Context中的方法,看原理圖中ContextWrapper指向ContextImpl的箭頭就是這個意思。

Context的擷取方式

  • View.getContext,傳回目前View的Context對象,通常就是目前正在顯示的Activity對象
  • Activity.this,傳回目前Activity的執行個體,一般UI相關的使用這個作為Context對象(Toast有些特殊,可以直接使用ApplicationContext,因為Toast的Window是系統Window)
  • Activity.getApplicationContext()擷取目前Activity所在應用程序的Context對象

    注意:這裡還有個getApplication,getApplication和getApplicationContext有什麼差別?

    getApplication和getApplicationContext擷取的是同一個執行個體;getApplication方法的語義更強,但是隻有在Activity或者Service中使用Application;但是如果在BroadcastReceiver等其它場景擷取執行個體,隻能借助getApplicationContext

  • ContextWrapper.getBaseContext()知道有這個擷取方式就行

Context的使用場景

Context作用域 Application Activity Service
show a dialog No YES No
strat an Activity 不推薦 YES 不推薦
Layout Inflation 不推薦 YES 不推薦
Start a Service YES YES YES
Send a Broadcast YES YES YES
Register Broadcast Receiver YES YES YES
Load Resource Values YES YES YES

上面的表格時對整體Context使用場景的一個彙總,在這裡隻對前三條特殊的進行說明:

- show a dialog

Dialog時不允許憑空出現,必須有對應Activity,才能在Window上顯示(有視圖的地方就有),是以需要使用Activity的Context,Application和Service不能顯示Dialog。但是在Activity中,如果非要使用Application的context來開啟dialog一定不可以嗎?不是的,需要改變dialog的一個屬性,修改為在系統級别Window顯示,具體請看部落格:https://blog.csdn.net/xgangzai/article/details/81390630中的Dialog的Window建立過程

- start an Activity

啟動一個Activity,用Application的Context會報錯,這是因為Application的context并沒有所謂的任務棧。新啟動的Activity找不到任務棧無法入棧。但是這種辦法也可以解決,為待啟動的Activity指定一個flag标記位,這樣啟動的時候會為它建立一個新的任務棧,這種Activity是以singleTask方式啟動。(不推薦)

- Layout Inflation

使用Application中的context或者service中的context,自定義的主題樣式不會被使用。

總結:一般和UI相關的都使用Activity的context。

Context相關問題

面試中可能會問到一個應用程式中正常情況下會包含多少個Context?

Context的數量=Activity的數量+Service的數量+1

其中1指的是Application的數量

四大元件中BroadcastReceiver,ContentProvide并不是Context的子類,在使用的時候都是通過其它地方傳遞過去。

Activity為什麼不能直接new出來?

在剛接到項目代碼時候,代碼中竟然出現了在變量聲明的時候,直接new出來一個Activity,當時真的很無語。那麼說一下Activity為什麼不能直接new出一個對象呢?

Android程式和java程式不一樣,java中建立一個類,寫一個Main方法就可以跑了,Android應用時基于元件的應用設計模式,元件的運作還必須有一個完整的工程環境,環境我們在上面說了也就是Context,隻有在這種上下文環境(Context)下,Activity,Service,BroadcastReceiver等系統元件才能正常工作,是以這些元件直接采用java普通的對象建立形式new出來時不可行的。

Context造成的記憶體洩漏?

錯誤的單例模式

傳進來Activity的context之後一直儲存Activity傳遞進來的context引用,即使Activity被銷毀掉也不會回收context引用。造成記憶體洩漏,(簡單的說,Activity的context往出傳的時候多想想,盡量不要)

代碼如下:

public class Singleton(){
    private static Single instance;
    private Context context;

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

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

總結,如何正确使用Context

  • Application的Context能解決的情況下,優先使用Application的context
  • 不要讓生命周期長于Activity的對象持有到Activity的引用,不然會造成記憶體洩漏
  • 靜态内部類使用的時候要注意,進了不要再Activity中使用,因為非靜态内部類會隐式持有外部執行個體的引用,如果使用靜态内部類,将外部執行個體引用為弱引用持有(聯想ListView使用的時候Adapter的建立)

android記憶體洩漏相關内沒進行詳細總結,可參考文章學習:

https://blog.csdn.net/u010198148/article/details/51649852

繼續閱讀