Android中Context的詳細介紹

文章最先發佈於個人微信公衆號 Android_De_Home 中,歡迎你們掃描下面二維碼關注微信公衆獲取更多知識內容。
本文爲sydMobile原創文章,能夠隨意轉載,但請務必註明出處!android

寫過Java程序的都知道,咱們在開發Java程序的時候,程序的入口在main()方法裏面,在main()方法裏面開始寫咱們程序就能夠了,隨便建立類就能夠,可是在Android開發中,你見過 new Activity()、new Service()嗎?沒有把,這些Android中的組件不會像普通的Java對象同樣new一下就能夠建立實例而後本身調用相對應的方法執行了,這就是Android不一樣於普通的Java開發之處,由於Android系統給你定義了一個環境,這些組件都是在這個環境下運行的,有屬於他們的生命週期,這些都是在Android系統中定義好的。這就是Android系統的環境,而這個環境中最重要的就是Context。數據庫

Context的官方介紹

Context介紹

這是在Android開發者官網中關於Context的介紹,很簡單的幾句介紹。 鏈接着有關應用程序的全局信息,這是一個經過Android系統實現的抽象類,它容許訪問特定應用程序的資源和類,以及對應用程序級的操做(好比啓動活動,廣播和接受意圖等等)。它來容許獲取以當前應用爲特徵的資源類型,是一個掌握着一些 資源(系統級別的,好比獲取資源包裏面的內容,圖片,getSystemService等等一類);是一個抽象類,Android系統提供了該抽象類的具體實現類(ContextImpl);經過它咱們就能夠獲取應用程序的資源和類(包括咱們最經常使用的好比啓動Activity,發廣播等等)。 這樣的描述是否是仍是很抽象啊,慢慢往下來看。安全

先看一下Context類的繼承關係 微信

context繼承關係

context繼承關係
這是在Android Studio中查看的繼承關係,在這個繼承關係中,我把幾個咱們經常使用的圈了出來,Application、Activity、Service 這幾個都是屬於Context的子類。 Context的中文翻譯爲:上下文;語境;環境;背景,那麼在咱們開發中咱們經常把他稱爲「上下文」,那麼到底什麼意思呢?咱們在程序中能夠這麼理解,就是咱們的當前對象(Activity、Server、Application等等這些都是對象)在這個程序中所運行的環境,也就是在整個程序中的環境,這些類都是在這個環境中進行運行,完成本身的生命週期的一樣他們也都是這個環境中的一員。

這樣抽象的介紹可能不利於理解,我來舉一個栗子(可能會有點不恰當,可是會便於理解):app

經過API對Context的介紹,咱們能夠知道他掌握着應用程序的全局信息,經過他咱們才能夠訪問使用應用程序的資源和類。把Android系統好比成一個公司,那麼Context能夠認爲是這個公司的老闆直接掌握着公司的資源,Application,Activity、Service等等其餘的一些Context的子類,這些都是老闆找的員工,有老闆會給他們制定特定的工做和做息時間(對應它們的生命週期),這些員工可不是隨隨便便就找的都是須要系統(也就是公司)指定的負責特定的工做任務,因此不能像別的對象同樣隨便new一個,而TextView、View等等能夠看作是這些主要員工工做的時候所須要的用品(好比:書本、桌子、電腦、鼠標等等一些工做輔助用品),相對來講能夠根據所需的功能隨便制定,須要了就能夠new一個(至關於隨便在市場上買一個)。這些全部的員工只有在老闆的帶領下,利用公司的資源才能夠完成工做(開發一個APP)。這樣的栗子應該會更加形象一些吧。模塊化

爲了便於理解我簡單畫了一下Context的繼承關係圖測試

Context繼承關係圖

固然這裏畫的只是重點把咱們經常使用的類畫了出來,Context的直接和間接的子類不少的。可能有人會說了爲何我在Android Studio中查看的時候沒有看到ContextImpl呢,是這樣的:翻譯

ContextImpl關係

圖片(ContextImpl)

上面是我在Android源碼中的截圖ContextImpl其實並非以public class的形式存在,而是class繼承了Context,這個文件是保護文件,就是註解了內部保護文件,因此我 們在IDE中是沒有顯示的,能夠去源碼中查看。設計

ContextImpl類介紹:

圖片(ContextImpl源碼介紹)

ContextImpl是Context API的常見實現,它爲Activity和其餘應用程序組件提供基本上下文對象,說的通俗一點就是ContextImpl實現了抽象類的方法,咱們在使用Context的時候的方法就是它實現的。3d

ContextWrapper類介紹:

圖片(ContextWrapper源碼介紹)

