1. 程式人生 > >Android進階之Context(上下文)你必須知道的一切

Android進階之Context(上下文)你必須知道的一切

1 Context概念

(1)在啟動Activity/Service,傳送廣播,獲取系統資源,獲取系統服務等都需要Context的參與,可見Context的常見性。到底什麼是Context,Context字面意思上下文,或者叫做場景,也就是使用者與作業系統操作的一個過程,比如你打電話,場景包括電話程式對應的介面,以及隱藏在背後的資料。

1.1 Android系統的角度Context是什麼呢?

Context是一個場景,代表與作業系統的互動的一種過程,是維持Android程式中各元件能夠正常工作的一個核心功能類。

1.2 在程式的角度Context是什麼呢?

(1)在程式的角度,Context是個抽象類,定義了各種抽象方法,包括啟動Activity/Service,傳送廣播,獲取系統資源,獲取系統服務等。Activity、Service、Application都是Context的的一個實現(子類),可以直接通過看其類結構來說明答案:
在這裡插入圖片描述


(2)Context類原始碼解析

public abstract class Context {
	public abstract Resources getResources();
}

(3)ContextWrapper的原始碼解析:Activity、Service、Application都是繼承自ContextWrapper,而ContextWrapper內部會包含一個mBase的Context,由這個mBase去實現了絕大多數的方法。

public class ContextWrapper extends Context {
    Context mBase;

    public ContextWrapper(Context base) {
        mBase = base;
    }
    
    protected void attachBaseContext(Context base) {
        if (mBase != null) {
            throw new IllegalStateException("Base context already set");
        }
        mBase = base;
    }

    @Override
    public Resources getResources() {
        return mBase.getResources();
    }
 }

(4)ContextThemeWrapper的原始碼解析
Context直接子類為ContextIml(具體實現類)和ContextWrapper(上下文功能包裝類),而ContextWrapper又有三個子類,分別是ContextThemeWrapper、Service和Application。基於Activity和Service、Application不在一個繼承層級裡,而是又繼承了ContextThemeWrapper。
ContextThemeWrapper是一個帶主題的封裝類,內部包含了主題(Theme)相關的介面,當Activity在啟動的時候系統都會載入一個主題,也就是我們在配置檔案AndroidManifest.xml裡面寫的android:theme=”@style/AppTheme”的屬性啦!(如下圖所示),可是Service和Applicaton並不需要載入主題,因此他們繼承自ContextWrapper。

2 Context與Application的Context(getApplicationContext)的區別

XXXActivity和getApplicationContext返回的肯定不是一個物件,一個是當前Activity的例項,一個是專案的Application的例項。各自的使用場景肯定不同,亂使用可能會帶來一些問題。
(1)工具類,可能會編寫成單例的方式,這些工具類大多需要去訪問資源,也就說需要Context的參與。在這樣的情況下,就需要注意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(){
		
	}
}

問題在於:這個Context哪來的我們不能確定,很大的可能性。在某個Activity裡面為了方便,直接傳了個this,這樣問題就來了,這個類中的sInstance是一個static且強引用的,在其內部引用了一個Activity作為Context,也就是說,我們的這個Activity只要我們的專案活著,就沒有辦法進行記憶體回收。而我們的Activity的生命週期肯定沒這麼長,所以造成了記憶體洩漏。
解決方法1:可以軟引用,嗯,軟引用,假如被回收了,會引起NullPointException。
解決方法2:引用的是一個ApplicationContext,讓它的生命週期和單例物件一致。

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

3 Context的應用場景

3.1 場景圖

在這裡插入圖片描述

3.2 數字標註提示

(1)數字1:啟動Activity在這些類中是可以的,但是需要建立一個新的task。一般情況不推薦。
(2)數字2:在這些類中去layout inflate是合法的,但是會使用系統預設的主題樣式,如果你自定義了某些樣式可能不會被使用。
(3)數字3:在receiver為null時允許,在4.2或以上的版本中,用於獲取黏性廣播的當前值。(可以無視)
(4)注意:ContentProvider、BroadcastReceiver之所以在上述表格中,是因為在其內部方法中都有一個context用於使用。

3.3 小結

(1)和UI相關的方法基本都不建議或者不可使用Application,並且,前三個操作基本不可能在Application中出現。實際上,凡是跟UI相關的,都應該使用Activity做為Context來處理;其他的一些操作(Service,Activity,Application)等例項都可以,注意Context引用的持有,防止記憶體洩漏。
(2)Toast通常使用Activity和Application的context,也可以使用Service、ContentProvider和BroadcastReceiver的context。但是在IntentService的onHandleIntent()不能使用,因為其在子執行緒中。

4 Context數量

在建立Activity、Service、Application時都會自動建立Context,它們各自維護著自己的上下文。在Android系統中Context類的繼承結構中Context一共有Application、Activity和Service三種類型,因此如果要統計一個app中Context數量,可以這樣來表示:

// 1表示Application數量。一個應用程式中可以有多個Activity和多個Service,但只有一個Application。
Context數量 = Activity數量 + Service數量 + 1

備註:可能有人會說一個應用程式裡面可以有多個Application啊,我的理解是:一個應用程式裡面可以有多個Application,可是在配置檔案AndroidManifest.xml中只能註冊一個,只有註冊的這個Application才是真正的Application,才會呼叫到全部的生命週期,所以Application的數量是1。

5 總結

Context的分析基本完成了,希望在以後的使用過程中,能夠稍微考慮下,這裡使用Activity合適嗎?會不會造成記憶體洩漏?這裡傳入Application work嗎?

6 學習連結

Android Context 上下文 你必須知道的一切

Android Application中的Context和Activity中的Context的區別

熟悉又陌生的Context