Android 淺析Context

我們平時在開發Android應用程序時一直都在使用Context比如啓動一個Activity,大部分人不知道爲什麼要使用context來啓動Activity,不能直接new一個activity而要使用context來啓動,一個App到底有多少個Context等等的細節,尤其是Context使用不當還會造成內存泄漏,所以有必要寫篇文章總結下。

Android應用模型

磨刀不誤砍柴工,先介紹一下Android的應用模型,Android應用是用java寫的,那麼他和java程序有什麼區別呢?其實Android不像java一樣隨便寫個類,有個main方法,就能運行,它是基於組件的應用模式,這些組件包括activity、service等,一個個組件協同合作,而context是他們協同合作的基礎,它們不能通過new來產生一個組件,而是要有它們各自的上下文環境,也就是我們這裏討論的Context。可以這樣講,Context是維持Android程序中各組件能夠正常工作的一個核心功能類。想象一下,Android應用就像一個公司,各個組件就是各個職員,而context就是公司環境,職員需要在這個環境中進行工作,即便新的職員來公司了也不能不在這個環境中工作(即不能new一個)。

理解Context

Context:上下文環境,其實這是一個很抽象的解釋,需要具體代入一些生活場景,初學者纔會理解,就比如聊天的時候,對面發了一堆消息來了,而你要回他的話,肯定要考慮他發的話(上文環境),再考慮自己怎麼回答(下文環境),這就是聊天環境(上下文環境),Android程序員把“場景”抽象爲Context類,他們認爲用戶和操作系統的每一次交互都是一個場景,比如打電話、發短信,這些都是一個有界面的場景,還有一些沒有界面的場景,比如後臺運行的服務(Service)。

Context的繼承結構分析

context繼承結構圖
Context的繼承結構還是稍微有點複雜的,可以看到,直系子類有兩個,一個是ContextWrapper,一個是ContextImpl。那麼從名字上就可以看出,ContextWrapper是上下文功能的封裝類,而ContextImpl則是上下文功能的實現類。而ContextWrapper又有三個直接的子類,ContextThemeWrapper、Service和Application。其中,ContextThemeWrapper是一個帶主題的封裝類,而它有一個直接子類就是Activity。

那麼在這裏我們至少看到了幾個所比較熟悉的面孔,Activity、Service、還有Application。由此,其實我們就已經可以得出結論了,Context一共有三種類型,分別是Application、Activity和Service。這三個類雖然分別各種承擔着不同的作用,但它們都屬於Context的一種,而它們具體Context的功能則是由ContextImpl類去實現的。
至此我們就可以得出一個結論一個應用中的context數量應該是Activity、Service、還有Application數量之和。

Context使用建議

但Context並不能隨便亂用,用的不好有可能會引起內存泄露的問題,下面就示例兩種錯誤的引用方式。

錯誤的單例模式

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掉,這樣就導致了內存泄漏。
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中使用非靜態內部類,因爲非靜態內部類會隱式持有外部類實例的引用,如果使用靜態內部類,將外部實例引用作爲弱引用持有。

本文旨在淺析Context,也算是自己學習的一個總結,如要深入研究可移步
http://blog.csdn.net/guolin_blog/article/details/47028975
參考內容:
http://www.jianshu.com/p/94e0f9ab3f1d

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