作為一個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