我們知道Android中 支持橫屏和豎屏,用戶可以選擇鎖定(rotation lock)也可以選擇讓傳感器來自動轉(zhuǎn)屏敏储。而轉(zhuǎn)屏?xí)r為了使用戶體驗更流暢阻星,會對屏幕截屏,然后使用截屏的圖來做轉(zhuǎn)屏動畫虹曙,直到轉(zhuǎn)屏動作結(jié)束迫横。接下來以自動 旋轉(zhuǎn)為例看一下大體流程番舆。過程雖然很短酝碳,但涉及到的模塊其實不少,一個簡化的相關(guān)類圖如下:
首先恨狈,是否要自動轉(zhuǎn)屏是在Setting中設(shè)置的疏哗。為了監(jiān)聽Setting中的改動,系統(tǒng)啟動時禾怠,PhoneWindowManager的init()函 數(shù)中創(chuàng)建了SettingsObserver對象返奉,它的observe()方法會監(jiān)聽Settings.System.USER_ROTATION的值。 假設(shè)系統(tǒng)啟動后rotation lock是打開的吗氏。當(dāng)用戶在Setting中設(shè)置自動轉(zhuǎn)屏后芽偏,會觸發(fā)以下流程:
可以看到,這里會觸發(fā)SettingsObserver.onChange()弦讽,其中主要調(diào)用了updateSettings()和 updateRotation()兩個函數(shù)污尉。簡單地說膀哲,主要的工作是根據(jù)需要監(jiān)聽傳感器數(shù)據(jù),據(jù)此判斷是否要轉(zhuǎn)屏被碗。如果需要就是對 configuration的各種更新某宪。過程中會凍結(jié)屏幕,同時截屏并以此作為轉(zhuǎn)屏動畫锐朴。另外還需要將新configuration傳給AMS兴喂,廣播該事 件給需要的模塊,同時App也會被調(diào)度來響應(yīng)變更焚志。
第一個函數(shù)updateSettings()如它的名字主要更新設(shè)置信息衣迷。如果UserRotation(朝向信息,如 Surface.ROTATION_0)和UserRotationMode(USER_ROTATION_FREE vs. USER_ROTATION_LOCKED)有更新酱酬,就設(shè)置標(biāo)記updateRotation為true表示接下去需要更新rotation相關(guān)信息蘑险。此 外,如果UserRotationMode的配置有變岳悟,由于需要傳感器信息的配合佃迄,還需調(diào)用updateOrientationListenerLp() 來設(shè)置或取消監(jiān)聽傳感器。這里假設(shè)設(shè)置為自動旋轉(zhuǎn)贵少,那么PhoneWindowManager會通過MyOrientationListener來監(jiān)聽傳 感器信息呵俏。MyOrientationListener是WindowOrientationListener的繼承類。它的enable()函數(shù)中調(diào)用 SensorManager提供的registerListener()接口來設(shè)置Sensor信息的listener滔灶。
registerListener()的具體實現(xiàn)在SystemSensorManager中普碎。registerListenerImpl()中會創(chuàng)建 SensorEventQueue對象(基類為BaseEventQueue),它是傳感器事件的隊列录平,記錄需要監(jiān)聽哪些傳感器信息麻车。同時它也負(fù)責(zé)與 SensorService的連接和通信,可以說是SensorEventListener與SensorService間的橋梁斗这。 SensorEventListener和SensorEventQueue之間是1:1的關(guān)系动猬,它們的映射關(guān)系保存在成員 mSensorListeners中。如果這里注冊的SensorEventListener還沒有相應(yīng)的SensorEventQueue表箭,則新建一 個赁咙,然后通過addSensor()方法將要關(guān)注的傳感器進(jìn)行注冊。這個過程中addSensor()調(diào)用了enableSensor()免钻,它最終是通過 SensorService的enableDisable()方法來完成注冊工作的彼水。這樣,SensorService就開始監(jiān)聽該Sensor极舔,當(dāng)?shù)讓?有傳感器數(shù)據(jù)來時凤覆,SensorService主線程中會調(diào)用相應(yīng)SensorEventConnection的sendEvents()將之發(fā)給對應(yīng)的 Client。前面初始化SensorEventQueue時會創(chuàng)建Receiver拆魏,它是一個Looper的回調(diào)對象盯桦,在Client端收到從 SensorService來的數(shù)據(jù)后被回調(diào)澡绩。當(dāng)有數(shù)據(jù)收到時Receiver的handleEvent()被調(diào)用,繼而通過JNI調(diào)用到 SystemSensorManager::dispatchSensorEvent()俺附。接著就調(diào)到了 WindowOrientationListener的onSensorChanged()函數(shù)肥卡。該函數(shù)計算是否需要轉(zhuǎn)屏。如果需要轉(zhuǎn)屏事镣,將計算結(jié)果傳給 onProposedRotationChanged()步鉴。
回到PhoneWindowManager的updateSettings()流程。最后如果檢測到UserRotation或 UserRotationMode有更新璃哟,會調(diào)用updateRotation()氛琢,繼而調(diào)用WMS的updateRotation()保證當(dāng)前的屏幕方 向是一致的。
假設(shè)現(xiàn)在用戶轉(zhuǎn)了屏幕随闪,期望轉(zhuǎn)屏事件發(fā)生蚀瘸。如前面所說粗卜,onProposedRotationChanged()被調(diào)用刀森,其中調(diào)用 updateRotation()函數(shù)杠览,隨之的updateRotationUncheckedLocked()就是真正執(zhí)行轉(zhuǎn)屏的地方了。當(dāng)然除了上面 這條path會進(jìn)行轉(zhuǎn)屏当宴,還有其它途徑可能會觸發(fā)轉(zhuǎn)屏畜吊,比如應(yīng)用請求轉(zhuǎn)屏。例如需要橫屏的游戲(通過 updateOrientationFromAppTokensLocked()方法)户矢。updateRotation()中主要是執(zhí)行兩個函 數(shù):updateRotationUncheckedLocked()和sendNewConfiguration()玲献。前者執(zhí)行轉(zhuǎn)屏動作,包含轉(zhuǎn)屏動畫 等梯浪。后者使AMS獲取當(dāng)前新的configuration捌年,并且廣播該事件給所有相應(yīng)的listener。先看下updateRotation()的流 程:
首先判斷前一個轉(zhuǎn)屏動畫是否還在進(jìn)行中挂洛,如果是就先不做轉(zhuǎn)屏動畫礼预,直接返回。WMS中有一個單例對象WindowAnimator來處理動畫抹锄,其中的 mDisplayContentsAnimators數(shù)組對于每個display存有一個DisplayContentsAnimator對象逆瑞。該對象中 的ScreenRotationAnimation對象即用于轉(zhuǎn)屏動畫。這里就是將主display對應(yīng)的 DisplayContentsAnimator中的ScreenRotationAnimation對象拿出來看是否正在動畫中伙单。然后通過 rotationForOrientationLw()來計算需要轉(zhuǎn)至的朝向,如果和原來的一樣也說明不需要操作哈肖,直接返回吻育。接下來調(diào)用 startFreezingDisplayLocked()來凍結(jié)屏幕,其中會創(chuàng)建轉(zhuǎn)屏動畫淤井,其實現(xiàn)主要在 ScreenRotationAnimation中布疼。在其構(gòu)造函數(shù)中摊趾,會對主屏上的內(nèi)容進(jìn)行截屏,截屏結(jié)果作為轉(zhuǎn)屏?xí)r最上面的層游两。
截屏操作主要是通過SurfaceFlinger提供的接口來完成的砾层。向SF發(fā)起申請的操作需要放在transaction中。首先創(chuàng)建用于截屏的 SurfaceControl贱案,它會調(diào)用SurfaceComposerClient的createSurface()方法在SF中創(chuàng)建一個截屏圖層的 layer肛炮。然后創(chuàng)建Surface并將之作為該圖層的生產(chǎn)者端對象。之后把這個生產(chǎn)者對象作為用于截屏的screenshot()方法的參數(shù)傳回給 SF宝踪。這樣侨糟,就相當(dāng)于把SF自己和自己建立了通路,生產(chǎn)者端和消費者端都是SF瘩燥,SF自己繪制出屏幕內(nèi)容再作為最上層的buffer交回SF合成繪制秕重。同 時WMS中又可以通過SurfaceControl提供的接口操作窗口信息,比如設(shè)置z-order(FREEZE_LAYER非常高厉膀,保證將下面的內(nèi)容 蓋兹茉拧),alpha(一開始是透明服鹅,等調(diào)整朝向后設(shè)為不透明)汰具。最后調(diào)用show()將之設(shè)為可見。調(diào)用 setRotationTransaction()方法是因為截屏是不考慮原始轉(zhuǎn)屏的菱魔,這里需要加一個初始變換將截屏的朝向調(diào)整到當(dāng)前實際朝向留荔。
WMS的sendNewConfiguration()函數(shù)調(diào)用AMS的updateConfiguration()方法。在該方法中澜倦,先調(diào)用WMS的 computeNewConfiguration()方法得到當(dāng)前的configuration聚蝶。因為display的size會影響內(nèi)存使用,進(jìn)而影響 內(nèi)存回收機制的行為藻治,因此需要調(diào)用applyDisplaySize()方法更新oom level碘勉。接著調(diào)用updateConfigurationLocked()函數(shù)。這是一個比較重要的函數(shù)桩卵,它更改當(dāng)前的configuration验靡, 同時確保頂層的Activity是符合當(dāng)前configuration的。它會遍歷LRU列表中的進(jìn)程并通過 scheduleConfigurationChanged()來讓它們更新configuration雏节。然后廣播 ACTION_CONFIGURATION_CHANGED等Intent胜嗓,這讓注冊了該Intent的模塊可以作出響應(yīng)。接著钩乍,取焦點 ActivityStack的頂層Activity辞州,調(diào)用ensureActivityConfigurationLocked()來確保其符合當(dāng)前 configuration。注意ensureActivityConfigurationLocked()中寥粹,如果該Activity可以被保留不用被 銷毀变过,返回true埃元,否則返回false。其后的ensureActivitiesVisibleLocked()方法確保其它可見的Activity以 新的configuration顯示媚狰。最后調(diào)用WMS的setNewConfiguration()岛杀,控制流回到WMS,它其中會觸發(fā) performLayoutAndPlaceSurfacesLocked()方法來更新整個window stack崭孤,其中的scheduleAnimationLocked()方法會讓之前的轉(zhuǎn)屏動畫開始类嗤。于是,每當(dāng)繪制周期到來時裳瘪,會調(diào)用 WindowAnimator的animateLocked()函數(shù)來進(jìn)行動畫土浸。animateLocked()中會遍歷所有display對應(yīng)的 DisplayContentsAnimator,如果有轉(zhuǎn)屏動畫ScreenRotationAnimation彭羹,就調(diào)用其 stepAnimationLocked()方法黄伊,來進(jìn)行一幀的轉(zhuǎn)屏動畫。
一般情況下是在animateLocked()函數(shù)中會判斷動畫結(jié)束派殷,這里會把相應(yīng)的ScreenRotationAnimation干掉还最。而后 stopFreezingDisplayLocked()函數(shù)被調(diào)用將屏幕解凍。另外如果凍屏超時(默認(rèn)2秒)毡惜,則會發(fā)送 WINDOW_FREEZE_TIMEOUT消息拓轻。它會取消orientation的改動,然后重新布局和繪制经伙。