Android7.0多視窗實現原理(二)
從上文可以知道當開始分屏時從SystemUI呼叫到ActivityStackSupervisor中的startActivityFromRecentsInner函式,當要分屏的Activity已經存在了,屬於熱啟動分屏。
在anyTaskForIdLocked函式中就直接將對應的TaskRecord物件返回。
Move Stack
由於獲取到的TaskRecord並不是在分屏所屬的ActivityStack,launchStackId和獲得task的stack id不相同,就呼叫moveTaskToStackLocked函式將Task移動到分屏的stack中。
在moveTaskToStackLocked函式中,首先根據taskId獲取到對應的TaskRecord,如果task為空,或者task所在的stack就是分屏stack就會直接返回,如果請求stack為freeform是stack,但是系統不支援freeform stack就丟擲異常。
接下來就需要將tack移動到對應到對應的stack中,之後記錄新stack的id,凍結task的邊界,對新的stack中的task進行重新計算尺寸。
首先分析moveTaskToStackUncheckedLocked函式將task移動到分屏的stack中。獲取task頂層正在執行的Activity,將task所在的stack記錄在preStack中,表示為之前的stack,判斷preStack是否為焦點stack並且現在最頂層的Activity是否與之前的相同,preStack中可見的Activity是否與之前最頂端的Activity相同,並且判斷之前頂層的Activity是否是最前端的Activity。
當在獲取分屏stack時,我們要臨時將resizeablility設定為false,不能進行resize,當獲取完stack才可以resize。getStack的詳細呼叫流程在上文https://blog.csdn.net/fu_kevin0606/article/details/81191078做過詳細的分析。獲取到的stack為分屏stack,stack id為3.
獲取完stack後呼叫WindowManagerService來移動task,在moveTaskToStack函式中根據taskId從mTaskIdToTask中獲取到Task物件,然後根據stackId獲取對應的TaskStack物件,之後呼叫Task.java的moveTaskToStack函式移動task到對應stack。
在Task.java中判斷task所屬的TaskStack是否為空,如果不為空,就先將該task從TaskStack中移除,再重新新增到頂部。
在WMS中將task移動到對應的TaskStack後,需要將TaskRecord移動到分屏的ActivityStack中。由於需要將task新增的分屏stack的頂端,就呼叫insertTaskAtTop函式,將task插入頂端。
在將task插入最頂端時,如果task是從Home介面啟動的,就獲取這個task的下一個taskRecord,如果下一個task也不為空,就將這個task的返回型別傳遞給下一個taskRecord。
之後根據Activity的啟動型別,從那個介面啟動的來設定Activity的返回型別,有事我們會遇到從一個介面返回,應該返回到主介面,卻返回到了其他開啟過的應用中,就是該返回值設定錯誤。
之後將task從歷史列表中移除,從新計算位置,插入mTaskHistory列表。
最後,如果我們移動的task之前有焦點,或者我們請求將焦點交給他,我們通過將stack移動到前面來移動焦點。
如果需將r賦值給mResumedActivity,呼叫moveToFront將stack移動到最前面。
將task移動到對應的stack中後,呼叫resizeTaskLocked函式resize task,由於此時為分屏stack,所以就使用分屏stack的邊界來resize task。
在resize task時會先判斷stack邊界是否可用,如果可用,之後呼叫TaskRecord的updateOverrideConfiguration函式來更新Task的配置資料資訊。
在計算config資訊時呼叫calculateOverrideConfig函式,在該函式中獲得裝置的寬高,根據寬高來計算裝置的方向,最後將獲取的config資訊返回。來判斷config是否發生改變,如果改變了就將新的config資訊返回,如果沒有發生改變就返回null。
如果此時config發生了改變overrideConfig不為null,就獲取頂層的Activity,確認頂層的Activity的config資訊與當前系統的config相同,如果config不相同,就將系統的config賦值給頂層Activity,然後通知應用config發生改變呼叫onConfigurationChanged函式。之後確保Activity在正確的config下使Activity可見。最後顯示頂端的Activity。
之後呼叫WindowManagerService來resizeTask,根據taskId從mTaskIdToTask中取出對應的Task,呼叫的task的resizeLocked函式來resize task,當resize成功後,如果需要relayout,就需要呼叫performSurfacePlacement介面重新整理函式來重新整理介面。在resizeLocked函式中將bounds設定給task。
此時將task從原來的stack中移動到了分屏stack中,並且對task重新計算了size,下面重新回到 startActivityFromRecentsInner函式中。
前面主要是將另一個stack的task移動到分屏stack中,下面主要做的就是將移動到分屏stack的task移動到前臺,可以被使用者看到。該動作主要在moveTaskToFrontLocked函式中執行。首先根據taskId獲取到對應的TaskRecord,獲取當前正在執行的Activity記錄在prev變數中,表現將要新的Activity要啟動,該Activity變為前一個Activity。最後呼叫findTaskToMoveToFrontLocked函式來移動task。
在findTaskToMoveToFrontLocked函式中,根據task物件獲取到棧頂的Activity,然後呼叫ActivityStack的moveTaskToFrontLocked函式將分屏task移動到stack的頂端。
在將task移動到stack頂端時呼叫insertTaskAtTop函式,將task插入頂端。此時task位於mTaskHistory列表的頂端,此時通過topRunningActivityLocked函式獲得mTaskHistory頂端的task中頂端的Activity,也就是要進行分屏的Activity。將焦點設定在該Activity上面。
最後呼叫resumeFocusedStackTopActivityLocked來顯示Activity。