ContextWrapper類代理Context的實現,將其全部調用簡單地委託給另外一個Context對象(ContextImpl),能夠被分類爲修飾行爲而不更改原始Context的類,其實就Context類的修飾類。真正的實現類是ContextImpl,ContextWrapper裏面的方法調用也是調用 ContextImpl裏面的方法。

ContextThemeWrapper

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

經過Context的繼承關係圖結合咱們幾個開發中比較熟悉的類,Activity、Service、Application,因此咱們能夠認爲Context一共有三種類型,分別是Application、Activity和Service,他們分別承擔不一樣的做用,可是都屬於Context,而他們具備Context的功能則是由ContextImpl類實現的。

Context的功能

Context做爲應用程序的運行環境,擁有的功能很是多,彈出Toast、啓動Activity、啓動Service、發送廣播、操做數據庫、獲取資源文件等等還有許多都須要用到Context,因爲Context的具體能力是由ContextImpl類去實現的,所以在絕大多數狀況下,Activity、Service和Application這三種類型的Context是能夠通用的,不過有幾種場景比較特殊,好比啓動Activity,還有彈出Dialog,這個時候因爲安全等緣由的考慮,Android不容許Activity或者Dialog憑空出現,一個Activity的啓動必需要創建在另外一個Activity的基礎上,也就是以此造成的返回棧。而Dialog則必須在一個Activity上面彈出(除非是System Alert類型的Dialog),所以在這種場景下,咱們只能使用Activity類型的Context,不然是會出錯的。(其實使用Application類型的Context也是能夠啓動Activity的,可是不建議這麼使用,會存在關於棧的問題)。

Context數量

上面已經說過了,Context在咱們經常使用的類型中有Application、Activity、Service三種類型所以一個應用程序中Context的數量能夠這麼計算:
Context數量 = Activity數量 +Service數量 + 1(Application數量)

(注意在多進程狀態下Application的數量,網上有人說,有幾個進程就會產生幾Application實例,可是經過輸入getApplicationContext的地址,我發如今不一樣進程中Application的地址是相同的,因而我認爲多進程中Application其實仍是一個,只是在新的進程中Application會從新調用onCreate()進行實例化)

分別分析一下Context的這幾種類型

Application Context的設計

Application這個類也是很常見的,翻譯過來就是應用程序,首先咱們來看關於Application Android源碼是怎麼介紹的。

(圖片 Application源碼)

維持着全局應用程序狀態的基礎類,你能夠經過繼承Application建立一個你本身的Application類。這個類要在AndroidMainfest.xml中的android:name屬性中聲明,而且這個類在任何其餘類被實例化以前被實例化,換句話說Application這個類在咱們打開應用程序的時候會被第一個實例化,Application是單例模式的,經過一些模塊化的方式能夠給你提供許多方法,若是你的單例模式須要一個全局的Context(好比註冊broadcast receive的時候) 包括getApplicationContext()做爲一個參數能夠調用你的單例的getInstance方法。
下面進行代碼測試: 咱們新建一個本身的Application繼承Application,而且在AndroidMainfest.xml文件中聲明一下。

(圖片 Application聲明)

這樣在咱們啓動咱們的APP的時候Android系統就會首先爲咱們建立一個MyApplication咱們在代碼中是如何獲取咱們的Application實例的呢?其實很簡單,Android API給我提供了getApplication方法來獲取

(圖片==MainActivity_getApplication)

這樣咱們就會獲取咱們的Application對象實例了,咱們看一下打印結果:

(圖片==MainActivity_log)

這是在MainActivity中的打印結果。這樣看不刺激,我把與Context有關的幾個方法都寫上,咱們來看一看結果(下面有具體的解釋)

(圖片==Application相關方法)

(圖片==MainActivity_log_內容)

(圖片==ActivitySecond_log_所有內容)

(圖片==ActivityThird_log_所有)

首先說明在這個程序是多進程,在啓動ActivitySecond的時候啓動了一個新的進程,也就是說ActivitySecond是屬於com.syd.mystudydemo:second這個進程。
從結果來看: getApplication、getApplicationContext不管在哪一個Activity中或者在不一樣的進程中獲得的結果都是同樣的。對應的都是MyApplicaton這個對象,不過須要注意的一點是在不一樣的進程中,開啓新的進程的時候MyApplication是會從新調用onCreate方法的。這是須要注意的(有可能在不一樣進程中Application中的變量會賦予不一樣的值)。

那麼getApplication和getApplicationContext有什麼不一樣呢? 這兩個方法所處的類不一樣,getApplicationContext的範圍更大一點,它是在Context裏面的方法,而getApplication這個方法,在Activity中。也就是所任何一個Context對象經過getApplicationContext均可以獲取Context對象,而getApplication只是在Activity對象中能夠得到。

