一、Context
1. 定義
Context,翻譯為上下文,也可以理解為環(huán)境,是提供一些程序的運(yùn)行環(huán)境基礎(chǔ)信息。和java程序不同,Android程序需要有一個(gè)完整的Android工程環(huán)境,在這個(gè)環(huán)境下,有像Activity、Service、BroadcastReceiver等系統(tǒng)組件,而這些組件要有它們各自的上下文環(huán)境,也就是Context??梢哉f(shuō)Context是維持Android程序中各組件能夠正常工作的一個(gè)核心功能類。
它是一個(gè)抽象類,是一個(gè)應(yīng)用程序環(huán)境信息的接口。里面定義了各種抽象方法,包括獲取系統(tǒng)資源、獲取系統(tǒng)服務(wù),發(fā)送廣播,啟動(dòng)Activity,Service等。所以從源碼角度看Context就是抽象出一個(gè)App應(yīng)用所有功能的集合。
2. Context的使用場(chǎng)景
使用Context調(diào)用方法,比如啟動(dòng)Activity、訪問(wèn)資源、調(diào)用系統(tǒng)級(jí)服務(wù)等
調(diào)用方法時(shí)傳入Context,比如彈出toast,創(chuàng)建dialog等等。
3. Context的關(guān)聯(lián)類
它的關(guān)聯(lián)類(直接子類)有:ContextWrapper(Context的封裝類) ContextImpl(Context的實(shí)現(xiàn)類),接下來(lái)的類都是繼承自ContextWrapper這個(gè)裝飾類的具體裝飾類,包括ContextThemeWrapper、 Application和Service。其中,ContextThemeWrapper是一個(gè)帶主題的封裝類,而它有一個(gè)直接子類就是Activity。也就是說(shuō),Context一共三種類型,包括Application、Service和Activity。
下面這張圖展示了Context的關(guān)聯(lián)類之間的關(guān)系:

Context的幾個(gè)子類在能力上的區(qū)別

數(shù)字1:?jiǎn)?dòng)Activity在這些類中是可以的,但是需要?jiǎng)?chuàng)建一個(gè)新的task。一般情況不推薦。
數(shù)字2:在這些類中去layout inflate是合法的,但是會(huì)使用系統(tǒng)默認(rèn)的主題樣式,如果你自定義了某些樣式可能不會(huì)被使用。
數(shù)字3:在receiver為null時(shí)允許,在4.2或以上的版本中,用于獲取黏性廣播的當(dāng)前值。
ContentProvider、BroadcastReceiver之所以在上述表格中,是因?yàn)樵谄鋬?nèi)部方法中都有一個(gè)context用于使用。
從表格中可以看到,和UI相關(guān)的方法基本都不建議或者不可使用 Application,并且,前三個(gè)操作基本不可能在Application中出現(xiàn)。實(shí)際上,凡是跟UI相關(guān)的,都應(yīng)該使用 Activity做為Context來(lái)處理;其他的一些操作,Service,Activity,Application等實(shí)例都可以。
Context可以實(shí)現(xiàn)很多功能,例如彈出Toast、啟動(dòng)Activity、啟動(dòng)Service、發(fā)送廣播、操作數(shù)據(jù)庫(kù)等等都需要用到Context。
TextView tv = new TextView(getContext());
?
ListAdapter adapter = new SimpleCursorAdapter(getApplicationContext(), ...);
?
AudioManager am = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);getApplicationContext().getSharedPreferences(name, mode);
?
getApplicationContext().getContentResolver().query(uri, ...);
?
getContext().getResources().getDisplayMetrics().widthPixels * 5 / 8;
?
getContext().startActivity(intent);
?
getContext().startService(intent);
?
getContext().sendBroadcast(intent);
由于Context的具體能力是由ContextImpl類去實(shí)現(xiàn)的,所以在絕大多數(shù)場(chǎng)景下,Activity、Service和Application這三種類型的Context都是可以通用的。不過(guò)有幾種場(chǎng)景比較特殊,比如啟動(dòng)Activity, 還有彈出Dialog、導(dǎo)入布局文件。出于安全原因的考慮,Android是不允許Activity或Dialog憑空出現(xiàn)的,一個(gè)Activity的啟動(dòng)必須要建立在另一個(gè)Activity的基礎(chǔ)之上,也就是以此形成的返回棧。而Dialog則必須在一個(gè)Activity上面彈出(除非是System Alert類型的Dialog),因此在這種場(chǎng)景下,我們只能使用Activity類型的Context,否則將會(huì)出錯(cuò)。
ps:在Activity中某個(gè)地方需要彈出對(duì)話框時(shí),通常會(huì)構(gòu)造一個(gè)AlertDialog然后show一下,其中在構(gòu)造AlertDialog的時(shí)候,傳遞了當(dāng)前的Activity作為上下文context。
二、源碼中的Context
1. 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提供了關(guān)于應(yīng)用程序環(huán)境的全局信息的接口。Context是一個(gè)抽象類,其實(shí)現(xiàn)由Android系統(tǒng)提供。通過(guò)它我們可以訪問(wèn)特定于應(yīng)用程序的資源和類【以及對(duì)應(yīng)用程序級(jí)操作(如啟動(dòng)活動(dòng)、廣播和接收intents)的調(diào)用)】
抽象類,提供了一組通用的API。
2. ContextImpl實(shí)現(xiàn)類
* Common implementation of Context API, which provides the base
* context object for Activity and other application components.
*/
class ContextImpl extends Context {
private Context mOuterContext;
......
}
注釋說(shuō)明:ContextImpl是對(duì)Context類的所有API的通用實(shí)現(xiàn),并且為Activity和其他應(yīng)用程序的組件提供了base Context object。(提供了Context類的對(duì)象mBase)
3. ContextWrapper包裝類
* Proxying implementation of Context that simply delegates all of its calls to
* another Context. Can be subclassed to modify behavior without changing
* the original Context.
*/
public class ContextWrapper extends Context {
Context mBase;
?
public ContextWrapper(Context base) {
mBase = base;
}
?
/**
* Set the base context for this ContextWrapper. All calls will then be
* delegated to the base context. Throws
* IllegalStateException if a base context has already been set.
*
* @param base The new base context for this wrapper.
*/
protected void attachBaseContext(Context base) {
if (mBase != null) {
throw new IllegalStateException("Base context already set");
}
mBase = base;
}
......
}
Context的代理實(shí)現(xiàn),這里的實(shí)現(xiàn)只是簡(jiǎn)單的把它所有的調(diào)用委派給另一個(gè)context(也就是ContextWrapper源碼里面的所有方法實(shí)現(xiàn)都是通過(guò)調(diào)用mBase的同名方法,也就是委派給ContextImpl)??梢栽诓桓脑糃ontext類的情況下,子類化以修改行為(添加新的方法或者是刪除某些方法)。
ContextWrapper類的構(gòu)造函數(shù)包含了一個(gè)真正的Context引用(ContextImpl對(duì)象),然后就變成了ContextImpl的裝飾者模式。
4. ContextWrapper的子類ContextThemeWrapper
* A ContextWrapper that allows you to modify the theme from what is in the
* wrapped context.
*/
public class ContextThemeWrapper extends ContextWrapper {
......
}
在Wrapperd context的基礎(chǔ)上允許修改主題;該類的內(nèi)部包含了主題Theme相關(guān)的接口。
三、Context的創(chuàng)建
1. Application context的創(chuàng)建過(guò)程

