一、Context類圖:
上面就是有關Context的幾個關鍵類的組織結構,一目瞭然,不多作解釋,接下來看看Android源碼中的類註釋。
二、類註釋:
Context:類註釋解釋的很清楚,Context是應用程序環境的全局信息的接口。這是一個抽象類,它的實現是由Android系統提供的。它允許訪問特定的應用程序的資源和類,以及對應用級操作的追加調用,例如啓動活動、廣播和接收意圖等。
ContextImpl:上下文API的通用實現,它爲活動和其他應用程序組件提供基本上下文對象。
ContextWrapper:代理上下文的實現,簡單地將所有的調用委託給另一個上下文。可以在不更改原始上下文的情況下對其進行子類修改。
ContextThemeWrapper:一個上下文包裝器,允許你修改或替換包裝上下文的主題。(ps:AndroidManifest.xml中主題的屬性功能就是這個類來實現的)
Application:維護全局應用程序狀態的基類。你可以通過創建一個子類來提供自己的實現,並在AndroidManifest.xml中指定這個子類的完全限定名稱爲“android:name”屬性的值。這個應用程序類,或者應用程序類的子類,在創建應用程序/包的過程中,在任何其他類被實例化之前先被實例化。(ps:具體的獲取全局Context後面再講)
Activity和Service相信大家已經非常熟悉了,在此就不多作解釋了,感興趣的朋友自己去看源碼註釋。
三、Context數量:
一個應用程序有幾個Context呢?從上面的關係圖我們得知,Context的具體實現子類就是:Application,Activity,Service,所以一個Android應用程序的Context種類= 3種,Context數量=Application數量(1個)+Activity數量 + Service數量。
四、Context能幹什麼:
Context能幹什麼,有哪些功能用到了Context?這個確實有點多,例如,彈出Toast、顯示dialog、layout inflation、啓動Activity、啓動Service、發送廣播、操作數據庫、加載資源等等都需要用到Context。
上面是Context的常見的幾種應用場景,我簡單解釋一下:
Show dialog:出於安全原因的考慮,Android是不允許Dialog憑空出現的,Dialog必須在一個Activity上面彈出(除非是System Alert類型的Dialog),所以只能用Activity的Context。
Start activity:在Android系統中,因爲Activity涉及到Activity棧,所以一個Activity的啓動必須要建立在另一個Activity的基礎之上,也就是以此形成的返回棧。因此在這種場景下,我們最好使用Activity類型的Context。
Layout inflation:在layout inflate時使用application和service是合法的,但是會使用系統默認的主題樣式,如果你自定義了某些樣式可能不會被使用。
總結一下:只要把握住一點,凡是跟UI相關的,都應該使用Activity做爲Context來處理;其他的一些操作,Service,Activity,Application等實例都可以,當然了,要注意防止內存泄漏,那內存泄漏是怎麼發生的呢,如何才能避免呢,請接着看。
五、Context引起的內存泄露
Context不能隨便亂用,用的不好有可能會引起內存泄露的問題,內存泄漏一般發生的原因是錯誤的引用持有,下面就示例兩種錯誤的引用持有方式。
1.錯誤的單例模式
public class Singleton {
private static Singleton instance;
private Context mContext;
private Singleton(Context context) {
this.mContext = context;
}
public static Singleton getInstance(Context context) {
if (instance == null) {
instance = new Singleton(context);
}
return instance;
}
}
這是一個非線程安全的單例模式,instance作爲靜態對象,其生命週期要長於普通的對象,其中也包含Activity,假如Activity A去getInstance獲得instance對象,傳入this,常駐內存的Singleton保存了你傳入的Activity A對象,並一直持有,即使Activity被銷燬掉,但因爲它的引用還存在於一個Singleton中,就不可能被GC掉,這樣就導致了內存泄漏。
2.View持有Activity引用
public class MainActivity extends Activity {
private static Drawable mDrawable;
@Override
protected void onCreate(Bundle saveInstanceState) {
super.onCreate(saveInstanceState);
setContentView(R.layout.activity_main);
ImageView iv = new ImageView(this);
mDrawable = getResources().getDrawable(R.drawable.ic_launcher);
iv.setImageDrawable(mDrawable);
}
}
有一個靜態的Drawable對象當ImageView設置這個Drawable時,ImageView保存了mDrawable的引用,而ImageView傳入的this是MainActivity的mContext,因爲被static修飾的mDrawable是常駐內存的,MainActivity是它的間接引用,MainActivity被銷燬時,也不能被GC掉,所以造成內存泄漏。
六、正確使用Context:
一般Context造成的內存泄漏,幾乎都是當Context銷燬的時候,卻因爲被引用導致銷燬失敗,而Application的Context對象可以理解爲隨着進程存在的,所以我們總結出使用Context的正確姿勢:
1.當Application的Context能搞定的情況下,並且生命週期長的對象,優先使用Application的Context。
2.不要讓生命週期長於Activity的對象持有到Activity的引用。
3.儘量不要在Activity中使用非靜態內部類,因爲非靜態內部類會隱式持有外部類實例的引用,如果使用靜態內部類,將外部實例引用作爲弱引用持有。
4.因爲static修飾的對象常駐內存,生命週期比一般的對象要長,所以在Context中使用static時多想一下是否是必須的,如果可以,儘量不要用。
七、獲取全局Context:
一方面,爲了防止內存泄漏,我們在使用Context時,優先使用Application的Context,另一方面,隨着應用程序的架構逐漸開始複雜起來的時候,很多的邏輯代碼都將脫離Activity,service類,但此時你又恰恰需要使用Context,所以就需要在任何地方都能很方便的獲取到Application的Context。
Android提供了一個Application類,每當應用程序啓動的時候,系統就會自動將這個類進行初始化。而我們可以定製一個自己的Application類,以便於管理程序內一些全局的狀態信息,比如說全局Context。因爲Application全局只有一個,它本身就已經是單例了,無需再用單例模式去爲它做多重實例保護。
public class MyApplication extends Application {
private static Context instance;
@Override
public void onCreate()
{
instance = getApplicationContext();
}
public static Context getContext()
{
return instance;
}
}
接下來我們需要告知系統,當程序啓動的時候應該初始化MyApplication類,而不是默認的Application類。這一步也很簡單,在AndroidManifest.xml文件的<application>標籤下進行指定就可以了,代碼如下所示:
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme"
android:name="com.dapeng.MyApplication">
</application>
這樣我們就已經實現了一種全局獲取Context的機制,之後不管你想在項目的任何地方使用Context,只需要調用一下MyApplication.getContext()就可以了。
本文參考閱讀:
http://blog.csdn.net/lmj623565791/article/details/40481055/
http://blog.csdn.net/yanbober/article/details/45967639
http://blog.csdn.net/guolin_blog/article/details/47028975
http://www.jianshu.com/p/94e0f9ab3f1d#