Android7.1 Presentation双屏异显原理分析
緒論
???????隨著用戶(hù)的需求增多,特別是對(duì)于一些Android平板電腦以及其他的一些Android智能設(shè)備可能有多個(gè)屏幕,用戶(hù)不僅想要在主屏幕上顯示內(nèi)容,同樣在第二屏幕上也要顯示想要的內(nèi)容,這樣可以達(dá)到更好的體驗(yàn)效果。Google也是不負(fù)眾望在A(yíng)ndroid4.2版本以后提供了Presentation類(lèi),可以輕松實(shí)現(xiàn)在兩塊屏幕上同時(shí)顯示不同的內(nèi)容。
??????Presentation是一個(gè)特殊的dialog,它的目的是顯示內(nèi)容到第二屏幕。在Presentation創(chuàng)建的時(shí)候關(guān)聯(lián)一個(gè)目標(biāo)設(shè)備,確定Presentation要顯示在那個(gè)設(shè)備上,根據(jù)這個(gè)設(shè)備的信息來(lái)配置Presentation的context和resources信息。
??????Presentation的context與包含它的Activity的context是不相同的,用presentation的上下文來(lái)填充一個(gè)presentation的layout和加載其它的資源是非常重要的,可以確保我們加載的目標(biāo)設(shè)備正確的尺寸密度assets。
??????當(dāng)Presentation附屬的display被移除的話(huà),Presentation就會(huì)自動(dòng)被取消。當(dāng)創(chuàng)建Presentation的Activity自己處于paused或者resumed,無(wú)論P(yáng)resentation在顯示什么內(nèi)容,該Activity就要關(guān)心對(duì)Presentation?進(jìn)行pausing?and?resuming。
??????本文將講解如何使用Presentation類(lèi)做到雙屏異顯,以及實(shí)現(xiàn)的原理。
使用方法
??????在將presentation顯示出來(lái)之前,最重要的事情就是選擇要將presentation顯示在哪個(gè)設(shè)備上。要選擇一個(gè)presentation顯示在哪個(gè)設(shè)備可能是一件非常困難的事情,因?yàn)橛锌赡艽藭r(shí)系統(tǒng)中有多個(gè)設(shè)備。與其自己猜想哪個(gè)設(shè)備是最適合顯示presentation,可以讓系統(tǒng)幫我們選擇一個(gè)最好的設(shè)備。在A(yíng)ndroid系統(tǒng)中為我們提供了兩種使用presentation的方法:
media router
??????選擇顯示presentation的設(shè)備最簡(jiǎn)單的方法就是使用Media?Router,media?router服務(wù)持續(xù)追蹤在系統(tǒng)中哪個(gè)音頻視頻線(xiàn)路是可用的,當(dāng)routes被選擇或者不被選擇,或者更好的顯示presentation?display的線(xiàn)路發(fā)生改變后media?router就會(huì)發(fā)送消息。所以一些應(yīng)用程序可以自動(dòng)監(jiān)視這些消息來(lái)在首選設(shè)備上顯示或取消presentation。
??????首選顯示presentation的設(shè)備是Media?Router進(jìn)行推薦的,如果應(yīng)用想要顯示內(nèi)容在第二屏幕上就該使用該設(shè)備。有時(shí)可能沒(méi)有首選顯示presentation的設(shè)備,在這種情況下,應(yīng)用可以在本地顯示內(nèi)容而不使用presentation。
?????下面將給出如何使用media router在首選的設(shè)備創(chuàng)建和顯示presentation。
??????根據(jù)上面代碼可以看出通過(guò)系統(tǒng)服務(wù)Media?Router來(lái)選擇一個(gè)合適的route,從route中來(lái)獲取首選的display,如果之前已經(jīng)有presentation,就要判斷一下之前presentation顯示的display是否與新的display相同,如果不相同說(shuō)明首選的display發(fā)生了改變,就將該presentation取消,置空,再去創(chuàng)建,否則不用創(chuàng)建。如果是第一次創(chuàng)建presentation只要首選的display不為空就會(huì)創(chuàng)建該presentation,創(chuàng)建完成后調(diào)用presentation的show方法將該presentation顯示出來(lái)。
display manager
??????另外一個(gè)獲取首選display的方法是直接使用display?Manager來(lái)獲取。display?manager服務(wù)提供方法枚舉和描述系統(tǒng)中所有的設(shè)備,包括可以顯示presentation的設(shè)備。display?manager持續(xù)追蹤系統(tǒng)中所有的設(shè)備,然而,并不是所有的設(shè)備都適合顯示presentation,例如,如果一個(gè)Activity試圖在主屏幕上顯示一個(gè)presentation,它可能會(huì)掩蓋自己的內(nèi)容,這就象在A(yíng)ctivity上打開(kāi)一個(gè)dialog一樣。
?????下面將給出例子,演示如何使用Display?Manager獲取合適的display顯示presentation。
????根據(jù)上面代碼可以看出通過(guò)系統(tǒng)服務(wù)Display?Manager來(lái)獲取所有適合顯示presentation的display列表,如果獲取到了多個(gè)適合display對(duì)象,之后我們就要考慮給用戶(hù)一個(gè)選擇,我們選擇第一個(gè)為首選的display。最后顯示presentation。
獲取首選顯示設(shè)備
????根據(jù)上文可以知道我們有兩種方法使用presentation,而在本文中主要講解第二種直接使用display?Manager來(lái)獲取首選display。
獲取display服務(wù)
?????我們?cè)贏(yíng)ctivity中使用getSystemService函數(shù),通過(guò)參數(shù)Context.DISPLAY_SERVICE來(lái)指定要獲取Service的類(lèi)型。調(diào)用到Activity.java的getSystemService函數(shù)中,如果去需要?jiǎng)?chuàng)建的對(duì)象為Window?manager或者search?manager就直接將對(duì)象返回,因?yàn)樵赼ctivity.java中有這兩個(gè)對(duì)象,如果我們要獲取display?Service就需要繼續(xù)獲取。
由于A(yíng)ctivity.java繼承自ContextThemeWrapper.java,所以就會(huì)調(diào)用父類(lèi)的getSystemService函數(shù)。
????在父類(lèi)中首先判斷獲取的Service是否是inflater服務(wù),如果是就創(chuàng)建一個(gè)mInflater返回,否則就調(diào)用getBaseContext函數(shù)獲取在ContextWrapper.java中設(shè)置的context對(duì)象,在創(chuàng)建Activity的時(shí)候ActivityThread調(diào)用ContextImpl的createActivityContext函數(shù)創(chuàng)建一個(gè)ContextImpl對(duì)象,然后通過(guò)Activity的attach函數(shù)將該contex對(duì)象設(shè)置到ContextWrapper中。所以getBaseContext函數(shù)獲取的也就是一個(gè)ContextImpl對(duì)象,之后會(huì)調(diào)用該對(duì)象的getSystemService函數(shù)。最后就會(huì)調(diào)用到SystemServiceRegistry的getSystemService函數(shù)獲取服務(wù)。
??????根據(jù)SystemServiceRegistry的名字可以看出來(lái),該類(lèi)是注冊(cè)了系統(tǒng)服務(wù)的,我們通過(guò)context以及需要獲取對(duì)象的name就可以獲取相應(yīng)的系統(tǒng)服務(wù)了。下面詳細(xì)講解該類(lèi)。
??????SystemServiceRegistry的作用就是管理系統(tǒng)中的所有的服務(wù),并且使用context通過(guò)getSystemService獲取服務(wù)對(duì)象。
??????SystemServiceRegistry對(duì)象配置在了preloaded-classes文件中,當(dāng)系統(tǒng)啟動(dòng)的時(shí)候在ZygoteInit的preload函數(shù)中進(jìn)行加載該文件,對(duì)該文件中的對(duì)象進(jìn)行預(yù)加載初始化。在SystemServiceRegistry對(duì)象的靜態(tài)代碼塊中調(diào)用registerService函數(shù)將系統(tǒng)中的服務(wù)注冊(cè)在SYSTEM_SERVICE_NAMES和SYSTEM_SERVICE_FETCHERS中,例如我們要獲取的Display服務(wù):
????將Context.DISPLAY_SERVICE,DisplayManager.class以及CachedServiceFetcher對(duì)象類(lèi)型為DisplayManager傳入registerService函數(shù)。
????在該函數(shù)中將傳入的對(duì)象記錄在兩個(gè)hashMap中,這兩個(gè)map在初始化完成后就不允許改變了。
????如上文當(dāng)我們使用getSystemService獲取服務(wù)時(shí),根據(jù)服務(wù)name從SYSTEM_SERVICE_FETCHERS中取出fetcher對(duì)象,display對(duì)象的fetcher對(duì)象為CachedServiceFetcher。
????調(diào)用getService函數(shù)來(lái)獲取服務(wù),先從cache里面獲取service,如果cache中沒(méi)有服務(wù)對(duì)象就調(diào)用createService函數(shù)進(jìn)行創(chuàng)建,將新創(chuàng)建的服務(wù)對(duì)象放入cache中用對(duì)應(yīng)下標(biāo)記錄。createService函數(shù)為抽象函數(shù)在子類(lèi)中實(shí)現(xiàn),從上文注冊(cè)display對(duì)象時(shí)可以看出createService函數(shù)創(chuàng)建了DisplayManager對(duì)象傳入?yún)?shù)context對(duì)象,context對(duì)象為調(diào)用getSystemService函數(shù)的activity。
????在DisplayManager的構(gòu)造函數(shù)中將傳入的context對(duì)象記錄在mContext成員變量中,并且創(chuàng)建DisplayManagerGlobal對(duì)象,mGlobal對(duì)象的創(chuàng)建屬于單例模式。
????在getInstance函數(shù)中,創(chuàng)建一個(gè)Display服務(wù)的binder對(duì)象,將binder轉(zhuǎn)換成IDisplayManager接口對(duì)象,記錄在成員變量mDm中。
將創(chuàng)建好的服務(wù)返回記錄在cache中,并且返回到獲取服務(wù)的Activity中。這就獲取到了display服務(wù)了。
設(shè)備初始化
??????在講解獲取設(shè)備時(shí),首先講一下系統(tǒng)中的設(shè)備如何初始化。在開(kāi)機(jī)過(guò)程中系統(tǒng)調(diào)用DisplayManagerService的onStart函數(shù)來(lái)注冊(cè)本地服務(wù),并且發(fā)送Handle消息MSG_REGISTER_DEFAULT_DISPLAY_ADAPTER來(lái)注冊(cè)本地設(shè)備。在調(diào)用registerDisplayAdapterLocked函數(shù)時(shí),創(chuàng)建LocalDisplayAdapter對(duì)象傳入該函數(shù)。
??????在registerDisplayAdapterLocked函數(shù)中將傳入的adapter對(duì)象放入mDisplayAdapters列表,并且調(diào)用LocalDisplayAdapter的registerLocked函數(shù)來(lái)注冊(cè)設(shè)備。
??????在注冊(cè)設(shè)備時(shí)遍歷BUILT_IN_DISPLAY_IDS_TO_SCAN列表,在該列表中有主屏幕,HDMI屏幕,依附HDMI的TERTIARY屏幕三種類(lèi)型的屏幕。依次根據(jù)這三個(gè)設(shè)備調(diào)用tryConnectDisplayLocked函數(shù)來(lái)嘗試連接設(shè)備。
??????在嘗試連接設(shè)備時(shí)調(diào)用SurfaceControl的getBuiltInDisplay函數(shù)獲取到對(duì)應(yīng)設(shè)備id的token對(duì)象,如果token為null說(shuō)明不存在該設(shè)備,如果找到對(duì)應(yīng)的token對(duì)象,看一下該設(shè)備有沒(méi)有可用的configs,如果沒(méi)有直接返回。再判斷有沒(méi)有活動(dòng)的配置activeConfig,如果沒(méi)有也會(huì)返回。最后都滿(mǎn)足要求的話(huà),在mDevices列表中獲取Device,如果此時(shí)還沒(méi)有創(chuàng)建Device對(duì)象,就創(chuàng)建LocalDisplayDevice對(duì)象,并且加入mDevices列表,調(diào)用sendDisplayDeviceEventLocked發(fā)送設(shè)備添加的消息,如果之前已經(jīng)創(chuàng)建過(guò)了Device對(duì)象就判斷該設(shè)備的信息是否更新了,如果更新了就發(fā)送設(shè)備改變的消息。
??????在發(fā)送消息時(shí), 通過(guò)mHandle對(duì)象發(fā)送一個(gè)Runnable對(duì)象,在run函數(shù)中調(diào)用mListener的onDisplayDeviceEvent函數(shù)通知監(jiān)聽(tīng)器設(shè)備變化。mHandle與mListener都是在創(chuàng)建LocalDisplayAdapter的時(shí)候進(jìn)行賦值的,如上文調(diào)用registerDisplayAdapterLocked函數(shù)時(shí)創(chuàng)建,最后回到DisplayManagerService的DisplayAdapterListener對(duì)象中。
在DisplayAdapterListener中監(jiān)聽(tīng)了設(shè)備的增加,變化,移除事件。
????當(dāng)設(shè)備增加時(shí)調(diào)用handleDisplayDeviceAdded函數(shù),并且將前邊創(chuàng)建的LocalDisplayDevice傳給該對(duì)象。
??????之后調(diào)用LocalDisplayDevice的getDisplayDeviceInfoLocked函數(shù)獲取設(shè)備信息,在該函數(shù)中設(shè)置主屏設(shè)備,以及其他設(shè)備的初始化信息。之后調(diào)用addLogicalDisplayLocked函數(shù)基于所給的display信息來(lái)添加一個(gè)logical?display。
????首先獲取設(shè)備的信息,根據(jù)設(shè)備的flag來(lái)判斷是否是默認(rèn)主屏幕,以設(shè)備id,layerStack,LocalDisplayDevice作為參數(shù)創(chuàng)建LogicalDisplay對(duì)象。
????在創(chuàng)建LogicalDisplay時(shí)也創(chuàng)建一個(gè)mBaseDisplayInfo對(duì)象,之后調(diào)用updateLocked函數(shù)來(lái)根據(jù)可用的Device來(lái)更新logical?display,如果logical?display依附的設(shè)備已經(jīng)被移除了它就不可用了。updateLocked函數(shù)主要內(nèi)容如下:
????mPrimaryDisplayDevice對(duì)象在LogicalDisplay的構(gòu)造函數(shù)中進(jìn)行賦值是一個(gè)LocalDisplayDevice,調(diào)用該對(duì)象的getDisplayDeviceInfoLocked獲取設(shè)備的基本信息,賦值給mBaseDisplayInfo對(duì)象中。
?????更新完設(shè)備信息回到addLogicalDisplayLocked函數(shù),將新創(chuàng)建的設(shè)備對(duì)象放入mLogicalDisplays列表中,我們可以通過(guò)設(shè)備id在該列表中獲取我們需要的設(shè)備對(duì)象。
獲取設(shè)備
??????當(dāng)獲取display服務(wù)后調(diào)用getDisplays函數(shù),通過(guò)參數(shù)DisplayManager.DISPLAY_CATEGORY_PRESENTATION?確定獲取的設(shè)備為可以顯示presentation的設(shè)備。代碼位于DisplayManager.java中
?????在該函數(shù)中首先獲取系統(tǒng)中所有的設(shè)備id,如果傳入的category為null調(diào)用addAllDisplaysLocked函數(shù)獲取所有的設(shè)備信息,如果category為DISPLAY_CATEGORY_PRESENTATION,以設(shè)備類(lèi)型為T(mén)YPE_WIFI,TYPE_HDMI,TYPE_OVERLAY,?TYPE_VIRTUAL?這些系統(tǒng)中可能存在的設(shè)備來(lái)調(diào)用addPresentationDisplaysLocked函數(shù)來(lái)尋找合適設(shè)備。
??????遍歷所有的設(shè)備id,通過(guò)getOrCreateDisplayLocked來(lái)獲取或者創(chuàng)建設(shè)備,當(dāng)獲取到設(shè)備后如果設(shè)備有FLAG_PRESENTATION標(biāo)簽,并且設(shè)備的類(lèi)型與傳入的類(lèi)型相同,說(shuō)明該設(shè)備適合顯示presentation,將該設(shè)備加入列表displays列表。下面具體分析getOrCreateDisplayLocked如何獲取設(shè)備。
??????在該函數(shù)中根據(jù)設(shè)備id從mDisplays列表中獲取display,如果設(shè)備已經(jīng)添加到了mDisplays列表,并且設(shè)備可用,就直接把設(shè)備返回。如果設(shè)備沒(méi)有創(chuàng)建出來(lái),就調(diào)用DisplayManagerGlobal的getCompatibleDisplay函數(shù)創(chuàng)建設(shè)備對(duì)象,將創(chuàng)建出來(lái)的設(shè)備添加進(jìn)mDisplays列表。
??????在getCompatibleDisplay函數(shù)中調(diào)用getDisplayInfo函數(shù)獲取設(shè)備信息,根據(jù)設(shè)備信息,設(shè)備id,以及兼容性設(shè)備信息以及activityToken創(chuàng)建Display對(duì)象返回。在調(diào)用getDisplayInfo函數(shù)獲取設(shè)備信息時(shí),先判斷設(shè)備信息是否使用緩存技術(shù),如果使用就先從緩存中找設(shè)備信息,如果找到直接返回。如果找不到再使用BInder遠(yuǎn)程調(diào)用到DisplayManagerService中的getDisplayInfo,獲取到設(shè)備信息后再放入緩存中,讓下次調(diào)用使用。如果不使用緩存,直接去DisplayManagerService中獲取。
?????在DisplayManagerService中調(diào)用getDisplayInfoInternal獲取設(shè)備信息。
??????根據(jù)設(shè)備id從列表mLogicalDisplays獲取出對(duì)應(yīng)的LogicalDisplay對(duì)象,當(dāng)系統(tǒng)在啟動(dòng)時(shí)會(huì)將LogicalDisplay與對(duì)應(yīng)的設(shè)備id記錄在mLogicalDisplays列表中,如上文。如果通過(guò)設(shè)備id在列表中找不到設(shè)備信息,說(shuō)明系統(tǒng)中不存在該設(shè)備返回null,如果找到LogicalDisplay,就調(diào)用該對(duì)象的getDisplayInfoLocked函數(shù)獲取設(shè)備信息。
??????在該函數(shù)中新創(chuàng)建一個(gè)DisplayInfo對(duì)象,將mBaseDisplayInfo?copy到新創(chuàng)建的DisplayInfo對(duì)象中,如果mOverrideDisplayInfo不為空就重新賦值DisplayInfo,根據(jù)之前分析屏幕旋轉(zhuǎn)可以知道到設(shè)備旋轉(zhuǎn)時(shí)mOverrideDisplayInfo不為空。如上面分析mBaseDisplayInfo對(duì)象在設(shè)備初始化時(shí)創(chuàng)建以及賦值,此時(shí)我們就獲取到所需要的設(shè)備。
創(chuàng)建presentation對(duì)象
????獲取到presentation要顯示的設(shè)備后,就將Activity的context對(duì)象和設(shè)備信息作為參數(shù)來(lái)創(chuàng)建Presentation對(duì)象。
??????由于Presentation繼承自Dialog,在創(chuàng)建Presentation時(shí)首選調(diào)用父類(lèi)的構(gòu)造函數(shù),將presentation的上下文傳給父類(lèi),之后將設(shè)備記錄在成員變量mDisplay中,創(chuàng)建DisplayManagerService的遠(yuǎn)程BInder對(duì)象,WindowManagerService的遠(yuǎn)程BInder對(duì)象。最后將presentation設(shè)置為不能在外部點(diǎn)擊取消。
?????下面分析一下createPresentationContext函數(shù)創(chuàng)建presentation上下文的過(guò)程。outerContext為我們創(chuàng)建presentation的context,創(chuàng)建presentation必須有一個(gè)context對(duì)象,如果為null就拋出異常。同時(shí),如果沒(méi)有指定presentation要顯示在哪個(gè)設(shè)備上,也會(huì)拋出異常。之后通過(guò)outerContext根據(jù)設(shè)備信息來(lái)創(chuàng)建displayContext,該context是一個(gè)ContextImpl對(duì)象,根據(jù)displayContext來(lái)獲取設(shè)備的主題信息。
??????之后根據(jù)外部的Window?manager獲取設(shè)備的Window?manager,這樣做主要是由于外部的Window?manager有一些如父窗口的額外信息,如果presentation使用應(yīng)用窗口類(lèi)型這樣做是非常重要的。最后創(chuàng)建ContextThemeWrapper對(duì)象返回,如果用戶(hù)通過(guò)presentation的context調(diào)用getSystemService函數(shù)獲取系統(tǒng)服務(wù)時(shí),如果獲取的是Window?manager就直接將displayWindowManager返回,如果獲取別的就調(diào)用父類(lèi)獲取,如上文講解。
?????獲取到presentation的context對(duì)象后,就調(diào)用父類(lèi)Dialog的構(gòu)造函數(shù)。CreateContextThemeWrapper為false,將presentation的context賦值給mContext對(duì)象。將displayWindowManager賦值給mWindowManager,創(chuàng)建PhoneWindow對(duì)象記錄在mWindow對(duì)象中,之后創(chuàng)建ListenersHandler對(duì)象,監(jiān)聽(tīng)dialog的消失,取消,顯示事件。
這時(shí)就創(chuàng)建成功了一個(gè)presentation對(duì)象。
請(qǐng)求顯示presentation
??????當(dāng)創(chuàng)建presentation對(duì)象后調(diào)用presentation對(duì)象的show方法請(qǐng)求顯示presentation,在Presentation中的show方法直接調(diào)用父類(lèi)Dialog的show方法。
????如果已經(jīng)調(diào)用過(guò)presentation的show方法那么mShowing為true,并且mDecor(最頂層的Window)已經(jīng)創(chuàng)建過(guò),就直接將mDecor設(shè)置為VISIBLE,并返回。如果是第一次調(diào)用show方法,mCreated為false?那么dispatchOnCreate函數(shù)調(diào)用我們presentation的onCreate來(lái)調(diào)用setContentView設(shè)置presentation的布局,并且初始化一些對(duì)象。之后調(diào)用onStart函數(shù),由于Presentation重寫(xiě)了父類(lèi)的onStart函數(shù),所以首先調(diào)用子類(lèi)的onStart函數(shù)。
??????在Presentation的onStart函數(shù)中注冊(cè)mDisplayListener監(jiān)聽(tīng),用來(lái)監(jiān)聽(tīng)設(shè)備的移除,配置改變。現(xiàn)在就要開(kāi)始監(jiān)視設(shè)備的改變了,可能display?metrics已經(jīng)發(fā)生改變,例如轉(zhuǎn)屏等事件發(fā)生,如果設(shè)備信息真的發(fā)生了改變就需要將presentation立即取消。
??????重新回到Dialog的show函數(shù)中,通過(guò)PhoneWindow的getDecorView函數(shù)獲取頂層的mDecor對(duì)象,如果mDecor為空就調(diào)用installDecor函數(shù)進(jìn)行獲取Decor對(duì)象。獲取到mDecor對(duì)象后調(diào)用addView將該對(duì)象添加到WindowManager中。最后調(diào)用到WindowManagerGlobal的addView函數(shù)來(lái)添加view。
??????在添加view時(shí)獲取Window的參數(shù)wparams,如果該Window存在父窗口,就根據(jù)參數(shù)為子窗口調(diào)節(jié)布局參數(shù)。如果不存在父窗口就判斷是否有硬件加速,如果應(yīng)用設(shè)置了硬件加速,就在wparams進(jìn)行標(biāo)記。
??????之后開(kāi)始監(jiān)聽(tīng)系統(tǒng)配置的變化,創(chuàng)建Runnable對(duì)象mSystemPropertyUpdater,將該對(duì)象添加到SystemProperties中,如果系統(tǒng)配置發(fā)生變化就會(huì)調(diào)用該對(duì)象的run函數(shù),在run函數(shù)中遍歷mRoots列表中的ViewRootImpl對(duì)象來(lái)加載每個(gè)view的系統(tǒng)配置。
?
??????接著在系統(tǒng)中獲取view的下標(biāo),根據(jù)下標(biāo)進(jìn)行判斷,如果下標(biāo)小于0說(shuō)明view已經(jīng)添加進(jìn)了Window?Manager,如果大于零再判斷該view是否已經(jīng)死亡,如果死亡就調(diào)用ViewRootImpl的doDie函數(shù)處理。
??????如果這個(gè)view是一個(gè)面板窗口,需要找到他所連接的窗口,以便未來(lái)進(jìn)行參考。首先判斷該窗口類(lèi)型是否大于第一個(gè)子窗口,小于等于最后一個(gè)子窗口,如果是就從mRoots列表中找到一個(gè)View窗口Binder對(duì)象和傳入view的Binder對(duì)象相同,就從對(duì)應(yīng)的mVIews中取出panelParentView。
????下面就是比較重要的操作根據(jù)view的上下文和設(shè)備信息來(lái)創(chuàng)建一個(gè)ViewRootImpl對(duì)象,ViewRootImpl構(gòu)造函數(shù)內(nèi)容如下所示。
??????將傳入的context參數(shù)記錄在全局變量mContext中,display記錄在mDisplay中,創(chuàng)建mWindowSession對(duì)象,這個(gè)比較重要下面分析,創(chuàng)建W類(lèi)的對(duì)象mWindow是ViewRootImpl中的一個(gè)內(nèi)部類(lèi),再初始化一些其他的對(duì)象,最后加載系統(tǒng)配置信息。下面分析一下mWindowSession對(duì)象的創(chuàng)建過(guò)程,在WindowManagerGlobal.java中實(shí)現(xiàn)。
????如果sWindowSession已經(jīng)創(chuàng)建過(guò)直接返回,如果沒(méi)有創(chuàng)建過(guò)先創(chuàng)建input?method的服務(wù)對(duì)象imm,之后創(chuàng)建WIndowManagerService的服務(wù)對(duì)象windowManager,調(diào)用windowManager的openSession函數(shù)將IWindowSessionCallback對(duì)象,input?method的客戶(hù)端,input?method的上下文最為參數(shù),通BInder傳輸?shù)絎indowManagerService中。
????在WindowManagerService中創(chuàng)建Session對(duì)象返回。這就創(chuàng)建完成了一個(gè)mWindowSession對(duì)象。回到上文當(dāng)創(chuàng)建完成一個(gè)ViewRootImpl對(duì)象,繼續(xù)在WindowManagerGlobal的addView函數(shù)中執(zhí)行。
????設(shè)置view的參數(shù),將view加入到mViews列表,新創(chuàng)建的ViewRootImpl對(duì)象加入mRoots列表,view參數(shù)信息加入mParams列表。
??????最后調(diào)用ViewRootImpl的setView函數(shù),將view對(duì)象,wparams,panelParentView設(shè)置到ViewRootImpl中。在ViewRootImpl中進(jìn)行添加Window到WIndowManager,測(cè)量,布局,以及繪制Window流程,下面將詳細(xì)講解。
添加窗口
??????在setView函數(shù)中首先根據(jù)mView是否為NULL,判斷是否已經(jīng)為該ViewRootImp設(shè)置過(guò)view了,如果已經(jīng)設(shè)置過(guò)了就什么也不用操作,如果該ViewRootImp還沒(méi)有設(shè)置過(guò)任何的view對(duì)象,就會(huì)將需要設(shè)置的view對(duì)象賦值給成員變量mView。對(duì)一些其它的對(duì)象進(jìn)行賦值,靜mAdded賦值為true表示已經(jīng)添加過(guò)view對(duì)象.
????之后用res變量來(lái)記錄添加Window的返回結(jié)果,調(diào)用requestLayout函數(shù)來(lái)請(qǐng)求layout,在后文詳細(xì)分析。如果input類(lèi)型不是沒(méi)有input?channel就為該Window創(chuàng)建一個(gè)mInputChannel對(duì)象用來(lái)接收input事件。判斷Window是否存在屬性PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY。
最后調(diào)用Session的addToDisplay函數(shù),將該Window添加到WindowManager中,以后詳細(xì)分析。
---------------------?
作者:宇落無(wú)痕?
來(lái)源:CSDN?
原文:https://blog.csdn.net/fu_kevin0606/article/details/79420621?
版權(quán)聲明:本文為博主原創(chuàng)文章,轉(zhuǎn)載請(qǐng)附上博文鏈接!
總結(jié)
以上是生活随笔為你收集整理的Android7.1 Presentation双屏异显原理分析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 基于Android5.1的双屏异显分析
- 下一篇: Android双屏异显的实现