Android Context 上下文及反射隨手可得的Application對象

Android Context 上下文及反射隨手可得的Application對象

本文大多數內容翻譯自:http://www.doubleencore.com/2013/06/context/ 我重新組織了下內容以及結構,建議大家儘可能看下原文。


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的引用問題。
例如以下的寫法:

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麼。
把上述代碼做下修改:


public static synchronized CustomManager getInstance(Context context)  
    {  
        if (sInstance == null)  
        {  
            sInstance = new CustomManager(context.getApplicationContext());  
        }  
        return sInstance;  
    }  

另外,通過反射的方式,獲取系統類的靜態字段,過程如下:
- 通過RuntimeInit類,獲取到mApplicationObject靜態字段,這個字段的類型爲android.app.ActivityThread ApplicationThreadApplicationThreadthis 0字段,注意這個是編譯器生成的,這個字段的類型是android.app.ActivityThread;
- 通過ActivityThread類,獲取其mInitialApplication字段,這個字段即是Application對象;

這個方法可以兼容1.6至5.1(之後的固件應該也能兼容)

Applicatioin app = RuntimeInit.mApplicationObject.this$0.mInitialApplication;

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嗎?

文章轉載於:
http://blog.csdn.net/lmj623565791/article/details/40481055
http://blog.csdn.net/nugongahou110/article/details/46928537

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章