(圖片==getApplicationContext—API說明)

getApplicationContext的API就說的很明白了,獲取一個當前進程的Application對象,它應該被用在這種狀況下,若是你須要這個Context對象和當前所在的context生命週期分離的話(也就是用getApplicationContext獲取的Application生命週期和當前組件沒有關係是和當前進程有關係的)用registerReceive(BroadcastReceiver,IntentFilter)來舉個例子。若是你用一個來自Activity的Context(就是當前Activity,前面說了Activity是Context的子類)去註冊一個receive的話,這個receiver是在這個Activity這個範圍內的,這樣的話就是意味着你在此Activity被銷燬前就要unregister。事實上若是你不這樣作的話,Android系統將會清除你泄露的註冊,移除這個Activity而後log一個錯誤信息。所以,若是你使用Activity上下文來註冊一個靜態的接收者(對進程是全局的,而不是與一個Activity實例關聯的),那麼在你使用的活動被銷燬的任何點上,你的註冊將被刪除。若是你使用getApplicationContext方法返回的Context,這個receiver被註冊在全局狀態在全局狀態下和你的Application有有關聯。所以它將永遠不會被系統unregistered。在receiver與靜態數據有關,而不是某一個特殊的組件的時候這樣作是有必要的。然而使用getApplicationContext在別的地方是很容易引發內存泄露的若是你忘記了unregister,unbind,等等。

因此咱們能夠知道使用getApplicationContext所獲取的Context是和具體某一個組件是沒有關係的,而他和你的整個進程有關係,做用的範圍很大,可是若是你不計條件場景亂用的話,很容易形成內存的泄露。

(圖片==getApplication-API說明)
API說的很明確也很簡單就是返回屬於當前Activity的Application對象,這樣也說明了getApplication是Activity類的方法。

(圖片==getContext--API說明)

返回基礎context對象,經過構造方法或者setBaseContext設置值的context。
其實獲取的這個context對象就是ContextImpl對象,沒錯就是抽象類Context的實現類,其實Context裏面的方法都是在ContextImpl裏面完成的,而Application、Activity、Service這些Context的類型並無實現Context裏面的方法,而是調用的ContextImpl裏面實現的方法。咱們來看看代碼是怎麼實現的吧,Application、Service的父類就是ContextWrapper,而ContextWrapper就是Context的裝飾類,它並無具體的實現Context裏面的抽象方法,而是這樣調用的,上源碼。

(圖片==ContextWrapper源碼)

(圖片==ContextWrapper源碼)

能夠看到ContextWrapper裏面的方法的實現都是mBase.method(),其實調用的都是mBase裏面的方法,而這個地方的mBase就是ContextImpl對象。

咱們能夠看一下這個方法

(圖片 ==ContextWrapper-att方法)

其實mBase都是經過這個方法來賦值的,而這個方法系統會自動調用,在onCreate以前就會調用。咱們須要知道的是,相似於Application、Activity、Service這幾種系統組件,不是經過構造方法new出來的,而是系統調用的,咱們能夠認爲Application是在onCreate後便由Android系統生成了一個Application對象(單例,onCreate不會重複調用,除非多進程中)。

總結:

Context做爲應用程序的運行環境,擁有的功能很是多,彈出Toast、啓動Activity、啓動Service、發送廣播、操做數據庫、獲取資源文件等等這些方法都是在Context中的。

好比咱們常見的方法: getResources(),getPackageManager(),getContentResolver(),getApplicationContext(),getColor(),getDrawable(),obtainStyledAttributes(),getPackageName(),getPackageResourcePath(),getSharedPreferences(),deleteFile(),getExternalCacheDir()
等等方法,你能夠這麼想,Context表示Android系統中咱們所編寫的APP程序的運行環境,它掌握着App的運行資源,全部這些與咱們APP程序有關的資源方法都在Context裏面(這樣咱們在Context的全部子類中就可使用了),只有一些很具體的方法在具體的某個組件中好比Activity是與頁面有關的因此關於窗口的方法在Activity(getWindowsManager()等等相似)裏面。 Context做爲Android中程序的運行環境是Android系統定製的,全部關於他的子類咱們就不要再經過new來生成了,就算你使用new生成了,那樣生成的也僅僅是普通對象,而不是Android系統中的組件,他沒有被系統賦予生命週期,因此是沒有任何價值的。要對Android系統的運行環境有個認識,咱們的App是在一個Context環境下運行的,這些組件是由系統定製的,在環境下賦予的生命週期,這一點是和Java不一樣的。

掃一掃關注微信公衆號,獲取更多幹貨和資源
相關文章
相關標籤/搜索