概述
在使用getContext方法的時(shí)候有沒有想過,在不同的場(chǎng)景下,取到的Context到底有什么不同,View,F(xiàn)ragment,Activity和Application的getContext又究竟是怎么樣?
下面來分析一下。
獲取Context
DecorView
DecorView的Context是Application Context。
ActivityThread.addView -> PhoneWindow.generateDecor

View
一般View是從LayoutInflater類中inflate生成的,查看inflate方法,會(huì)調(diào)用rinflate

發(fā)現(xiàn)是入?yún)ontext,在inflate方法內(nèi)賦值,其實(shí)最后就是LayoutInflater.from的參數(shù)Context。
所以結(jié)論是普通View是LayoutInflater.from的參數(shù)Context。
但是對(duì)于xml這樣的布局文件里面的View又是怎么樣的呢?
看一下Activity.setContentView方法的調(diào)用過程是:
Activity.setContentView -> PhoneWindow.setContentView -> LayoutInflater.inflate方法,而這個(gè)LayoutInflater是在PhoneWindow的構(gòu)造方法內(nèi)創(chuàng)建的,回到Activity.attach方法,看到構(gòu)造方法的參數(shù)是Activity的Context。

于是增加一個(gè)結(jié)論,在xml中解析的View的Context屬于Activity。
可能有人問,那Fragment也是嗎?看一下Fragment.onCreateView中參數(shù)有LayoutInflater,跟蹤一下
Fragment.onCreateView <- Fragment.performCreateView -> FragmentManagerImpl.moveToState <- Fragment.performGetLayoutInflater <- Fragment.getLayoutInflater <- FragmentHostCallback.onGetLayoutInflater
結(jié)果是用了FramgentHostCallback構(gòu)造方法的參數(shù)Context

結(jié)論也是Activity的Context。
FragmentActivity
一般使用FragmentActivity.this和FragmentActivity.getContext方法取到Context,最終取到的都是Activity的Context,不再贅述。
Fragment
通過Fragment.getContext取到Context,結(jié)果是取到FragmentHostCallback.getContext也是Activity的Context。
Application
取到的是Application Context。
Applicaiton與Activity的區(qū)別
所以最終的目的是區(qū)分Application與Activity的Context有什么不同。所以看它們各自的實(shí)現(xiàn),Application與Activity都繼承于ContextWrapper,ContextWrapper就是包裝了一下抽象類Context,在構(gòu)造方法里傳入Context對(duì)象,根本在于構(gòu)建Application和Activity的地方。
追蹤一下發(fā)現(xiàn)Context構(gòu)建都在ContextImpl類內(nèi),Application對(duì)應(yīng)createAppContext,而Activity則對(duì)應(yīng)createActivityContext,都在ActivityThread中調(diào)用各自創(chuàng)建Context方法進(jìn)行初始化。

對(duì)比ContextImpl的構(gòu)建,前三個(gè)參數(shù)都一致,Activity的Context多了activityToken和classLoder,其中activityToken對(duì)應(yīng)ActivityRecord類,接著調(diào)用Context.setResource方法設(shè)置Activity的配置和邏輯顯示相關(guān)的信息Display。

看一下這個(gè)熟悉的錯(cuò)誤,這個(gè)是使用Application的Context啟動(dòng)Activity時(shí)報(bào)的錯(cuò)誤,這個(gè)mSourceRecord就是AcitivityRecord對(duì)象,對(duì)于Application而言為null,所以需要指定New_Task這個(gè)標(biāo)志。
兩者之間更重要的一個(gè)區(qū)別是:生命周期。
對(duì)于Application的Context而言,在整個(gè)應(yīng)用的生命周期內(nèi)都不會(huì)改變;而對(duì)于不同的Activity,其Context有可能不同,例如添加一個(gè)Dialog必須附著在Activity上,所以使用Application就會(huì)報(bào)錯(cuò)。
下圖來源于Stackoverflow

總結(jié)
除了Application,DecorView和getApplicationContext方法會(huì)取到Application Context外,其他方法getContext都會(huì)取到Activity Context或者傳入的Context。
一般來說,Application Context存在于整個(gè)應(yīng)用的生命周期中,不會(huì)隨場(chǎng)景變化而改變,所以對(duì)于打開不同的Activity,Activity Context可能存在不同,而且生命周期跟Activity的生命周期一致。