從源碼上解析Android Context

一、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)景

  1. 使用Context調(diào)用方法,比如啟動(dòng)Activity、訪問(wèn)資源、調(diào)用系統(tǒng)級(jí)服務(wù)等

  2. 調(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)系:


1.png

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

2.jpg

數(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ò)程

3.png
  1. 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ò)程

4.png

如果想要在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的方法。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容