真的理解Context?

Github鏈接

概述

在使用getContext方法的時(shí)候有沒有想過,在不同的場(chǎng)景下,取到的Context到底有什么不同,View,F(xiàn)ragment,Activity和Application的getContext又究竟是怎么樣?

下面來分析一下。

獲取Context

DecorView

DecorView的Context是Application Context。

ActivityThread.addView -> PhoneWindow.generateDecor

DecorView的Context.png

View

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

LayoutInflater.rinflate.png

發(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。

Activity.attach.png

于是增加一個(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

FragmentHostCallback.context.png

結(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)行初始化。

createContext.png

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

need_new_task.png

看一下這個(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

Context使用.png

總結(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的生命周期一致。

最后編輯于
?著作權(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),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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