Android 轉(zhuǎn)屏機制

我們知道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的改動,然后重新布局和繪制经伙。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末扶叉,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子帕膜,更是在濱河造成了極大的恐慌枣氧,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,743評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件垮刹,死亡現(xiàn)場離奇詭異达吞,居然都是意外死亡,警方通過查閱死者的電腦和手機荒典,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評論 3 385
  • 文/潘曉璐 我一進(jìn)店門酪劫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人寺董,你說我怎么就攤上這事覆糟。” “怎么了螃征?”我有些...
    開封第一講書人閱讀 157,285評論 0 348
  • 文/不壞的土叔 我叫張陵搪桂,是天一觀的道長。 經(jīng)常有香客問我盯滚,道長踢械,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,485評論 1 283
  • 正文 為了忘掉前任魄藕,我火速辦了婚禮内列,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘背率。我一直安慰自己话瞧,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,581評論 6 386
  • 文/花漫 我一把揭開白布寝姿。 她就那樣靜靜地躺著交排,像睡著了一般。 火紅的嫁衣襯著肌膚如雪饵筑。 梳的紋絲不亂的頭發(fā)上埃篓,一...
    開封第一講書人閱讀 49,821評論 1 290
  • 那天,我揣著相機與錄音根资,去河邊找鬼架专。 笑死,一個胖子當(dāng)著我的面吹牛玄帕,可吹牛的內(nèi)容都是我干的部脚。 我是一名探鬼主播,決...
    沈念sama閱讀 38,960評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼裤纹,長吁一口氣:“原來是場噩夢啊……” “哼委刘!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起鹰椒,我...
    開封第一講書人閱讀 37,719評論 0 266
  • 序言:老撾萬榮一對情侶失蹤锡移,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后吹零,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體罩抗,經(jīng)...
    沈念sama閱讀 44,186評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,516評論 2 327
  • 正文 我和宋清朗相戀三年灿椅,在試婚紗的時候發(fā)現(xiàn)自己被綠了套蒂。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,650評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡茫蛹,死狀恐怖操刀,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情婴洼,我是刑警寧澤骨坑,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響欢唾,放射性物質(zhì)發(fā)生泄漏且警。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,936評論 3 313
  • 文/蒙蒙 一礁遣、第九天 我趴在偏房一處隱蔽的房頂上張望斑芜。 院中可真熱鬧,春花似錦祟霍、人聲如沸杏头。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽醇王。三九已至,卻和暖如春崭添,著一層夾襖步出監(jiān)牢的瞬間寓娩,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評論 1 266
  • 我被黑心中介騙來泰國打工滥朱, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留根暑,地道東北人。 一個月前我還...
    沈念sama閱讀 46,370評論 2 360
  • 正文 我出身青樓徙邻,卻偏偏與公主長得像排嫌,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子缰犁,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,527評論 2 349

推薦閱讀更多精彩內(nèi)容