-
ActivityThead類(應(yīng)用程序進(jìn)程的主線程管理類),會(huì)調(diào)用其內(nèi)部類ApplicationThread的scheduleLaunchActivity方法來(lái)啟動(dòng)Activity。
scheduleLaunchActivity方法內(nèi)容:
updateProcessState(); ActivityClientRecord r=new ActivityClientRecord(); //新建一個(gè)記錄對(duì)象,用來(lái)存放啟動(dòng)信息 ... //給r賦值 sendMessage(H.LAUNCH_ACTIVITY,r); //向H類發(fā)送r消息ActivityClientRecord是activity的一個(gè)記錄類,記錄了很多啟動(dòng)activity的信息,暫且不管。
H 是 ActivityThread 內(nèi)部類,繼承自 Handler。
H類接收到消息之后,在handleMessage方法中對(duì)它進(jìn)行了處理。
handleMessage方法主要代碼:r.packageInfo=getPackageInfoNoCheck(r.activityInfo.applicationInfo,r.compatInfo); handleLaunchActivity(r,null,"LAUNCH_ACTIVITY");第一行通過(guò)一個(gè)get方法從消息r中獲取到LoadedApk類型的對(duì)象,并且把值賦給ActivityClientRecord記錄類r的成員變量packageInfo, LoadedApk用來(lái)描述已加載的APK文件。
第二行調(diào)用了ActivityThread中的handleLaunchActivity方法,其具體內(nèi)容如下:Activity a=performLauchActivity(r, customIntent);上面同樣是調(diào)用了ActivityThread中的另一個(gè)方法:performLauchActivity,其具體內(nèi)容如下:
//此方法返回一個(gè)Activity類的對(duì)象 try{ Application app=r.packageInfo.makeApplication(false, mInstrumentation);//r.packageInfo上面已經(jīng)賦值了 ... } ... return activity;可以看到,上面調(diào)用了LoadedApk的makeApplication方法,其內(nèi)容如下:
//返回一個(gè)Application類的對(duì)象 if(mApplication!=null) { return mApplication; //這里是第一次啟動(dòng),所以為空,不會(huì)返回;但是如果不是第一次啟動(dòng),就會(huì)直接返回 } ContextImpl appContext=ContextImpl.createAppContext(mActivityThread,this); //2 app=mActivityThread.mInstrumentation.newApplication(cl,appClass,appContext);//3 appContext.setOuterContext(app);//4 mApplication=app; //5注釋2處通過(guò)ContextImpl的createAppContext方法來(lái)創(chuàng)建ContextImpl。
注釋3處代碼用于創(chuàng)建application, 在Instrumentation的newApplication方法中傳入了ClassLoader類型的對(duì)象以及注釋2處創(chuàng)建的ContextImpl。
在注釋4處把注釋3中創(chuàng)建出來(lái)的Application賦值給ContextImpl的Context類型的成員變量mOuterContext,也就是讓ContextImpl內(nèi)部持有外部Application類的引用,用于注冊(cè)系統(tǒng)服務(wù)或者是其他方法。
最后,通過(guò)mApplication=app 將Application賦值給LoadedApk的成員變量mApplication。
接下來(lái),看一下注釋3處的Application是如何創(chuàng)建的,Instrumentation的newApplication方法:Application app=(Application)clazz.newInstance(); app.attach(context); return app;通過(guò)反射來(lái)創(chuàng)建Application,并且調(diào)用了Application中的attach方法,將ContextImpl傳進(jìn)去,最后返回Application。
attach方法的主要內(nèi)容是:attachBaseContext(context); mLoadedApk=ContextImpl.getImpl(context).mPackInfo;attchBaseContext方法在Application的父類ContextWrapper中實(shí)現(xiàn),代碼如下:
protected void attachBaseContext(Context base){ if(mBase!=null){ throw new IllegalStateException("Base context already set"); } mBase=base; //把ContextImpl賦值給ContextWrapper的Context類型的成員變量mBase }這個(gè)base一路傳遞過(guò)來(lái)指的是ContextImpl,它是Context的實(shí)現(xiàn)類。
Application通過(guò)父類ContextWrapper類的成員變量mBase指向了ContextImpl,讓Application類真正實(shí)現(xiàn)了其祖父類Context抽象類中的所有抽象方法,這個(gè)過(guò)程是通過(guò)attachBaseContext方法來(lái)實(shí)現(xiàn)的。
把ContextImpl賦值給ContextWrapper的Context類型的成員變量mBase,這樣在ContextWrapper中就可以使用Context的方法,而Application繼承自ContextWrapper,同樣可以使用Context的方法。
2. Activity context的創(chuàng)建過(guò)程

如果想要在Activity中使用Context提供的方法,務(wù)必要先創(chuàng)建Context。Activity的Context會(huì)在Activity的啟動(dòng)過(guò)程中被創(chuàng)建。
上面已經(jīng)說(shuō)過(guò),ActivityThread會(huì)調(diào)用scheduleLaunchActivity方法來(lái)啟動(dòng)Activity,此方法把啟動(dòng)Activity的參數(shù)封裝成ActivityClientRecord,sendMessage方法向H類發(fā)送類型為L(zhǎng)AUNCH_ACTIVITY的消息,并將ActivityClientRecord傳遞過(guò)去。sendMessage方法的目的將啟動(dòng)Activity的邏輯放在主線程的消息隊(duì)列中,這樣啟動(dòng)Activity的邏輯就會(huì)在主線程中執(zhí)行。然后H類的handleMessage方法會(huì)對(duì)LAUNCH_ACTIVITY類型的消息進(jìn)行處理,其中調(diào)用了ActivityThread的handleLaunchActivity方法,而在handleLaunchActivity方法中又調(diào)用了ActivityThread的performLaunchActivity方法,在此方法中首先通過(guò)createBaseContextForActivity方法來(lái)創(chuàng)建Activity的ContextImpl,然后再將ComImpl傳入activity的attach方法中,并且調(diào)用了ContextImpl的setOuterContext方法,將此前創(chuàng)建的Activity實(shí)例賦值給ContextImpl的成員變量mOuterContext,這樣ContextImpl也可以訪問(wèn)Activity的變量和方法。
Activity的attchBaseContext方法其父類ContextThemeWrapper中實(shí)現(xiàn),然后此方法接著調(diào)用ContextThemeWrapper的父類ContextWrapper的attachBaseContext方法。
總結(jié):在啟動(dòng)Activity的過(guò)程中創(chuàng)建ContextImpl, 并賦值給ContextWrapper的成員變量mBase。Activity繼承自ContextWrapper的子類ContextThemeWrapper, 這樣在Activity中就可以使用Context中定義的方法了。
3. Service的Context創(chuàng)建過(guò)程
與Activity的Context創(chuàng)建過(guò)程類似,是在Service的啟動(dòng)過(guò)程中被創(chuàng)建的。
總結(jié):在Service的啟動(dòng)過(guò)程中創(chuàng)建ContextImpl,并且將ContextImpl賦值給ContextWrapper的Context類型的成員變量mBase, 這樣可以在ContextWrapper中就可以使用Context的方法,而Service繼承自ContextWrapper,同樣可以使用Context的方法。