關於Context的理解與總結——認識Context


作為一個Android開發者,我們在Android開發中經常會使用到Context這個類。它在加載資源、啟動Activity、獲取系統服務、創建View等活動中都需要參與。

但Context到底是什么,我就很少去關注了…那么我們該如何理解去Context呢?它到底是什么呢?

什么是Context
翻譯角度
Context翻譯為中文,有:上下文、背景、環境等翻譯,我們可以把Context理解成一種環境。Android應用模型是基於組件的應用設計模式,組件的運行要有一個完整的Android工程環境 。

因此Android不像普通Java程序一樣,隨便創建一個類,寫上main方法就可以運行,每個組件需要有自己工作的環境,才能正常運行。而Context,就是我們這里所說的環境。

比如,當我們需要創建一個Button時,也需要給它提供一個環境:Button button = new Button(context);

一個Activity可以是一個Context,一個Service也可以是一個Context。

源碼角度

/**
 * Interface to global information about an application environment.  This is
 * an abstract class whose implementation is provided by
 * the Android system.  It
 * allows access to application-specific resources and classes, as well as
 * up-calls for application-level operations such as launching activities,
 * broadcasting and receiving intents, etc.
 */
public abstract class Context {
...
}

我們可以看到Context源碼中的注釋,里面說到Context提供了關於應用環境的全局信息的接口,它是抽象類,調用由Android系統來進行。它可以獲取有應用特征的資源和類,可以執行一些應用級別的操作(如啟動Activity,發送廣播,接收Intent等等)

Context是一個抽象類,它有兩個實現的子類——ContextImpl 和 ContextWrapper。

ContextWrapper類僅僅是一個包裝類,它的構造函數需要傳遞一個真正的Context的引用。並且它提供了attachBaseContext() 方法來指定真正的Context。調用ContextWrapper最終都會調用它包含的真正的Context對象的方法。

ContextImpl才是真正的實現類,它實現了Context中的所有方法。平時我們調用的所有Context的方法的實現均來自這個類。

Activity、Application、Service三個類均繼承自ContextWrapper,而在具體初始化過程中,則會構造ContextImpl對象,來實現Context中的方法。

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去調用getInstance獲取instance,則Singleton類保存了Activity的引用,導致Activity被銷毀后仍然不能被GC回收。

例子2

public class MainActivity extends Activity {
    private static Drawable mDrawable;

    @Override
    protected void onCreate(Bundle saveInstanceState) {
        super.onCreate(saveInstanceState);
        setContentView(R.layout.activity_main);
        ImageView ivImage = new ImageView(this);
        mDrawable = getResources().getDrawable(R.drawable.ic_launcher);
        ivImage.setImageDrawable(mDrawable);
    }
}

在上面這個例子中,Drawable是靜態的。調用ImageView的setImageDrawable設置Drawable時,ImageView就會持有這個Drawable的引用。而ImageView同時還持有Activity的引用,並且它持有的Drawable是常駐內存的,導致MainActivity被銷毀時,無法被GC回收。

如何避免
一般Context所導致的內存泄漏,都是由於Context被銷毀時,由於它的引用導致無法被回收。但是我們可以使用Application的引用,因為Application的生命周期是隨着進程存在的。

因此我們盡量在使用Context的時候用如下的姿勢:

生命周期長的對象,並且Application的Context可以滿足使用時,優先使用Application的Context。
不要讓聲明周期比Activity長的對象持有Activity的引用
盡量不要在Activity中使用非靜態的內部類。因為非靜態的內部類會隱式持有外部類的引用。如果要使用靜態內部類,使用弱引用來持有外部類的實例。
————————————————
版權聲明:本文為CSDN博主「N0tExpectErr0r」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/qq_21556263/article/details/83211897


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM