1、Context概念
其實一直想寫一篇關于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去實作了絕大多數的方法。
先扯這麼多,有能力了會從别的角度去審視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的引用問題。
例如以下的寫法:
[java] view plain copy
- package com.mooc.shader.roundimageview;
- import android.content.Context;
- public class CustomManager
- {
- private static CustomManager sInstance;
- private Context mContext;
- private CustomManager(Context context)
- {
- this.mContext = context;
- }
- public static synchronized CustomManager getInstance(Context context)
- {
- if (sInstance == null)
- {
- sInstance = new CustomManager(context);
- }
- return sInstance;
- }
- //some methods
- private void someOtherMethodNeedContext()
- {
- }
- }
對于上述的單例,大家應該都不陌生(請别計較getInstance的效率問題),内部保持了一個Context的引用;
這麼寫是沒有問題的,問題在于,這個Context哪來的我們不能确定,很大的可能性,你在某個Activity裡面為了友善,直接傳了個this;這樣問題就來了,我們的這個類中的sInstance是一個static且強引用的,在其内部引用了一個Activity作為Context,也就是說,我們的這個Activity隻要我們的項目活着,就沒有辦法進行記憶體回收。而我們的Activity的生命周期肯定沒這麼長,是以造成了記憶體洩漏。
那麼,我們如何才能避免這樣的問題呢?
有人會說,我們可以軟引用,嗯,軟引用,假如被回收了,你不怕NullPointException麼。
把上述代碼做下修改:
[java] view plain copy
- public static synchronized CustomManager getInstance(Context context)
- {
- if (sInstance == null)
- {
- sInstance = new CustomManager(context.getApplicationContext());
- }
- return sInstance;
- }
這樣,我們就解決了記憶體洩漏的問題,因為我們引用的是一個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引用的持有,防止記憶體洩漏。