Android開發之Android Context,上下文(Activity Context, Application Context)
轉載:http://blog.csdn.net/lmj623565791/article/details/40481055
1、Context概念
Context,相信不管是第一天開發Android,還是開發Android的各種老鳥,對於Context的使用一定不陌生~~你在加載資源、啟動一個新的Activity、獲取系統服務、獲取內部文件(夾)路徑、創建View操作時等都需要Context的參與,可見Context的常見性。大家可能會問到底什么是Context,Context字面意思上下文,或者叫做場景,也就是用戶與操作系統操作的一個過程,比如你打電話,場景包括電話程序對應的界面,以及隱藏在背后的數據;
但是在程序的角度Context又是什么呢?在程序的角度,我們可以有比較權威的答案,Context是個抽象類,我們可以直接通過看其類結構來說明答案:
可以看到Activity、Service、Application都是Context的子類;
也就是說,Android系統的角度來理解:Context是一個場景,代表與操作系統的交互的一種過程。
從程序的角度上來理解:Context是個抽象類,而Activity、Service、Application等都是該類的一個實現。
在仔細看一下上圖:Activity、Service、Application都是繼承自ContextWrapper,而ContextWrapper內部會包含一個base context,由這個base context去實現了絕大多數的方法。
2、Context與ApplicationContext
看了標題,千萬不要被誤解,ApplicationContext並沒有這個類,其實更應該叫做:Activity與Application在作為Context時的區別。嗯,的確是這樣的,大家在需要Context的時候,如果是在Activity中,大多直接傳個this,當在匿名內部類的時候,因為this不能用,需要寫XXXActivity.this,很多哥們會偷懶,直接就來個getApplicationContext。那么大家有沒有想過,XXXActivity.this和getApplicationContext的區別呢?
XXXActivity和getApplicationContext返回的肯定不是一個對象,一個是當前Activity的實例,一個是項目的Application的實例。既然區別這么明顯,那么各自的使用場景肯定不同,亂使用可能會帶來一些問題。
下面開始介紹在使用Context時,需要注意的問題。
3、引用的保持
大家在編寫一些類時,例如工具類,可能會編寫成單例的方式,這些工具類大多需要去訪問資源,也就說需要Context的參與。
在這樣的情況下,就需要注意Context的引用問題。
例如以下的寫法:
1 package com.mooc.shader.roundimageview; 2 3 import android.content.Context; 4 5 public class CustomManager 6 { 7 private static CustomManager sInstance; 8 private Context mContext; 9 10 private CustomManager(Context context) 11 { 12 this.mContext = context; 13 } 14 15 public static synchronized CustomManager getInstance(Context context) 16 { 17 if (sInstance == null) 18 { 19 sInstance = new CustomManager(context); 20 } 21 return sInstance; 22 } 23 24 //some methods 25 private void someOtherMethodNeedContext() 26 { 27 28 } 29 }
對於上述的單例,大家應該都不陌生(請別計較getInstance的效率問題),內部保持了一個Context的引用;
這么寫是沒有問題的,問題在於,這個Context哪來的我們不能確定,很大的可能性,你在某個Activity里面為了方便,直接傳了個this;這樣問題就來了,我們的這個類中的sInstance是一個static且強引用的,在其內部引用了一個Activity作為Context,也就是說,我們的這個Activity只要我們的項目活着,就沒有辦法進行內存回收。而我們的Activity的生命周期肯定沒這么長,所以造成了內存泄漏。
那么,我們如何才能避免這樣的問題呢?
有人會說,我們可以軟引用,嗯,軟引用,假如被回收了,你不怕NullPointException么。
把上述代碼做下修改:
1 public static synchronized CustomManager getInstance(Context context) 2 { 3 if (sInstance == null) 4 { 5 sInstance = new CustomManager(context.getApplicationContext()); 6 } 7 return sInstance; 8 }
這樣,我們就解決了內存泄漏的問題,因為我們引用的是一個ApplicationContext,它的生命周期和我們的單例對象一致。
這樣的話,可能有人會說,早說嘛,那我們以后都這么用不就行了,很遺憾的說,不行。上面我們已經說過,Context和Application Context的區別是很大的,也就是說,他們的應用場景(你也可以認為是能力)是不同的,並非所有Activity為Context的場景,Application Context都能搞定。
下面就開始介紹各種Context的應用場景。
4、Context的應用場景
大家注意看到有一些NO上添加了一些數字,其實這些從能力上來說是YES,但是為什么說是NO呢?下面一個一個解釋:
數字1:啟動Activity在這些類中是可以的,但是需要創建一個新的task。一般情況不推薦。
數字2:在這些類中去layout inflate是合法的,但是會使用系統默認的主題樣式,如果你自定義了某些樣式可能不會被使用。
數字3:在receiver為null時允許,在4.2或以上的版本中,用於獲取黏性廣播的當前值。(可以無視)
注:ContentProvider、BroadcastReceiver之所以在上述表格中,是因為在其內部方法中都有一個context用於使用。
好了,這里我們看下表格,重點看Activity和Application,可以看到,和UI相關的方法基本都不建議或者不可使用Application,並且,前三個操作基本不可能在Application中出現。實際上,只要把握住一點,凡是跟UI相關的,都應該使用Activity做為Context來處理;其他的一些操作,Service,Activity,Application等實例都可以,當然了,注意Context引用的持有,防止內存泄漏。
5、總結
好了,到此,Context的分析基本完成了,希望大家在以后的使用過程中,能夠稍微考慮下,這里使用Activity合適嗎?會不會造成內存泄漏?這里傳入Application work嗎?
android開發中的 Activity 與 Context 區別與聯系
Context 是 Application /Activity /Service的基類
Intent(Context , Class);
Activity中的上下文Context是隨着活動的產生而產生,隨其消亡而消亡,但是整個應用程序的上下文Context這是伴隨着整個應用程序而存在的,無論活動的存活與否都影響不到這個上下文。
獲取上下文的 Context : this.getApplicationContext()
獲取Activity的Context
mContext = FavoritesSongActivity.this
Activity 的作用域
雖然Context神通廣大,但並不是隨便拿到一個Context實例就可以為所欲為,它的使用還是有一些規則限制的。由於Context的具體實例是由ContextImpl類去實現的,因此在絕大多數場景下,Activity、Service和Application這三種類型的Context都是可以通用的。不過有幾種場景比較特殊,比如啟動Activity,還有彈出Dialog。出於安全原因的考慮,Android是不允許Activity或Dialog憑空出現的,一個Activity的啟動必須要建立在另一個Activity的基礎之上,也就是以此形成的返回棧。而Dialog則必須在一個Activity上面彈出(除非是System Alert類型的Dialog),因此在這種場景下,我們只能使用Activity類型的Context,否則將會出錯。
通常我們想要獲取Context對象,主要有以下四種方法
1:View.getContext,返回當前View對象的Context對象,通常是當前正在展示的Activity對象。
2:Activity.getApplicationContext,獲取當前Activity所在的(應用)進程的Context對象,通常我們使用Context對象時,要優先考慮這個全局的進程Context。
3:ContextWrapper.getBaseContext():用來獲取一個ContextWrapper進行裝飾之前的Context,可以使用這個方法,這個方法在實際開發中使用並不多,也不建議使用。
4:Activity.this 返回當前的Activity實例,如果是UI控件需要使用Activity作為Context對象,但是默認的Toast實際上使用ApplicationContext也可以。
getApplication()和getApplicationContext()
上面說到獲取當前Application對象用getApplicationContext,不知道你有沒有聯想到getApplication(),這兩個方法有什么區別?相信這個問題會難倒不少開發者。