日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > Android >内容正文

Android

《深入理解Android 卷III》第四章 深入理解WindowManagerService

發(fā)布時(shí)間:2024/1/17 Android 45 豆豆
生活随笔 收集整理的這篇文章主要介紹了 《深入理解Android 卷III》第四章 深入理解WindowManagerService 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

《深入理解Android 卷III》即將公布,作者是張大偉。此書填補(bǔ)了深入理解Android Framework卷中的一個(gè)主要空白。即Android Framework中和UI相關(guān)的部分。在一個(gè)特別講究顏值的時(shí)代,本書分析了Android 4.2中WindowManagerService、ViewRoot、Input系統(tǒng)、StatusBar、Wallpaper等重要“顏值繪制/處理”模塊


第4章 ?深入理解WindowManagerService(節(jié)選)

本章主要內(nèi)容:

·??演示樣例最原始最簡單的窗體創(chuàng)建方法

·??研究WMS的窗體管理結(jié)構(gòu)

·??探討WMS布局系統(tǒng)的工作原理

·??研究WMS動(dòng)畫系統(tǒng)的工作原理

本章涉及的源碼文件名稱及位置:

·??SystemServer.java

frameworks/base/services/java/com/android/server/SystemServer.java

·??WindowManagerService.java

frameworks/base/services/java/com/android/server/wm/WindowManagerService.java

·??ActivityStack.java

frameworks/base/services/java/com/android/server/am/ActivityStack.java

·??WindowState.java

frameworks/base/services/java/com/android/server/wm/WindowState.java

·??PhoneWindowManager.java

frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java

·??AccelerateDecelerateInterpolator.java

frameworks/base/core/java/android/view/animation/AccelerateDecelerateInterpolator.java

·??Animation.java

frameworks/base/core/java/android/view/animation/Animation.java

·??AlphaAnimation.java

frameworks/base/core/java/android/view/animation/AlphaAnimation.java

·??WindowAnimator.java

frameworks/base/services/java/com/android/server/wm/WindowAnimator.java

·??WindowStateAnimator.java

frameworks/base/services/java/com/android/server/wm/WindowStateAnimator.java

4.1 初識(shí)WindowManagerService

WindowManagerService(下面簡稱WMS)是繼ActivityManagerService與PackageManagerService之后又一個(gè)復(fù)雜卻十分重要的系統(tǒng)服務(wù)。

在介紹WMS之前。首先要了解窗體(Window)是什么。

Android系統(tǒng)中的窗體是屏幕上的一塊用于繪制各種UI元素并能夠響應(yīng)應(yīng)用戶輸入的一個(gè)矩形區(qū)域。

從原理上來講,窗體的概念是獨(dú)自占有一個(gè)Surface實(shí)例的顯示區(qū)域。

比如Dialog、Activity的界面、壁紙、狀態(tài)欄以及Toast等都是窗體。

《卷I》第8章曾具體介紹了一個(gè)Activity通過Surface來顯示自己的過程:

·??Surface是一塊畫布,應(yīng)用能夠隨心所欲地通過Canvas或者OpenGL在其上作畫。

·??然后通過SurfaceFlinger將多塊Surface的內(nèi)容依照特定的順序(Z-order)進(jìn)行混合并輸出到FrameBuffer,從而將Android“美麗的臉蛋”顯示給用戶。

既然每一個(gè)窗體都有一塊Surface供自己涂鴉。必定須要一個(gè)角色對全部窗體的Surface進(jìn)行協(xié)調(diào)管理。于是,WMS便應(yīng)運(yùn)而生。WMS為全部窗體分配Surface,掌管Surface的顯示順序(Z-order)以及位置尺寸,控制窗體動(dòng)畫,而且還是輸入系統(tǒng)的一重要的中轉(zhuǎn)站。

說明一個(gè)窗體擁有顯示和響應(yīng)用戶輸入這兩層含義,本章將側(cè)重于分析窗體的顯示,而響應(yīng)用戶輸入的過程則在第5章進(jìn)行具體的介紹。

本章將深入分析WMS的兩個(gè)基礎(chǔ)子系統(tǒng)的工作原理:

·??布局系統(tǒng)(Layout System)。計(jì)算與管理窗體的位置、層次。

·??動(dòng)畫系統(tǒng)(Animation System),依據(jù)布局系統(tǒng)計(jì)算的窗體位置與層次渲染窗體動(dòng)畫。

為了讓讀者對WMS的功能以及工作方式有一個(gè)初步地認(rèn)識(shí),并見識(shí)一下WMS的強(qiáng)大,本節(jié)將從一個(gè)簡單而奇妙的樣例開始WMS的學(xué)習(xí)之旅。

4.1.1 一個(gè)從命令行啟動(dòng)的動(dòng)畫窗體

1.SampleWindow的實(shí)現(xiàn)

在這一節(jié)里將編寫一個(gè)最簡單的Java程序SampleWindow。僅使用WMS的接口創(chuàng)建并渲染一個(gè)動(dòng)畫窗體。

此程序?qū)侀_Activity、Wallpaper等UI架構(gòu)的復(fù)雜性,直接了當(dāng)?shù)亟沂網(wǎng)MS的client怎樣申請、渲染并注銷自己的窗體。

同一時(shí)候這也初步地反應(yīng)了WMS的工作方式。

這個(gè)樣例非常easy。僅僅有三個(gè)文件:

·??SampleWindow.java 主程序源碼。

·??Android.mk 編譯腳本。

·??sw.sh 啟動(dòng)器。

分別看一下這三個(gè)文件的實(shí)現(xiàn):

[-->SampleWindow.java::SampleWindow]

package understanding.wms.samplewindow;

......

public class SampleWindow {

?

??? publicstatic void main(String[] args) {

??? ????try {

??????? ?????//SampleWindow.Run()是這個(gè)程序的主入口

????????????new SampleWindow().Run();

??? ????} catch (Exception e) {

???????????e.printStackTrace();

??? ????}

??? }

??? //IWindowSession 是client向WMS請求窗體操作的中間代理,而且是進(jìn)程唯一的

???IWindowSession mSession = null;

??? //InputChannel 是窗體接收用戶輸入事件的管道。在第5章中將對其進(jìn)行具體的探討

???InputChannel mInputChannel = new InputChannel();

?

??? // 下面的三個(gè)Rect保存了窗體的布局結(jié)果。

當(dāng)中mFrame表示了窗體在屏幕上的位置與尺寸

??? // 在4.4中將具體介紹它們的作用以及計(jì)算原理

??? RectmInsets = new Rect();

??? RectmFrame = new Rect();

??? RectmVisibleInsets = new Rect();

?

???Configuration mConfig = new Configuration();

??? // 窗體的Surface,在此Surface上進(jìn)行的繪制都將在此窗體上顯示出來

??? SurfacemSurface = new Surface();

??? // 用于在窗體上進(jìn)行畫圖的畫刷

??? PaintmPaint = new Paint();

??? // 加入窗體所需的令牌。在4.2節(jié)將會(huì)對其進(jìn)行介紹

??? IBindermToken = new Binder();

?

??? // 一個(gè)窗體對象。本例演示了怎樣將此窗體加入到WMS中,并在其上進(jìn)行繪制操作

??? MyWindowmWindow = new MyWindow();

?

??? //WindowManager.LayoutParams定義了窗體的布局屬性,包括位置、尺寸以及窗體類型等

???LayoutParams mLp = new LayoutParams();

?

???Choreographer mChoreographer = null;

??? //InputHandler 用于從InputChannel接收按鍵事件做出響應(yīng)

???InputHandler mInputHandler = null;

?

??? booleanmContinueAnime = true;

?

??? publicvoid Run() throws Exception{

??????? Looper.prepare();

??????? // 獲取WMS服務(wù)

???????IWindowManager wms = IWindowManager.Stub.asInterface(

????????????????????? ServiceManager.getService(Context.WINDOW_SERVICE));

?

??????? // 通過WindowManagerGlobal獲取進(jìn)程唯一的IWindowSession實(shí)例。它將用于向WMS

??????? // 發(fā)送請求。

注意這個(gè)函數(shù)在較早的Android版本號(hào)(如4.1)位于ViewRootImpl類中

??????? mSession= WindowManagerGlobal.getWindowSession(Looper.myLooper());

?

??????? // 獲取屏幕分辨率

???????IDisplayManager dm = IDisplayManager.Stub.asInterface(

??????????????????????? ServiceManager.getService(Context.DISPLAY_SERVICE));

???????DisplayInfo di = dm.getDisplayInfo(Display.DEFAULT_DISPLAY);

???????Point scrnSize = new Point(di.appWidth, di.appHeight);

??????? // 初始化WindowManager.LayoutParams

?????? ?initLayoutParams(scrnSize);

?

??????? // 將新窗體加入到WMS

??????? installWindow(wms);

?

??????? // 初始化Choreographer的實(shí)例。此實(shí)例為線程唯一。這個(gè)類的使用方法與Handler

??????? // 相似。只是它總是在VSYC同步時(shí)回調(diào)。所以比Handler更適合做動(dòng)畫的循環(huán)器[1]

??????? mChoreographer= Choreographer.getInstance();

?

??????? // 開始處理第一幀的動(dòng)畫

??????? scheduleNextFrame();

?

??????? // 當(dāng)前線程陷入消息循環(huán),直到Looper.quit()

??????? Looper.loop();

?

??????? // 標(biāo)記不要繼續(xù)繪制動(dòng)畫幀

??????? mContinueAnime= false;

?

??????? // 卸載當(dāng)前Window

??????? uninstallWindow(wms);

??? }

?

??? publicvoid initLayoutParams(Point screenSize) {

??????? // 標(biāo)記即將安裝的窗體類型為SYSTEM_ALERT。這將使得窗體的ZOrder順序比較靠前

???????mLp.type = LayoutParams.TYPE_SYSTEM_ALERT;

??????? mLp.setTitle("SampleWindow");

??????? // 設(shè)定窗體的左上角坐標(biāo)以及高度和寬度

???????mLp.gravity = Gravity.LEFT | Gravity.TOP;

???????mLp.x = screenSize.x / 4;

???????mLp.y = screenSize.y / 4;

???????mLp.width = screenSize.x / 2;

???????mLp.height = screenSize.y / 2;

??????? // 和輸入事件相關(guān)的Flag,希望當(dāng)輸入事件發(fā)生在此窗體之外時(shí),其它窗體也能夠接受輸入事件

???????mLp.flags = mLp.flags | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;

??? }

?

??? publicvoid installWindow(IWindowManager wms) throws Exception {

??? ????// 首先向WMS聲明一個(gè)Token,不論什么一個(gè)Window都須要隸屬與一個(gè)特定類型的Token

??????? wms.addWindowToken(mToken,WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);

??????? // 設(shè)置窗體所隸屬的Token

???????mLp.token = mToken;

??????? // 通過IWindowSession將窗體安裝進(jìn)WMS,注意,此時(shí)僅僅是安裝到WMS。本例的Window

??????? // 眼下仍然沒有有效的Surface。只是,經(jīng)過這個(gè)調(diào)用后。mInputChannel已經(jīng)能夠用來接受

??????? // 輸入事件了

??????? mSession.add(mWindow,0, mLp, View.VISIBLE, mInsets, mInputChannel);

??????? /*通過IWindowSession要求WMS對本窗體進(jìn)行又一次布局。經(jīng)過這個(gè)操作后。WMS將會(huì)為窗體

???????? 創(chuàng)建一塊用于繪制的Surface并保存在參數(shù)mSurface中。同一時(shí)候。這個(gè)Surface被WMS放置在

????????LayoutParams所指定的位置上 */

??????? mSession.relayout(mWindow,0, mLp, mLp.width, mLp.height, View.VISIBLE,

??????????????????????? 0, mFrame, mInsets,mVisibleInsets, mConfig, mSurface);

??????? if(!mSurface.isValid()) {

??????????? thrownew RuntimeException("Failed creating Surface.");

??????? }

??????? // 基于WMS返回的InputChannel創(chuàng)建一個(gè)Handler,用于監(jiān)聽輸入事件

??????? //mInputHandler一旦被創(chuàng)建,就已經(jīng)在監(jiān)聽輸入事件了

??????? mInputHandler= new InputHandler(mInputChannel, Looper.myLooper());

??? }

?

??? publicvoid uninstallWindow(IWindowManager wms) throws Exception {

??????? // 從WMS處卸載窗體

??????? mSession.remove(mWindow);

??????? // 從WMS處移除之前加入的Token

??????? wms.removeWindowToken(mToken);

??? }

?

??? publicvoid scheduleNextFrame() {

??????? // 要求在顯示系統(tǒng)刷新下一幀時(shí)回調(diào)mFrameRender。注意,僅僅回調(diào)一次

??????? mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION

???????????????, mFrameRender, null);

??? }

?

??? // 這個(gè)Runnable對象用以在窗體上描繪一幀

??? publicRunnable mFrameRender = new Runnable() {

???????@Override

??????? publicvoid run() {

??????????? try{

???????????????// 獲取當(dāng)期時(shí)間戳

???????????????long time = mChoreographer.getFrameTime() % 1000;

?

???????????????// 畫圖

???????????????if (mSurface.isValid()) {

???????????????????Canvas canvas = mSurface.lockCanvas(null);

???????????????????canvas.drawColor(Color.DKGRAY);

???????????????????canvas.drawRect(2 * mLp.width * time / 1000

??????????????????????????? - mLp.width, 0, 2 *mLp.width * time

??????????????????????????? / 1000, mLp.height,mPaint);

???????????????????mSurface.unlockCanvasAndPost(canvas);

???????????????????mSession.finishDrawing(mWindow);

???????????????}

?

??????????? if(mContinueAnime)

???????????????scheduleNextFrame();

???????????} catch (Exception e) {

???????????????e.printStackTrace();

???????????}

??????? }

??? };

?

??? // 定義一個(gè)類繼承InputEventReceiver。用以在其onInputEvent()函數(shù)中接收窗體的輸入事件

??? classInputHandler extends InputEventReceiver {

???????Looper mLooper = null;

??????? publicInputHandler(InputChannel inputChannel, Looper looper) {

??????????? super(inputChannel,looper);

??????????? mLooper= looper;

??????? }

???????@Override

??????? publicvoid onInputEvent(InputEvent event) {

??????????? if(event instanceof MotionEvent) {

???????????????MotionEvent me = (MotionEvent)event;

?? ?????????????if (me.getAction() ==MotionEvent.ACTION_UP) {

???????????????????// 退出程序

???????????????????mLooper.quit();

???????????????}

???????????}

??????????? super.onInputEvent(event);

??????? }

??? }

?

??? // 實(shí)現(xiàn)一個(gè)繼承自IWindow.Stub的類MyWindow。

??? classMyWindow extends IWindow.Stub {

??????? // 保持默認(rèn)的實(shí)現(xiàn)就可以

??? }

}

由于此程序使用了大量的隱藏API(即SDK中未定義這些API)。因此須要放在Android源碼環(huán)境中進(jìn)行編譯它。相應(yīng)的Android.mk例如以下:

[-->Android.mk]

LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)

?

LOCAL_SRC_FILES := $(call all-subdir-java-files)

LOCAL_MODULE_TAGS := optional

LOCAL_MODULE := samplewindow

include $(BUILD_JAVA_LIBRARY)

將這兩個(gè)文件放在$TOP/frameworks/base/cmds/samplewindow/下,然后用make或mm命令進(jìn)行編譯。終于生成的結(jié)果是samplewindow.jar,文件位置在out/target/<ProductName>/system/framework/下。將該文件通過adb push到手機(jī)的/system/framework/下。

提示讀者可使用Android4.2模擬器來運(yùn)行此程序。

然而,samplewindow.jar不是一個(gè)可運(yùn)行程序,。故。需借助Android的app_process工具來載入并運(yùn)行它。筆者編寫了一個(gè)腳本做為啟動(dòng)器:

[-->sw.sh]

base=/system

export CLASSPATH=$base/framework/samplewindow.jar

?

exec app_process $base/binunderstanding.wms.samplewindow.SampleWindow "$@"

?

注意app_process事實(shí)上就是大名鼎鼎的zygote。只是。僅僅有使用--zygote參數(shù)啟動(dòng)時(shí)它才會(huì)給改名為zygote[2],否則就像java –jar命令一樣,運(yùn)行指定類的main靜態(tài)函數(shù)。

在手機(jī)中運(yùn)行該腳本,其運(yùn)行結(jié)果是一個(gè)灰色的方塊不斷地從屏幕左側(cè)移動(dòng)到右側(cè),如圖4-1所看到的。


圖 4-1 SampleWindow在手機(jī)中的運(yùn)行效果

2.初識(shí)窗體的創(chuàng)建、繪制與銷毀

SampleWindow的這段代碼盡管簡單,可是卻非常好地提煉了一個(gè)窗體的創(chuàng)建、繪制以及銷毀的過程。注意,本例沒有使用不論什么 WMS以外的系統(tǒng)服務(wù)。也沒有使用Android系統(tǒng)四大組件的框架,也就是說。假設(shè)你愿意,能夠利用WMS實(shí)現(xiàn)自己的UI與應(yīng)用程序框架。這樣就能夠衍生出一個(gè)新的平臺(tái)了。

總結(jié)在client創(chuàng)建一個(gè)窗體的步驟:

·??獲取IWindowSession和WMS實(shí)例。client能夠通過IWindowSession向WMS發(fā)送請求。

·??創(chuàng)建并初始化WindowManager.LayoutParams。注意這里是WindowManager下的LayoutParams,它繼承自ViewGroup.LayoutParams類,并擴(kuò)展了一些窗體相關(guān)的屬性。當(dāng)中最重要的是type屬性。這個(gè)屬性描寫敘述了窗體的類型,而窗體類型正是WMS對多個(gè)窗體進(jìn)行ZOrder排序的依據(jù)。

·??向WMS加入一個(gè)窗體令牌(WindowToken)。本章興許將分析窗體令牌的概念,眼下讀者僅僅要知道。窗體令牌描寫敘述了一個(gè)顯示行為,而且WMS要求每一個(gè)窗體必須隸屬于某一個(gè)顯示令牌。

·??向WMS加入一個(gè)窗體。必須在LayoutParams中指明此窗體所隸屬于的窗體令牌,否則在某些情況下加入操作會(huì)失敗。

在SampleWindow中,不設(shè)置令牌也可成功?完畢加入操作。由于窗體的類型被設(shè)為TYPE_SYSTEM_ALERT,它是系統(tǒng)窗體的一種。

而對于系統(tǒng)窗體。WMS會(huì)自己主動(dòng)為其創(chuàng)建顯示令牌,故無需client擔(dān)心。此話題將會(huì)在后文進(jìn)行更具體的討論。

·??向WMS申請對窗體進(jìn)行又一次布局(relayout)。所謂的又一次布局,就是依據(jù)窗體新的屬性去調(diào)整其Surface相關(guān)的屬性。或者又一次創(chuàng)建一個(gè)Surface(比如窗體尺寸變化導(dǎo)致之前的Surface不滿足要求)。向WMS加入一個(gè)窗體之后,其僅僅是將它在WMS中進(jìn)行了注冊而已。僅僅有經(jīng)過又一次布局之后,窗體才擁有WMS為其分配的畫布。有了畫布,窗體之后就能夠隨時(shí)進(jìn)行繪制工作了。

而窗體的繪制步驟例如以下:

·??通過Surface.lock()函數(shù)獲取能夠在其上作畫的Canvas實(shí)例。

·??使用Canvas實(shí)例進(jìn)行作畫。

·??通過Surface.unlockCanvasAndPost()函數(shù)提交繪制結(jié)果。

提示關(guān)于Surface的原理與使用方法,請參考《卷 I》第8章“深入理解Surface系統(tǒng)”。

這是對Surface作畫的標(biāo)準(zhǔn)方法。

在client也能夠通過OpenGL進(jìn)行作畫。只是這超出了本書的討論范圍。另外,在SampleWindow樣例中使用了Choreographer類進(jìn)行了動(dòng)畫幀的安排。Choreographer意為編舞指導(dǎo),是Jelly Bean新增的一個(gè)工具類。其使用方法與Handler的post()函數(shù)非Z且不會(huì)再顯示新的窗體,則須要從WMS將之前加入的顯示令牌一并刪除。

3.窗體的概念

在SampleWindow樣例中,有一個(gè)名為mWindow(類型為IWindow)的變量。讀者可能會(huì)理所當(dāng)然地覺得它就是窗體了。

事實(shí)上這樣的認(rèn)識(shí)并不全然正確。

IWindow繼承自Binder,而且其Bn端位于應(yīng)用程序一側(cè)(在樣例中IWindow的實(shí)現(xiàn)類MyWindow就繼承自IWindow.Stub),于是其在WMS一側(cè)僅僅能作為一個(gè)回調(diào)。以及起到窗體Id的作用。

那么。窗體的本質(zhì)是什么呢?

是進(jìn)行繪制所使用的畫布:Surface。

當(dāng)一塊Surface顯示在屏幕上時(shí),就是用戶所看到的窗體了。client向WMS加入一個(gè)窗體的過程,事實(shí)上就是WMS為其分配一塊Surface的過程。一塊塊Surface在WMS的管理之下有序地排布在屏幕上,Android才得以呈現(xiàn)出多姿多彩的界面來。所以從這個(gè)意義上來講。WindowManagerService被稱之為SurfaceManagerService也說得通的。

于是,依據(jù)對Surface的操作類型能夠?qū)ndroid的顯示系統(tǒng)分為三個(gè)層次。如圖4-2所看到的。


圖 4-2 Android顯示系統(tǒng)的三個(gè)層次

在圖4-2中:

·??第一個(gè)層次是UI框架層,其工作為在Surface上繪制UI元素以及響應(yīng)輸入事件。

·??第二個(gè)層次為WMS,其主要工作在于管理Surface的分配、層級(jí)順序等。

·??第三層為SurfaceFlinger。負(fù)責(zé)將多個(gè)Surface混合并輸出。

經(jīng)過這個(gè)樣例的介紹,相信大家對WMS的功能有了一個(gè)初步的了解。接下來,我們要進(jìn)入WMS的內(nèi)部,通過其啟動(dòng)過程一窺它的構(gòu)成。

4.1.2WMS的構(gòu)成

俗話說,一個(gè)好漢三個(gè)幫!WMS的強(qiáng)大是由非常多重要的成員互相協(xié)調(diào)工作而實(shí)現(xiàn)的。了解WMS的構(gòu)成將會(huì)為我們深入探索WMS打下良好的基礎(chǔ),進(jìn)而分析它的啟動(dòng)過程。這是再合適只是了。

1.WMS的誕生

和其它的系統(tǒng)服務(wù)一樣,WMS的啟動(dòng)位于SystemServer.java中ServerThread類的run()函數(shù)內(nèi)。

[-->SystemServer.java::ServerThread.run()]

Public void run() {

??? ......

???WindowManagerService wm = null;

??? ......

??? try {

???????......

??????? // ①創(chuàng)建WMS實(shí)例

?????? /* 通過WindowManagerService的靜態(tài)函數(shù)main()創(chuàng)建WindowManagerService的實(shí)例。

?????????? 注意main()函數(shù)的兩個(gè)參數(shù)wmHandler和uiHandler。這兩個(gè)Handler分別運(yùn)行于由

?????????? ServerThread所創(chuàng)建的兩個(gè)名為“WindowManager”和“UI”的兩個(gè)HandlerThread中 */

??????? wm =WindowManagerService.main(context, power, display, inputManager,

??????? uiHandler,wmHandler,

??????? ????????????factoryTest !=SystemServer.FACTORY_TEST_LOW_LEVEL,

???????????????????!firstBoot, onlyCore);

??????? // 加入到ServiceManager中去

??? ????ServiceManager.addService(Context.WINDOW_SERVICE,wm);

???????......

??? catch(RuntimeException e) {

??????? ......

??? }

??? ......

??? try {

??????? //②初始化顯示信息

??????? wm.displayReady();

??? } catch(Throwable e) {......}

??? ......

??? try {

??????? // ③通知WMS,系統(tǒng)的初始化工作完畢

??????? wm.systemReady();

??? } catch(Throwable e) {......}

??? ......

}

由此能夠看出,WMS的創(chuàng)建分為三個(gè)階段:

·??創(chuàng)建WMS的實(shí)例。

·??初始化顯示信息。

·??處理systemReady通知。

接下來,將通過以上三個(gè)階段分析WMS從無到有的過程。

看一下WMS的main()函數(shù)的實(shí)現(xiàn):

[-->WindowManagerService.java::WindowManagerSrevice.main()]

public static WindowManagerService main(finalContext context,

??? finalPowerManagerService pm, final DisplayManagerService dm,

??? finalInputManagerService im,

??? finalHandler uiHandler, final Handler wmHandler,

??? finalboolean haveInputMethods, final boolean showBootMsgs,

??? finalboolean onlyCore) {

??? finalWindowManagerService[] holder = new WindowManagerService[1];

??? // 通過由SystemServer為WMS創(chuàng)建的Handler新建一個(gè)WindowManagerService對象

??? // 此Handler運(yùn)行在一個(gè)名為WindowManager的HandlerThread中

??? wmHandler.runWithScissors(newRunnable() {

???????@Override

??????? publicvoid run() {

??????????? holder[0]= new WindowManagerService(context, pm, dm, im,

??????????????????????????? uiHandler,haveInputMethods, showBootMsgs, onlyCore);

??????? }

??? }, 0);

??? returnholder[0];

}

注意Handler類在Android 4.2中新增了一個(gè)API:runWithScissors()。這個(gè)函數(shù)將會(huì)在Handler所在的線程中運(yùn)行傳入的Runnable對象。同一時(shí)候堵塞調(diào)用線程的運(yùn)行,直到Runnable對象的run()函數(shù)運(yùn)行完畢。

WindowManagerService.main()函數(shù)在ServerThread專為WMS創(chuàng)建的線程“WindowManager”上創(chuàng)建了一個(gè)WindowManagerService的新實(shí)例。WMS中全部須要的Looper對象,比如Handler、Choreographer等。將會(huì)運(yùn)行在“WindowManager”線程中。

接下來看一下其構(gòu)造函數(shù)。看一下WMS定義了哪些重要的組件。

[-->WindowManagerService.java::WindowManagerService.WindowManagerService()]

private WindowManagerService(Context context,PowerManagerService pm,

???????????DisplayManagerService displayManager, InputManagerService inputManager,

???????????Handler uiHandler,

??? booleanhaveInputMethods, boolean showBootMsgs, boolean onlyCore)

??? ......

??? mDisplayManager=

??? ?????????(DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);

??? mDisplayManager.registerDisplayListener(this,null);

??? Display[]displays = mDisplayManager.getDisplays();

??? /* 初始化DisplayContent列表。DisplayContent是Android4.2為支持多屏幕輸出所引入的一個(gè)

?????? 概念。一個(gè)DisplayContent指代一塊屏幕,屏幕能夠是手機(jī)自身的屏幕。也能夠是基于Wi-FiDisplay

?????? 技術(shù)的虛擬屏幕[3]*/

??? for(Display display : displays) {

??????? createDisplayContentLocked(display);

??? }

??? .....

??? /* 保存InputManagerService。輸入事件終于要分發(fā)給具有焦點(diǎn)的窗體。而WMS是窗體管理者,

?????? 所以WMS是輸入系統(tǒng)中的重要一環(huán)。關(guān)于輸入系統(tǒng)的內(nèi)容將在第5章中深入探討*/

??? mInputManager= inputManager;

?

??? // 這個(gè)看起來其貌不揚(yáng)的mAnimator,事實(shí)上具有非常關(guān)鍵的數(shù)據(jù)。它管理著全部窗體的動(dòng)畫

??? mAnimator= new WindowAnimator(this, context, mPolicy);

?

??? // 在“UI“線程中將對還有一個(gè)重要成員mPolicy,也就是WindowManagerPolicy進(jìn)行初始化

??? initPolicy(uiHandler);

?

??? // 將自己加入到Watchdog中

??? Watchdog.getInstance().addMonitor(this);

??? ......

}

第二步。displayReady()函數(shù)的調(diào)用主要是初始化顯示尺寸的信息。其內(nèi)容比較瑣碎。這里就先不介紹了。只是值得注意的一點(diǎn)是。再displayReady()完畢后。WMS會(huì)要求ActivityManagerService進(jìn)行第一次Configuration的更新。

第三步。在systemReady()函數(shù)中。WMS本身將不會(huì)再做不論什么操作了,直接調(diào)用mPolicy的systemReady()函數(shù)。

2.WMS的重要成員

總結(jié)一下在WMS的啟動(dòng)過程中所創(chuàng)建的重要成員,參考圖4-3。


圖 4-3 WMS的重要成員

下面是對圖4-3中重要成員的簡介:

·??mInputManager,InputManagerService(輸入系統(tǒng)服務(wù))的實(shí)例。用于管理每一個(gè)窗體的輸入事件通道(InputChannel)以及向通道上派發(fā)事件。關(guān)于輸入系統(tǒng)的具體內(nèi)容將在本書第5章具體探討。

·??mChoreographer,Choreographer的實(shí)例,在SampleWindow的樣例中已經(jīng)見過了。Choreographer的意思是編舞指導(dǎo)。

它擁有從顯示子系統(tǒng)獲取VSYNC同步事件的能力,從而能夠在合適的時(shí)機(jī)通知渲染動(dòng)作,避免在渲染的過程中由于發(fā)生屏幕重繪而導(dǎo)致的畫面撕裂。從這個(gè)意義上來講,Choreographer的確是指導(dǎo)Android翩翩起舞的大師。WMS使用Choreographer負(fù)責(zé)驅(qū)動(dòng)全部的窗體動(dòng)畫、屏幕旋轉(zhuǎn)動(dòng)畫、墻紙動(dòng)畫的渲染。

·??mAnimator。WindowAnimator的實(shí)例。它是全部窗體動(dòng)畫的總管(窗體動(dòng)畫是一個(gè)WindowStateAnimator的對象)。在Choreographer的驅(qū)動(dòng)下。逐個(gè)渲染全部的動(dòng)畫。

·??mPolicy,WindowPolicyManager的一個(gè)實(shí)現(xiàn)。眼下它僅僅有PhoneWindowManager一個(gè)實(shí)現(xiàn)類。mPolicy定義了非常多窗體相關(guān)的策略,能夠說是WMS的首席顧問。每當(dāng)WMS要做什么事情的時(shí)候,都須要向這個(gè)顧問請教應(yīng)當(dāng)怎樣做。比如,告訴WMS某一個(gè)類型的Window的ZOrder的值是多少,幫助WMS矯正不合理的窗體屬性。會(huì)為WMS監(jiān)聽屏幕旋轉(zhuǎn)的狀態(tài),還會(huì)預(yù)處理一些系統(tǒng)按鍵事件(比如HOME、BACK鍵等的默認(rèn)行為就是在這里實(shí)現(xiàn)的)。等等。所以。mPolicy可謂是WMS中最重要的一個(gè)成員了。

·??mDisplayContents,一個(gè)DisplayContent類型的列表。Android4.2支持基于Wi-fi Display的多屏幕輸出,而一個(gè)DisplayContent描寫敘述了一塊能夠繪制窗體的屏幕。

每一個(gè)DisplayContent都用一個(gè)整型變量作為其ID。當(dāng)中手機(jī)默認(rèn)屏幕的ID由Display.DEFAULT_DISPLAY常量指定。DisplayContent的管理是由DisplayManagerService完畢的,在本章不會(huì)去探討DisplayContent的實(shí)現(xiàn)細(xì)節(jié)。而是關(guān)注DisplayContent對窗體管理與布局的影響。

下面的幾個(gè)成員的初始化并沒有出如今構(gòu)造函數(shù)中。只是它們的重要性一點(diǎn)也不亞于上面幾個(gè)。

·??mTokenMap,一個(gè)HashMap,保存了全部的顯示令牌(類型為WindowToken)。用于窗體管理。

在SampleWindow樣例中以前提到過,一個(gè)窗體必須隸屬于某一個(gè)顯示令牌。在那個(gè)樣例中所加入的令牌就被放進(jìn)了這個(gè)HashMap中。從這個(gè)成員中還衍生出幾個(gè)輔助的顯示令牌的子集。比如mAppTokens保存了全部屬于Activity的顯示令牌(WindowToken的子類AppWindowToken),mExitingTokens則保存了正在退出過程中的顯示令牌等。當(dāng)中mAppTokens列表是有序的,它與AMS中的mHistory列表的順序保持一致。反映了系統(tǒng)中Activity的順序。

·??mWindowMap,也是一個(gè)HashMap。保存了全部窗體的狀態(tài)信息(類型為WindowState),用于窗體管理。

在SampleWindow樣例中,使用IWindowSession.add()所加入的窗體的狀態(tài)將會(huì)被保存在mWindowMap中。與mTokenMap一樣,mWindowMap一樣有衍生出的子集。

比如mPendingRemove保存了那些退出動(dòng)畫播放完畢并即將被移除的窗體,mLosingFocus則保存了那些失去了輸入焦點(diǎn)的窗體。在DisplayContent中,也有一個(gè)windows列表,這個(gè)列表存儲(chǔ)了顯示在此DisplayContent中的窗體,而且它是有序的。

窗體在這個(gè)列表中的位置決定了其終于顯示時(shí)的Z序。

·??mSessions。一個(gè)List。元素類型為Session。Session事實(shí)上是SampleWindow樣例中的IWindowSession的Bn端。也就是說,mSessions這個(gè)列表保存了當(dāng)前全部想向WMS尋求窗體管理服務(wù)的client。

注意Session是進(jìn)程唯一的。

·??mRotation,僅僅是一個(gè)int型變量。

它保存了當(dāng)前手機(jī)的旋轉(zhuǎn)狀態(tài)。

WMS定義的成員一定不止這些,可是它們是WMS每一種功能最核心的變量。讀者在這里能夠線對它們有一個(gè)感性的認(rèn)識(shí)。在本章興許的內(nèi)容里將會(huì)具體分析它們在WMS的各種工作中所發(fā)揮的核心作用。

4.1.3 初識(shí)WMS的小結(jié)

這一節(jié)通過SampleWindow的樣例向讀者介紹了WMS的client怎樣使用窗體,然后通過WMS的誕生過程簡單剖析了一下WMS的重要成員組成。以期通過本節(jié)的學(xué)習(xí)能夠?yàn)榕d許的學(xué)習(xí)打下基礎(chǔ)。

從下一節(jié)開始,我們將會(huì)深入探討WMS的工作原理。

4.2 WMS的窗體管理結(jié)構(gòu)

經(jīng)過上一節(jié)的介紹,讀者應(yīng)該對WMS的窗體管理有了一個(gè)感性的認(rèn)識(shí)。從這一節(jié)開將深入WMS的內(nèi)部去剖析其工作流程。

依據(jù)前述內(nèi)容可知,SampleWindow加入窗體的函數(shù)是IWindowSession.add()。

IWindowSession是WMS與client交互的一個(gè)代理,add則直接調(diào)用到了WMS的addWindow()函數(shù)。

我們將從這個(gè)函數(shù)開始WMS之旅。

本小節(jié)僅僅討論它的前半部分。

注意由于篇幅所限,本章不準(zhǔn)備討論removeWindow的實(shí)現(xiàn)。

[-->WindowManagerService.java::WindowManagerService.addWindow()Part1]

public int addWindow(Session session, IWindowclient, int seq,

???????????WindowManager.LayoutParams attrs, int viewVisibility,int displayId

???????????Rect outContentInsets, InputChannel outInputChannel) {

?

??????? // 首先檢查權(quán)限,沒有權(quán)限的client不能加入窗體

??????? intres = mPolicy.checkAddPermission(attrs);

?

???????......

?

??????? // 當(dāng)為某個(gè)窗體加入子窗體時(shí),attachedWindow將用來保存父窗體的實(shí)例

???????WindowState attachedWindow = null;

??????? //win就是即將被加入的窗體了

???????WindowState win = null;

???????......

??????? finalint type = attrs.type;

??????? synchronized(mWindowMap){

???????????......

???????????//①獲取窗體要加入到的DisplayContent

???????????/* 在加入窗體時(shí),必須通過displayId參數(shù)指定加入到哪一個(gè)DisplayContent。

??????????????SampleWindow樣例沒有指定displayId參數(shù)。Session會(huì)替SampleWindow選擇

??????????????DEFAULT_DISPLAY,也就是手機(jī)屏幕 */

??????????? finalDisplayContent displayContent = getDisplayContentLocked(displayId);

??????????? if(displayContent == null) {

???????????????return WindowManagerGlobal.ADD_INVALID_DISPLAY;

???????????}

???????????......

???????????// 假設(shè)要加入的窗體是還有一個(gè)的子窗體,就要求父窗體必須已經(jīng)存在
??????????? // 注意。 attrs.type表示了窗體的類型,attrs.token則表示了窗體所隸屬的對象

???????????// 對于子窗體來說。attrs.token表示了父窗體

??????????? if(type >= FIRST_SUB_WINDOW &&.type <= LAST_SUB_WINDOW) {

???????????????attachedWindow = windowForClientLocked(null, attrs.token, false);

???????????????if (attachedWindow == null) {

???????????????????return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;

???????????????}

???????????????//在這里還能夠看出WMS要求窗體的層級(jí)關(guān)系最多為兩層

???????????????if (attachedWindow.mAttrs.type >= FIRST_SUB_WINDOW

??????????????????????????????? &&attachedWindow.mAttrs.type <= LAST_SUB_WINDOW) {

????????? ??????????return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;

???????????????}

???????????}

?

??????????? booleanaddToken = false;

???????????// ②WindowToken出場!

依據(jù)client的attrs.token取出已注冊的WindowToken

???????????WindowToken token = mTokenMap.get(attrs.token);

???????????// 下面的if語句塊初步揭示了WindowToken和窗體之間的關(guān)系

??????????? if(token == null) {

???????????????// 對于下面幾種類型的窗體,必須通過LayoutParams.token成員為其指定一個(gè)已經(jīng)

???????????????// 加入至WMS的WindowToken

???????????????if (type >= FIRST_APPLICATION_WINDOW

??????????????????????????????? && type<= LAST_APPLICATION_WINDOW) {

???????????????????return WindowManagerGlobal.ADD_BAD_APP_TOKEN;

???????????????}

???????????????if (type == TYPE_INPUT_METHOD) {

???????????????????return WindowManagerGlobal.ADD_BAD_APP_TOKEN;

???????????????}

???????????????if (type == TYPE_WALLPAPER) {

???????????????????return WindowManagerGlobal.ADD_BAD_APP_TOKEN;

???????????????}

???????????????if (type == TYPE_DREAM) {

???????????????????return WindowManagerGlobal.ADD_BAD_APP_TOKEN;

???????????????}

???????????????// 其它類型的窗體則不須要事先向WMS加入WindowToken由于WMS會(huì)在這里隱式地創(chuàng)

???????????????// 建一個(gè)。注意最后一個(gè)參數(shù)false,這表示此WindowToken由WMS隱式創(chuàng)建。

???????????????token = new WindowToken(this, attrs.token, -1, false);

? ??????????????addToken = true;

???????????} else if (type >= FIRST_APPLICATION_WINDOW

???????????????????????????????????????????? &&type <= LAST_APPLICATION_WINDOW) {

???????????????// 對于APPLICATION類型的窗體,要求相應(yīng)的WindowToken的類型也為APPLICATION

???????????????// 而且是WindowToken的子類:AppWindowToken

???????????????AppWindowToken atoken = token.appWindowToken;

???????????????if (atoken == null) {

???????????????????return WindowManagerImpl.ADD_NOT_APP_TOKEN;

???????????????} else if (atoken.removed) {

????????? ??????????returnWindowManagerImpl.ADD_APP_EXITING;

???????????????}

???????????????if (type==TYPE_APPLICATION_STARTING && atoken.firstWindowDrawn){

???????????????????return WindowManagerImpl.ADD_STARTING_NOT_NEEDED;

???????????????}

???????????} else if (type == TYPE_INPUT_METHOD) {

???????????????// 對于其它幾種類型的窗體也有相似的要求:窗體類型必須與WindowToken的類型一致

???????????????if (token.windowType != TYPE_INPUT_METHOD) {

???????????????????return WindowManagerGlobal.ADD_BAD_APP_TOKEN;

???????????????}

???????????} else if (type == TYPE_WALLPAPER) {

???????????????if (token.windowType != TYPE_WALLPAPER) {

???????????????????return WindowManagerGlobal.ADD_BAD_APP_TOKEN;

???????????????}

???????????} else if (type == TYPE_DREAM) {

???????????????if (token.windowType != TYPE_DREAM) {

???????????????????return WindowManagerGlobal.ADD_BAD_APP_TOKEN;

???????????????}

???????????}

?

???????????// ③WMS為要加入的窗體創(chuàng)建了一個(gè)WindowState對象

???????????// 這個(gè)對象維護(hù)了一個(gè)窗體的全部狀態(tài)信息

??????????? win= new WindowState(this, session, client, token,

??????????? attachedWindow,seq, attrs, viewVisibility, displayContent);

?

???????????......

???????????// WindowManagerPolicy出場了。這個(gè)函數(shù)的調(diào)用會(huì)調(diào)整LayoutParams的一些成員的取值

??????????? mPolicy.adjustWindowParamsLw(win.mAttrs);

?

??????????? res= mPolicy.prepareAddWindowLw(win, attrs);

??????????? if(res != WindowManagerGlobal.ADD_OKAY) {

???????????????return res;

???????????}

?

???????????// 接下來將剛剛隱式創(chuàng)建的WindowToken加入到mTokenMap中去。通過這行代碼應(yīng)該

???????????//讀者應(yīng)該能想到。全部的WindowToken都被放入這個(gè)HashTable中

??????????? ......

?

??????????? if(addToken) {

???????????????mTokenMap.put(attrs.token, token);

???????????}

??????????? win.attach();

???????????// 然后將WindowState對象加入到mWindowMap中

??????????? mWindowMap.put(client.asBinder(),win);

???????????// 剩下的代碼稍后再做分析

???????????......

??????? }

??? }

addWindow()函數(shù)的前段代碼展示了三個(gè)重要的概念,各自是WindowToken、WindowState以及DisplayContent。而且在函數(shù)開始處對窗體類型的檢查推斷也初步揭示了它們之間的關(guān)系:除子窗體外,加入不論什么一個(gè)窗體都必須指明其所屬的WindowToken;窗體在WMS中通過一個(gè)WindowState實(shí)例進(jìn)行管理和保管。同一時(shí)候必須在窗體中指明其所屬的DisplayContent,以便確定窗體將被顯示到哪一個(gè)屏幕上。

4.2.1 理解WindowToken

1.WindowToken的意義

為了搞清晰WindowToken的作用是什么。看一下其位于WindowToken.java中的定義。盡管它未定義不論什么函數(shù),但其成員變量的意義卻非常重要。

·??WindowToken將屬于同一個(gè)應(yīng)用組件的窗體組織在了一起。所謂的應(yīng)用組件能夠是Activity、InputMethod、Wallpaper以及Dream。在WMS對窗體的管理過程中,用WindowToken指代一個(gè)應(yīng)用組件。比如在進(jìn)行窗體ZOrder排序時(shí)。屬于同一個(gè)WindowToken的窗體會(huì)被安排在一起,而且在當(dāng)中定義的一些屬性將會(huì)影響全部屬于此WindowToken的窗體。這些都表明了屬于同一個(gè)WindowToken的窗體之間的緊密聯(lián)系。

·??WindowToken具有令牌的作用,是相應(yīng)用組件的行為進(jìn)行規(guī)范管理的一個(gè)手段。

WindowToken由應(yīng)用組件或其管理者負(fù)責(zé)向WMS聲明并持有。應(yīng)用組件在須要新的窗體時(shí)。必須提供WindowToken以表明自己的身份,而且窗體的類型必須與所持有的WindowToken的類型一致。

從上面的代碼能夠看到,在創(chuàng)建系統(tǒng)類型的窗體時(shí)不須要提供一個(gè)有效的Token,WMS會(huì)隱式地為其聲明一個(gè)WindowToken,看起來誰都能夠加入個(gè)系統(tǒng)級(jí)的窗體。難道Android為了內(nèi)部使用方便而置安全于不顧嗎?非也。addWindow()函數(shù)一開始的mPolicy.checkAddPermission()的目的就是如此。它要求client必須擁有SYSTEM_ALERT_WINDOW或INTERNAL_SYSTEM_WINDOW權(quán)限才干創(chuàng)建系統(tǒng)類型的窗體。

2.向WMS聲明WindowToken

既然應(yīng)用組件在創(chuàng)建一個(gè)窗體時(shí)必須指定一個(gè)有效的WindowToken才行,那么WindowToken到底該怎樣聲明呢?

在SampleWindow應(yīng)用中,使用wms.addWindowToken()函數(shù)聲明mToken作為它的令牌,所以在加入窗體時(shí),通過設(shè)置lp.token為mToken向WMS進(jìn)行出示。從而獲得WMS加入窗體的許可。

這說明。僅僅要是一個(gè)Binder對象(隨便一個(gè)),都能夠作為Token向WMS進(jìn)行聲明。對于WMS的client來說,Token僅僅是一個(gè)Binder對象而已

為了驗(yàn)證這一點(diǎn),來看一下addWindowToken的代碼。例如以下所看到的:

[-->WindowManagerService.java::WindowManagerService.addWindowToken()]

???@Override

??? publicvoid addWindowToken(IBinder token, int type) {

??????? // 須要聲明Token的調(diào)用者擁有MANAGE_APP_TOKENS的權(quán)限

??????? if(!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,

???????????????"addWindowToken()")) {

??????????? thrownew SecurityException("Requires MANAGE_APP_TOKENS permission");

??????? }

?

??????? synchronized(mWindowMap){

??????????? ......

???????????// 注意其構(gòu)造函數(shù)的參數(shù)與addWindow()中不同。最后一個(gè)參數(shù)為true,表明這個(gè)Token

???????????// 是顯式申明的

??????????? wtoken= new WindowToken(this, token, type, true);

??????????? mTokenMap.put(token,wtoken);

??????????? ......

??????? }

??? }

使用addWindowToken()函數(shù)聲明Token,將會(huì)在WMS中創(chuàng)建一個(gè)WindowToken實(shí)例。并加入到mTokenMap中。鍵值為client用于聲明Token的Binder實(shí)例。與addWindow()函數(shù)中隱式地創(chuàng)建WindowToken不同。這里的WindowToken被聲明為顯式的。隱式與顯式的差別在于,當(dāng)隱式創(chuàng)建的WindowToken的最后一個(gè)窗體被移除后,此WindowToken會(huì)被一并從mTokenMap中移除。顯式創(chuàng)建的WindowToken僅僅能通過removeWindowToken()顯式地移除。

addWindowToken()這個(gè)函數(shù)告訴我們。WindowToken事實(shí)上有兩層含義:

·??對于顯示組件(client)而言的Token。是隨意一個(gè)Binder的實(shí)例,對顯示組件(client)來說僅僅是一個(gè)創(chuàng)建窗體的令牌,沒有其它的含義。

·??對于WMS而言的WindowToken這是一個(gè)WindowToken類的實(shí)例,保存了相應(yīng)于client一側(cè)的Token(Binder實(shí)例)。并以這個(gè)Token為鍵。存儲(chǔ)于mTokenMap中。

client一側(cè)的Token是否已被聲明,取決于其相應(yīng)的WindowToken是否位于mTokenMap中。

注意在普通情況下,稱顯示組件(client)一側(cè)Binder的實(shí)例為Token,而稱WMS一側(cè)的WindowToken對象為WindowToken。可是為了敘述方便,在沒有歧義的前提下不會(huì)過分細(xì)致地區(qū)分這兩個(gè)概念。

接下來,看一下各種顯示組件是怎樣聲明WindowToken的。

(1)??? Wallpaper和InputMethod的Token

Wallpaper的Token聲明在WallpaperManagerService中。

參考下面代碼:

[-->WallpaperManagerService.java::WallpaperManagerService.bindWallpaperComponentLocked()]

BooleanbindWallpaperComponentLocked(......) {

??? ......

???WallpaperConnection newConn = new WallpaperConnection(wi, wallpaper);

??? ......

??? mIWindowManager.addWindowToken(newConn.mToken,

?????????????????????????WindowManager.LayoutParams.TYPE_WALLPAPER);

??? ......

}

WallpaperManagerService是Wallpaper管理器,它負(fù)責(zé)維護(hù)系統(tǒng)已安裝的全部的Wallpaper并在它們之間進(jìn)行切換。而這個(gè)函數(shù)的目的是準(zhǔn)備顯示一個(gè)Wallpaper。

newConn.mToken與SampleWindow樣例一樣。是一個(gè)簡單的Binder對象。

這個(gè)Token將在即將顯示出來的Wallpaper被連接時(shí)傳遞給它,之后Wallpaper就可以通過這個(gè)Token向WMS申請創(chuàng)建繪制壁紙所需的窗體了。

注意 WallpaperManagerService向WMS聲明的Token類型為TYPE_WALLPAPER,所以,Wallpaper僅能本分地創(chuàng)建TYPE_WALLPAPER類型的窗體。

相應(yīng)的。WallpaperManagerService會(huì)在detachWallpaperLocked()函數(shù)中取消對Token的聲明:

[-->WallpaperManagerService.java::WallpaperManagerService.detachWallpaperLocked()]

booleandetachWallpaperLocked(WallpaperData wallpaper){

??? ......

??? mIWindowManager.removeWindowToken(wallpaper.connection.mToken);

??? ......

}

再此之后。假設(shè)這個(gè)被detach的Wallpaper想再要?jiǎng)?chuàng)建窗體便不再可能了。

WallpaperManagerService使用WindowToken對一個(gè)特定的Wallpaper做出了例如以下限制:

·??Wallpaper僅僅能創(chuàng)建TYPE_WALLPAPER類型的窗體。

·??Wallpaper顯示的生命周期由WallpaperManagerService牢牢地控制著。僅有當(dāng)前的Wallpaper才干創(chuàng)建窗體并顯示內(nèi)容。其它的Wallpaper由于沒有有效的Token,而無法創(chuàng)建窗體。

InputMethod的Token的來源與Wallpaper相似,其聲明位于InputMethodManagerService的startInputInnerLocked()函數(shù)中,取消聲明的位置在InputmethodManagerService的unbindCurrentMethodLocked()函數(shù)。InputMethodManagerService通過Token限制著每一個(gè)InputMethod的窗體類型以及顯示生命周期。

(2)??? Activity的Token

Activity的Token的使用方式與Wallpaper和InputMethod相似。可是其包括很多其它的內(nèi)容。

畢竟,對于Activity。不管是其組成還是操作都比Wallpaper以及InputMethod復(fù)雜得多。

對此。WMS專為Activity實(shí)現(xiàn)了一個(gè)WindowToken的子類:AppWindowToken。

既然AppWindowToken是為Activity服務(wù)的。那么其聲明自然在ActivityManagerService中。具體位置為ActivityStack.startActivityLocked(),也就是啟動(dòng)Activity的時(shí)候。相關(guān)代碼例如以下:

[-->ActivityStack.java::ActivityStack.startActivityLocked()]

private final void startActivityLocked(......) {

??? ......

??? mService.mWindowManager.addAppToken(addPos,r.appToken, r.task.taskId,

???????????????????????????????r.info.screenOrientation, r.fullscreen);

??? ......

}

startActivityLocked()向WMS聲明r.appToken作為此Activity的Token,這個(gè)Token是在ActivityRecord的構(gòu)造函數(shù)中創(chuàng)建的。隨然后在realStartActivityLocked()中將此Token交付給即將啟動(dòng)的Activity。

[-->ActivityStack.java::ActivityStack.realStartActivityLocked()]

final boolean realStartActivityLocked(......) {

??? ......

??? app.thread.scheduleLaunchActivity(newIntent(r.intent), r.appToken,

????? ??????????System.identityHashCode(r), r.info,

?????? ?????????newConfiguration(mService.mConfiguration),

???? ???????????r.compat, r.icicle, results, newIntents,!andResume,

??? mService.isNextTransitionForward(),profileFile, profileFd,

????? ??????????profileAutoStop);

??? ......

}

啟動(dòng)后的Activity就可以使用此Token創(chuàng)建類型為TYPE_APPLICATION的窗體了。

取消Token的聲明則位于ActivityStack.removeActivityFromHistoryLocked()函數(shù)中。

Activity的Token在client是否和Wallpaper一樣,僅僅是一個(gè)主要的Binder實(shí)例呢?事實(shí)上不然。看一下r.appToken的定義能夠發(fā)現(xiàn)。這個(gè)Token的類型是IApplicationToken.Stub。當(dāng)中定義了一系列和窗體相關(guān)的一些通知回調(diào),它們是:

·?? windowsDrawn()。當(dāng)窗體完畢初次繪制后通知AMS。

·?? windowsVisible(),當(dāng)窗體可見時(shí)通知AMS。

·?? windowsGone(),當(dāng)窗體不可見時(shí)通知AMS。

·?? keyDispatchingTimeout(),窗體沒能按時(shí)完畢輸入事件的處理。這個(gè)回調(diào)將會(huì)導(dǎo)致ANR。

·?? getKeyDispatchingTimeout(),從AMS處獲取界定ANR的時(shí)間。

AMS通過ActivityRecord表示一個(gè)Activity。而ActivityRecord的appToken在其構(gòu)造函數(shù)中被創(chuàng)建,所以每一個(gè)ActivityRecord擁有其各自的appToken。

而WMS接受AMS對Token的聲明。并為appToken創(chuàng)建了唯一的一個(gè)AppWindowToken。

因此,這個(gè)類型為IApplicationToken的Binder對象appToken粘結(jié)了AMS的ActivityRecord與WMS的AppWindowToken。僅僅要給定一個(gè)ActivityRecord。都能夠通過appToken在WMS中找到一個(gè)相應(yīng)的AppWindowToken,從而使得AMS擁有了操縱Activity的窗體繪制的能力。比如,當(dāng)AMS覺得一個(gè)Activity須要被隱藏時(shí)。以Activity相應(yīng)的ActivityRecord所擁有的appToken作為參數(shù)調(diào)用WMS的setAppVisibility()函數(shù)。此函數(shù)通過appToken找到其相應(yīng)的AppWindowToken,然后將屬于這個(gè)Token的全部窗體隱藏。

注意每當(dāng)AMS由于某些原因(如啟動(dòng)/結(jié)束一個(gè)Activity,或?qū)ask移到前臺(tái)或后臺(tái))而調(diào)整ActivityRecord在mHistory中的順序時(shí),都會(huì)調(diào)用WMS相關(guān)的接口移動(dòng)AppWindowToken在mAppTokens中的順序。以保證兩者的順序一致。在后面解說窗體排序規(guī)則時(shí)會(huì)介紹到,AppWindowToken的順序?qū)Υ绑w的順序影響非常大。

4.2.2 理解WindowState

從WindowManagerService.addWindow()函數(shù)的實(shí)現(xiàn)中能夠看出,當(dāng)向WMS加入一個(gè)窗體時(shí),WMS會(huì)為其創(chuàng)建一個(gè)WindowState。

WindowState表示一個(gè)窗體的全部屬性,所以它是WMS中事實(shí)上的窗體。

這些屬性將在后面遇到時(shí)再做介紹。

相似于WindowToken。WindowState在顯示組件一側(cè)也有個(gè)相應(yīng)的類型:IWindow.Stub。IWindow.Stub提供了非常多與窗體管理相關(guān)通知的回調(diào)。比如尺寸變化、焦點(diǎn)變化等。

另外,從WindowManagerService.addWindow()函數(shù)中看到新的WindowState被保存到mWindowMap中,鍵值為IWindow的Bp端。mWindowMap是整個(gè)系統(tǒng)全部窗體的一個(gè)全集。

說明對照一下mTokenMap和mWindowMap。

這兩個(gè)HashMap維護(hù)了WMS中最重要的兩類數(shù)據(jù):WindowToken及WindowState。

它們的鍵都是IBinder,差別是: mTokenMap的鍵值可能是IAppWindowToken的Bp端(使用addAppToken()進(jìn)行聲明)。或者是其它隨意一個(gè)Binder的Bp端(使用addWindowToken()進(jìn)行聲明);而mWindowToken的鍵值一定是IWindow的Bp端。

關(guān)于WindowState的很多其它細(xì)節(jié)將在后面的講述中進(jìn)行介紹。只是經(jīng)過上面的分析,不難得到WindowToken和WindowState之間的關(guān)系,參考圖4-4。


圖 4-4 WindowToken與WindowState的關(guān)系

更具體一些,以一個(gè)正在回放視頻并彈出兩個(gè)對話框的Activity為例。WindowToken與WindowState的意義如圖4-5所看到的。


圖 4-5WindowState與WindowToken的從屬關(guān)系

4.2.3理解DisplayContent??????????????

假設(shè)說WindowToken依照窗體之間的邏輯關(guān)系將其分組。那么DisplayContent則依據(jù)窗體的顯示位置將其分組。隸屬于同一個(gè)DisplayContent的窗體將會(huì)被顯示在同一個(gè)屏幕中。每一個(gè)DisplayContent都相應(yīng)這一個(gè)唯一的ID。在加入窗體時(shí)能夠通過指定這個(gè)ID決定其將被顯示在那個(gè)屏幕中。

DisplayContent是一個(gè)非常具有隔離性的一個(gè)概念。

處于不同DisplayContent的兩個(gè)窗體在布局、顯示順序以及動(dòng)畫處理上不會(huì)產(chǎn)生不論什么耦合。因此,就這幾個(gè)方面來說,DisplayContent就像一個(gè)孤島,全部這些操作都能夠在其內(nèi)部獨(dú)立運(yùn)行。因此,這些本來屬于整個(gè)WMS全局性的操作,變成了DisplayContent內(nèi)部的操作了。

4.3 理解窗體的顯示次序

在addWindow()函數(shù)的前半部分中,WMS為窗體創(chuàng)建了用于描寫敘述窗體狀態(tài)的WindowState。接下來便會(huì)為新建的窗體確定顯示次序。

手機(jī)屏幕是以左上角為原點(diǎn),向右為X軸方向。向下為Y軸方向的一個(gè)二維空間。為了方便管理窗體的顯示次序,手機(jī)的屏幕被擴(kuò)展為了一個(gè)三維的空間。即多定義了一個(gè)Z軸,其方向?yàn)榇怪庇谄聊槐砻嬷赶蚱聊煌狻6鄠€(gè)窗體依照其前后順序排布在這個(gè)虛擬的Z軸上,因此窗體的顯示次序又被稱為Z序(Z order)。在這一節(jié)中將深入探討WMS確定窗體顯示次序的過程以及其影響因素。

4.3.1 主序、子序和窗體類型

看一下WindowState的構(gòu)造函數(shù):

[-->WindowState.java::WindowState.WindowState()]

WindowState(WindowManagerService service, Sessions, IWindow c, WindowToken token,

??????WindowState attachedWindow, int seq, WindowManager.LayoutParams a,

??? intviewVisibility, final DisplayContent displayContent) {

??? ......

??? // 為子窗體分配ZOrder

??? if((mAttrs.type >= FIRST_SUB_WINDOW &&

???????????mAttrs.type <= LAST_SUB_WINDOW)) {

??????? // 這里的mPolicy即是WindowManagerPolicy

??????? mBaseLayer= mPolicy.windowTypeToLayerLw(

???????????????attachedWindow.mAttrs.type)

???????????????* WindowManagerService.TYPE_LAYER_MULTIPLIER

???????????????+ WindowManagerService.TYPE_LAYER_OFFSET;

??????? mSubLayer= mPolicy.subWindowTypeToLayerLw(a.type);

???????......

??? } else {// 為普通窗體分配ZOrder

??????? mBaseLayer= mPolicy.windowTypeToLayerLw(a.type)

???????????????* WindowManagerService.TYPE_LAYER_MULTIPLIER

???????????????+ WindowManagerService.TYPE_LAYER_OFFSET;

??????? mSubLayer= 0;

???????......

??? }

??? ......

}

窗體的顯示次序由兩個(gè)成員字段描寫敘述:主序mBaseLayer和子序mSubLayer。

主序用于描寫敘述窗體及其子窗體在全部窗體中的顯示位置。

而子序則描寫敘述了一個(gè)子窗體在其兄弟窗體中的顯示位置。

·??主序越大,則窗體及其子窗體的顯示位置相對于其它窗體的位置越靠前。

·??子序越大,則子窗體相對于其兄弟窗體的位置越靠前。對于父窗體而言,其主序取決于其類型。其子序則保持為0。而子窗體的主序與其父窗體一樣。子序則取決于其類型。從上述代碼能夠看到,主序與子序的分配工作是由WindowManagerPolicy的兩個(gè)成員函數(shù)windowTypeToLayerLw()和subWindowTypeToLayerLw()完畢的。

表4-1與表4-2列出了全部可能的窗體類型以及其主序與子序的值。

表 4-1 窗體的主序

窗體類型

主序

窗體類型

主序

TYPE_UNIVERSE_BACKGROUND

11000

TYPE_WALLPAPER

21000

TYPE_PHONE

31000

TYPE_SEARCH_BAR

41000

TYPE_RECENTS_OVERLAY

51000

TYPE_SYSTEM_DIALOG

51000

TYPE_TOAST

61000

TYPE_PRIORITY_PHONE

71000

TYPE_DREAM

81000

TYPE_SYSTEM_ALERT

91000

TYPE_INPUT_METHOD

101000

TYPE_INPUT_METHOD_DIALOG

111000

TYPE_KEYGUARD

121000

TYPE_KEYGUARD_DIALOG

131000

TYPE_STATUS_BAR_SUB_PANEL

141000

應(yīng)用窗體與未知類型的窗體

21000

?

表 4-2 窗體的子序

子窗體類型

子序

TYPE_APPLICATION_PANEL

1

TYPE_APPLICATION_ATTACHED_DIALOG

1

TYPE_APPLICATION_MEDIA

-2

TYPE_APPLICATION_MEDIA_OVERLAY

-1

TYPE_APPLICATION_SUB_PANEL

2

?

注意表4-2中的MEDIA和MEDIA_OVERLAY的子序?yàn)樨?fù)值,這表明它們的顯示次序位于其父窗體的后面。這兩個(gè)類型的子窗體是SurfaceView控件創(chuàng)建的。SurfaceView被實(shí)例化后。會(huì)向WMS加入一個(gè)類型為MEDIA的子窗體。它的父窗體就是承載SurfaceView控件的窗體。

這個(gè)子窗體的Surface將被用于視頻回放、相機(jī)預(yù)覽或游戲繪制。為了不讓這個(gè)子窗體覆蓋住全部的父窗體中承載的其它控件(如拍照button,播放器控制button等)。它必須位于父窗體之后。

從表4-1所描寫敘述的主序與窗體類型的相應(yīng)關(guān)系中能夠看出。WALLPAPER類型的窗體的主序竟和APPLICATION類型的窗體主序同樣。這看似有點(diǎn)不合常理。WALLPAPER不是應(yīng)該顯示在全部Acitivity之下嗎?事實(shí)上WALLPAPER類型的窗體是一個(gè)非常不安分的角色,須要在全部的APPLICATION窗體之間跳來跳去。

這是由于,有的Activity指定了android:windowShowWallpaper為true。則表示窗體要求將用戶當(dāng)前壁紙作為其背景。對于WMS來說,最簡單的辦法就是將WALLPAPER窗體放置到緊鄰擁有這個(gè)式樣的窗體的下方。在這樣的需求下。為了保證主序決定窗體順序的原則,WALLPAPER使用了與APPLICATION同樣的主序。另外。輸入法窗體也是一個(gè)非常特殊的情況。輸入法窗體會(huì)選擇輸入目標(biāo)窗體,并將自己放置于其上。在本章中不討論這兩個(gè)特殊的樣例,WALLPAPER的排序規(guī)則將在第7章中進(jìn)行介紹。而輸入法的排序則留給讀者自行研究。

盡管知道了窗體的主序與子序是怎樣分配的,只是我們?nèi)匀淮嬗幸蓡?#xff1a;假設(shè)有兩個(gè)同樣類型的窗體,那么它們的主序與子序豈不是全然同樣?怎樣確定它們的顯示順序呢?事實(shí)上。表4-1和表4-2中所描寫敘述的主序和子序僅僅是排序的依據(jù)之中的一個(gè),WMS須要依據(jù)當(dāng)前全部同類型窗體的數(shù)量為每一個(gè)窗體計(jì)算終于的現(xiàn)實(shí)次序。

4.3.2 通過主序與子序確定窗體的次序

回到WMS的addWindow()函數(shù)中。繼續(xù)往下看:

[-->WindowManagerService.java::WindowManagerService.addWindow()]

public int addWindow(Session session, IWindowclient, int seq,

???WindowManager.LayoutParams attrs, int viewVisibility, int displayId,

??? RectoutContentInsets, InputChannel outInputChannel) {

??? ......

??? synchronized(mWindowMap){

??????? //在前面的代碼中,WMS驗(yàn)證了加入窗體的令牌的有效性。并為新窗體創(chuàng)建了新的WindowState對象

??????? // 新的WindowState對象在其構(gòu)造函數(shù)中依據(jù)窗體類型初始化了其主序mBaseLayer和mSubLayer

???????......

??????? // 接下來,將新的WindowState依照顯示次序插入到當(dāng)前DisplayContent的mWindows列表中

??????? // 為了代碼結(jié)構(gòu)的清晰。不考慮輸入法窗體和壁紙窗體的處理

??? if (type== TYPE_INPUT_METHOD) {

??????? ......

??????? }else if (type == TYPE_INPUT_METHOD_DIALOG) {

??????? }else {

???????????// 將新的WindowState按顯示次序插入到當(dāng)前DisplayContent的mWindows列表中

??????????? addWindowToListInOrderLocked(win,true);

??????????? if(type == TYPE_WALLPAPER) {

???????????????......

???????????}

??????? }

???????......

??????? // 依據(jù)窗體的排序結(jié)果,為DisplayContent的全部窗體分配終于的顯示次序

??????? assignLayersLocked(displayContent.getWindowList());

??????? ......

??? }

??? ......

??? returnres;

}

這里有兩個(gè)關(guān)鍵點(diǎn):

·??addWindowToListInOrderLocked()將新建的WindowState依照一定的順序插入到當(dāng)前DisplayContent的mWindows列表中。

在分析WMS的重要成員時(shí)提到過這個(gè)列表。它嚴(yán)格地依照顯示順序存儲(chǔ)了全部窗體的WindowState。

·??assignLayersLocked()將依據(jù)mWindows的存儲(chǔ)順序?qū)θ康腤indowState的主序和子序進(jìn)行調(diào)整。

接下來分別分析一下這兩個(gè)函數(shù)。

1.addWindowToListInOrderLocked()分析

addWindowToListInOrderLocked()的代碼非常長。只是其排序原則卻比較清晰。這里直接給出其處理原則。感興趣的讀者可依據(jù)這些原則自行深究相關(guān)代碼。

注意再次強(qiáng)調(diào)一下,mWindows列表是依照主序與子序的升序進(jìn)行排序的。所以顯示靠前的窗體放在列表靠后的位置,而顯示靠前的窗體,則位于列表的前面。也就是說,列表順序與顯示順序是相反的。

這點(diǎn)在閱讀代碼時(shí)要牢記。以免混淆。

在后面的敘述中,非特別強(qiáng)調(diào),所謂的前后都是指顯示順序而不是在列表的存儲(chǔ)順序。

子窗體的排序規(guī)則:子窗體的位置計(jì)算是相對父窗體的。并依據(jù)其子序進(jìn)行排序。由于父窗體的子序?yàn)?,所以子序?yàn)樨?fù)數(shù)的窗體會(huì)放置在父窗體的后面,而子序?yàn)檎龜?shù)的窗體會(huì)放置在父窗體的前面。

假設(shè)新窗體與現(xiàn)有窗體子序相等,則正數(shù)子序的新窗體位于現(xiàn)有窗體的前面,負(fù)數(shù)子序的新窗體位于現(xiàn)有窗體的后面。

非子窗體的排序則是依據(jù)主序進(jìn)行的,可是其規(guī)則較為復(fù)雜,分為應(yīng)用窗體和非應(yīng)用窗體兩種情況。之所以要差別處理應(yīng)用窗體是由于全部的應(yīng)用窗體的初始主序都是21000。而且應(yīng)用窗體的位置應(yīng)該與它所屬的應(yīng)用的其它窗體放在一起。比如應(yīng)用A顯示于應(yīng)用B的后方。當(dāng)應(yīng)用A由于某個(gè)動(dòng)作打開一個(gè)新的窗體時(shí),新窗體應(yīng)該位于應(yīng)用A其它窗體的前面,可是不得覆蓋應(yīng)用B的窗體。僅僅依據(jù)主序進(jìn)行排序是無法實(shí)現(xiàn)這個(gè)管理邏輯的,還須要依賴Activity的順序。在WindowToken一節(jié)的解說中。以前簡單分析了mAppTokens列表的性質(zhì)。它所保存的AppWindowToken的順序與AMS中ActivityRecord的順序時(shí)刻保持一致。因此,AppWindowToken在mAppTokens的順序就是Activity的順序。

非應(yīng)用窗體的排序規(guī)則:依照主序進(jìn)行排序,主序高者排在前面,當(dāng)現(xiàn)有窗體的主序與新窗體同樣時(shí),新窗體位于現(xiàn)有窗體的前面。

應(yīng)用窗體的排序規(guī)則:如上所述,同一個(gè)應(yīng)用的窗體的顯示位置必須相鄰。

假設(shè)當(dāng)前應(yīng)用已有窗體在顯示(當(dāng)前應(yīng)用的窗體存儲(chǔ)在其WindowState.appWindowToken.windows中),新窗體將插入到其所屬應(yīng)用其它窗體的前面。可是保證STARTING_WINDOW永遠(yuǎn)位于最前方,BASE_APPLICATION永遠(yuǎn)位于最后方。假設(shè)新窗體是當(dāng)前應(yīng)用的第一個(gè)窗體。則參照其它應(yīng)用的窗體順序,將新窗體插入到位于前面的最后一個(gè)應(yīng)用的最后一個(gè)窗體的后方,或者位于后面的第一個(gè)應(yīng)用的最前一個(gè)窗體的前方。

假設(shè)當(dāng)前沒有其它應(yīng)用的窗體能夠參照。則直接依據(jù)主序?qū)⑿麓绑w插入到列表中。

窗體排序的總結(jié)例如以下:

·??子窗體依據(jù)子序相對于其父窗體進(jìn)行排序。同樣子序的窗體,正子序則越新越靠前,負(fù)子序則越新越靠后。

·??應(yīng)用窗體參照本應(yīng)用其它窗體或相鄰應(yīng)用的窗體進(jìn)行排序。

假設(shè)沒有不論什么窗體能夠參照。則依據(jù)主序進(jìn)行排序。

·??非應(yīng)用窗體依據(jù)主序進(jìn)行排序。

經(jīng)過addWindowToListInOrderLocked()函數(shù)的處理之后。當(dāng)前DisplayContent的窗體列表被插入了一個(gè)新的窗體。

然后等待assignLayersLocked()的進(jìn)一步處理。

2.assignLayersLocked分析

assignLayersLocked()函數(shù)將依據(jù)每一個(gè)窗體的主序以及它們在窗體列表中的位置又一次計(jì)算終于的顯示次序mLayer。

[-->WindowManagerService.java::WindowManagerService.assignLayersLocked()]

privatefinal void assignLayersLocked(WindowList windows) {

??? int N = windows.size();

??? int curBaseLayer = 0;

??? // curLayer表示當(dāng)前分配到的Layer序號(hào)

??? int curLayer = 0;

??? int i;

?

??? // 遍歷列表中的全部的窗體,逐個(gè)分配顯示次序

??? for (i=0; i<N; i++) {

??????? final WindowState w = windows.get(i);

??????? final WindowStateAnimator winAnimator =w.mWinAnimator;

??????? boolean layerChanged = false;

??????? int oldLayer = w.mLayer;

??????? if (w.mBaseLayer == curBaseLayer ||w.mIsImWindow

??????????????? || (i > 0 &&w.mIsWallpaper)) {

??????????? // 為具有同樣主序的窗體在curLayer上添加一個(gè)偏移量,并將curLayer作為終于的顯示次序

??????????? curLayer +=WINDOW_LAYER_MULTIPLIER;

??????????? w.mLayer = curLayer;

??????? } else {

??????????? // 此窗體擁有不同的主序,直接將主序作為其顯示次序并更新curLayer

??????????? curBaseLayer = curLayer =w.mBaseLayer;

??????????? w.mLayer = curLayer;

??????? }

??????? // 假設(shè)現(xiàn)實(shí)次序發(fā)生了變化則進(jìn)行標(biāo)記

??????? if (w.mLayer != oldLayer) {

??????????? layerChanged = true;

??????????? anyLayerChanged = true;

??????? }

??????? ......

??? }

??? ......

??? // 向當(dāng)前DisplayContent的監(jiān)聽者通知顯示次序的更新

??? if (anyLayerChanged) {

??????? scheduleNotifyWindowLayersChangedIfNeededLocked(

??????? getDefaultDisplayContentLocked());

??? }

}

assignLayersLocked()的工作原理比較繞。簡單來說,假設(shè)某個(gè)窗體在整個(gè)列表中擁有唯一的主序。則該主序就是其終于的顯示次序。假設(shè)若干個(gè)窗體擁有同樣的主序(注意經(jīng)過addWindowToListInOrderLocked()函數(shù)的處理后,擁有同樣主序的窗體都是相鄰的),則第i個(gè)同樣主序的窗體的顯示次序?yàn)樵谥餍虻幕A(chǔ)上添加i * WINDOW_LAYER_MULTIPLIER的偏移。

經(jīng)過assignLayersLocked()之后,一個(gè)擁有9個(gè)窗體的系統(tǒng)的現(xiàn)實(shí)次序的信息如表4-3所看到的。

表4- 3 窗體終于的顯示次序信息

?

窗體1

窗體2

窗體3

窗體4

窗體5

窗體6

窗體7

窗體8

窗體9

主序mBaseLayer

11000

11000

21000

21000

21000

21000

71000

71000

101000

子序mSubLayer

0

0

0

-1

0

0

0

0

0

顯示次序mLayer

11000

11005

21000

21005

21010

21015

71000

71005

101000

?

在確定了終于的顯示次序mLayer后。又計(jì)算了WindowStateAnimator還有一個(gè)屬性:mAnimLayer。

例如以下所看到的:

[-->WindowManagerService.java::assignLayersLocked()]

??? finalWindowStateAnimator winAnimator = w.mWinAnimator;

??? ......

? ??if (w.mTargetAppToken != null) {

??????? // 輸入目標(biāo)為Activity的輸入法窗體,其mTargetAppToken是其輸入目標(biāo)所屬的AppToken

???????winAnimator.mAnimLayer =

???????????????w.mLayer + w.mTargetAppToken.mAppAnimator.animLayerAdjustment;

??? } elseif (w.mAppToken != null) {

??????? // 屬于一個(gè)Activity的窗體

???????winAnimator.mAnimLayer =

???????????????w.mLayer + w.mAppToken.mAppAnimator.animLayerAdjustment;

??? } else {

???????winAnimator.mAnimLayer = w.mLayer;

??? }

?? ?......

對于絕大多數(shù)窗體而言,其相應(yīng)的WindowStateAnimator的mAnimLayer就是mLayer。而當(dāng)窗體附屬為一個(gè)Activity時(shí),mAnimLayer會(huì)加入一個(gè)來自AppWindowAnimator的矯正:animLayerAdjustment。

WindowStateAnimator和AppWindowAnimator是動(dòng)畫系統(tǒng)中的兩員大將,它們負(fù)責(zé)渲染窗體動(dòng)畫以及終于的Surface顯示次序的改動(dòng)。回想一下4.1.2中的WMS的組成結(jié)構(gòu)圖,WindowState屬于窗體管理體系的類。因此其所保存的mLayer的意義偏向于窗體管理。

WindowStateAnimator/AppWindowAnimator則是動(dòng)畫體系的類,其mAnimLayer的意義偏向于動(dòng)畫。而且由于動(dòng)畫系統(tǒng)維護(hù)著窗體的Surface,因此mAnimLayer是Surface的實(shí)際顯示次序

在沒有動(dòng)畫的情況下,mAnimLayer與mLayer是相等的。而當(dāng)窗體附屬為一個(gè)Activity時(shí),則會(huì)依據(jù)AppTokenAnimator的須要適當(dāng)?shù)靥砑右粋€(gè)矯正值。這個(gè)矯正值來自AppTokenAnimator所使用的Animation。當(dāng)Animation要求動(dòng)畫對象的ZOrder必須位于其它對象之上時(shí)(Animation.getZAdjustment()的返回值為Animation.ZORDER_TOP),這個(gè)矯正是一個(gè)正數(shù)WindowManagerService.TYPE_LAYER_OFFSET(1000),這個(gè)矯正值非常大,于是窗體在動(dòng)畫過程中會(huì)顯示在其它同主序的窗體之上。相反。假設(shè)要求ZOrder必須位于其它對象之下時(shí),矯正為-WindowManagerService.TYPE_LAYER_OFFSET(-1000)。于是窗體會(huì)顯示在其它同主序的窗體之下。在動(dòng)畫完結(jié)后,mAnimLayer會(huì)被又一次賦值為WindowState.mLayer。使得窗體回到其應(yīng)有的位置。

動(dòng)畫系統(tǒng)的工作原理將在4.5節(jié)具體探討。

注意矯正值為常數(shù)1000,也就出現(xiàn)一個(gè)隱藏的bug:當(dāng)同主序的窗體的數(shù)量大于200時(shí)。APPLICATION窗體的mLayer值可能超過22000。此時(shí),在對于mLayer值為21000的窗體應(yīng)用矯正后,仍然無法保證動(dòng)畫窗體位于同主序的窗體之上。只是超過200個(gè)應(yīng)用窗體的情況非常少見,而且僅在動(dòng)畫過程中才會(huì)出現(xiàn)bug,所以google貌似也懶得解決問題。

4.3.3 更新顯示次序到Surface

再回到WMS的addWindow()函數(shù)中。發(fā)現(xiàn)再?zèng)]有可能和顯示次序相關(guān)的代碼了。mAnimLayer是怎樣發(fā)揮自己的作用呢?不要著急,事實(shí)上。新建的窗體眼下尚無Surface。

回想一下SimpleWindow樣例,在運(yùn)行session.relayout()后,WMS才為新窗體分配了一塊Surface。也就是說,僅僅有運(yùn)行relayout()之后才會(huì)為新窗體的Surface設(shè)置新的顯示次序。

為了不中斷對顯示次序的調(diào)查進(jìn)展。就直接開門見山地告訴大家,設(shè)置顯示次序到Surface的代碼位于WindowStateAnimator. prepareSurfaceLocked()函數(shù)中,是通過Surface.setLayer()完畢的。

在4.5節(jié)會(huì)深入為大家揭開WMS動(dòng)畫子系統(tǒng)的面紗。

4.3.4 關(guān)于顯示次序的小結(jié)

這一節(jié)討論了窗體類型對窗體顯示次序的影響。

窗體依據(jù)自己的類型得出其主序及子序,然后addWindowToListInOrderLocked()依據(jù)主序、子序以及其所屬的Activity的順序。依照升序排列在DisplayContent的mWindows列表中。

然后assignLayersLocked()為mWindows中的全部窗體分配終于的顯示次序。之后,WMS的動(dòng)畫系統(tǒng)將終于的顯示次序通過Surface.setLayer()設(shè)置進(jìn)SurfaceFlinger。



[1]關(guān)于Choreographer。請參考鄧凡平的博客《Android Project Butter分析》(http://blog.csdn.net/innost/article/details/8272867)。

[2]讀者可閱讀《深入理解Android 卷I》第4章“深入理解Zygote”來了解和zygote相關(guān)的知識(shí)

[3]關(guān)于Wi-Fi Display的具體信息,請讀者參考http://blog.csdn.net/innost/article/details/8474683的介紹。

轉(zhuǎn)載于:https://www.cnblogs.com/wzjhoutai/p/6873790.html

創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)

總結(jié)

以上是生活随笔為你收集整理的《深入理解Android 卷III》第四章 深入理解WindowManagerService的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

中文字幕视频网站 | 国产高清在线a视频大全 | 91欧美国产 | 久久社区视频 | 亚洲狠狠丁香婷婷综合久久久 | 日日爽夜夜操 | 亚洲v精品 | 成人动漫一区二区三区 | 国色天香在线观看 | 草久久影院 | 日韩中文字幕国产 | 亚洲成人免费观看 | 亚洲在线成人精品 | 天天视频色版 | 中文字幕制服丝袜av久久 | 在线观看黄网站 | 国产精品一区免费观看 | 天天射狠狠干 | 欧美国产精品久久久久久免费 | 亚洲精品免费视频 | 国产一线二线三线在线观看 | 亚一亚二国产专区 | 999久久久久久久久6666 | 99久久婷婷国产一区二区三区 | 成人在线观看av | 久久免费精品视频 | 91自拍成人 | 欧美一区在线观看视频 | 91日韩在线视频 | 999久久久欧美日韩黑人 | se视频网址 | 九九视频免费观看视频精品 | 亚洲欧美视屏 | 中文字幕区 | 天天做天天看 | 久久婷婷网 | 在线韩国电影免费观影完整版 | 午夜精品久久久久 | 国产成人亚洲在线观看 | 伊人色综合久久天天网 | 日韩有码欧美 | 久久久久免费精品国产小说色大师 | 国产日韩av在线 | 久久久国产精品麻豆 | 成人av电影免费 | 久久久精品视频网站 | 日本一区二区三区免费看 | 美女视频黄,久久 | 国产黄免费在线观看 | 国产在线观看a | 久久精品美女视频 | 国产免费不卡 | 欧美激情第28页 | 日本最新高清不卡中文字幕 | 91看片麻豆 | 91大神视频网站 | 亚洲精品乱码久久 | 精品久久久久久久 | 狠狠色伊人亚洲综合网站野外 | 黄色在线免费观看网址 | av丝袜在线| 日日综合 | 天天操天天怕 | 欧美ⅹxxxxxx | 99热这里只有精品国产首页 | 日韩视| 久久99国产精品视频 | 国产精品免费一区二区三区在线观看 | 五月天婷亚洲天综合网鲁鲁鲁 | 免费高清在线观看成人 | 波多野结衣精品视频 | 美女视频黄在线观看 | 欧美作爱视频 | 久久区二区 | 国产综合精品久久 | 美女搞黄国产视频网站 | 成人在线免费看视频 | 在线视频1卡二卡三卡 | 狠狠色香婷婷久久亚洲精品 | 中文字幕在 | 丰满少妇在线观看 | 国产精品久久电影网 | 天天射射天天 | 九九国产精品视频 | 91av久久| 国产香蕉97碰碰久久人人 | 就要干b| 激情开心| 国产精久久久久久妇女av | 夜色资源网 | 国产精品夜夜夜一区二区三区尤 | 最近中文字幕mv | 国产精品男女啪啪 | 97精品伊人 | 成人a免费看 | 久久国产精品一国产精品 | 亚洲成年人在线播放 | 亚洲欧美va | 婷婷精品在线视频 | 免费福利视频网 | 天天操天天添天天吹 | 婷婷综合视频 | 亚洲精品白浆高清久久久久久 | 成人午夜电影在线播放 | 麻豆视频www | av一区在线播放 | 特级黄色片免费看 | 精品影院一区二区久久久 | 偷拍福利视频一区二区三区 | 国产精品久久在线观看 | 精品国产乱子伦一区二区 | 久草精品视频在线观看 | 久久日本视频 | 少妇bbb搡bbbb搡bbbb | 日韩特级毛片 | 国产精品成人久久久久 | 日韩精品一区二区三区中文字幕 | 久草视频在线免费 | 成人免费精品 | 亚洲精品国偷拍自产在线观看蜜桃 | 国产成本人视频在线观看 | 国产资源在线播放 | 亚洲精品视频偷拍 | 久久亚洲免费视频 | 热精品 | 色五月色开心色婷婷色丁香 | 超碰资源在线 | 国产在线观看av | 免费看三级黄色片 | 国产又粗又猛又爽又黄的视频先 | 在线观看91精品视频 | 久久久久伦理电影 | 亚州国产精品久久久 | 手机av在线网站 | 精品影院 | 九九精品视频在线观看 | 久久国产精品99久久久久久老狼 | 五月婷婷色丁香 | 欧美伦理一区二区 | 黄色亚洲片 | 国产精品色婷婷视频 | 国产福利精品视频 | 日韩r级电影在线观看 | 国产另类av | 日韩精品在线一区 | 欧美日韩在线免费观看视频 | 国产成人黄色片 | 黄色日本免费 | 国产免费高清视频 | 97超碰国产精品女人人人爽 | av高清一区二区三区 | 成人av在线播放网站 | 久久久国产精品亚洲一区 | 免费在线观看中文字幕 | 丝袜制服综合网 | 成人av高清在线 | 99久久精| 丝袜制服综合网 | 国产视频99| 成人在线观看网址 | 777奇米四色 | 人人看看人人 | 久久久久久久久久久久av | 久草免费在线视频 | 精品一区二区三区久久 | 午夜精品一区二区三区四区 | 亚洲视频精品在线 | 男女啪啪网站 | 国产高清综合 | 97在线视频免费观看 | 丁香激情五月 | 免费黄色一区 | 狠狠狠狠狠狠干 | 九九久久久久久久久激情 | 午夜丁香网 | 国产成人亚洲在线观看 | 日韩av免费一区二区 | 午夜精品久久久久久 | 久草视频在线新免费 | 国产精品久久久久久久久久久久久久 | 国产精品一区二区在线 | 一级免费黄视频 | 在线中文字母电影观看 | 久久一区二区三区四区 | 久草视频免费在线观看 | 天天拍天天操 | 国产成人三级 | 在线观看国产区 | 国内精品久久久精品电影院 | 97超碰人人在线 | 91麻豆精品国产91久久久久 | 久久成人国产精品入口 | 91精彩在线视频 | 亚洲精品视频在 | 国产69精品久久久久9999apgf | 亚洲播播 | 亚洲精品中文字幕在线 | 青春草视频 | 97av.com| 一区二区不卡高清 | 日韩中文幕 | 亚洲精品久久激情国产片 | 久久精品久久综合 | 久草综合在线观看 | 69久久99精品久久久久婷婷 | 99精品视频免费 | 中文字幕在线观看视频一区二区三区 | 成人免费视频视频在线观看 免费 | 亚洲国产欧洲综合997久久, | 黄色小视频在线观看免费 | 亚洲精品动漫成人3d无尽在线 | 亚洲综合国产精品 | 性色xxxxhd | 中文免费在线观看 | 色婷婷一| 麻豆91在线| 最近日本韩国中文字幕 | 久久99精品国产99久久6尤 | 国产精品剧情 | 草久久影院 | 色婷婷av国产精品 | 在线黄色毛片 | 午夜精品久久久久久久久久久久 | 久久成人国产精品免费软件 | 极品久久久久久久 | 在线观看视频黄色 | 国产精品乱码久久久 | 色欧美88888久久久久久影院 | 五月综合婷 | 国产手机av | 521色香蕉网站在线观看 | 天天色.com | 操老逼免费视频 | 国产精品欧美一区二区 | 欧美激情视频在线免费观看 | 欧美综合干| 欧美性生活大片 | 九九久久久久久久久激情 | 久久免费视频网 | 黄免费网站 | 欧美网址在线观看 | 久久久久久国产精品999 | 大型av综合网站 | 午夜精品久久久久久久久久久 | 在线看毛片网站 | 国产一区二区久久精品 | 久久久蜜桃一区二区 | 精品国产久 | 一区二区在线影院 | a级片久久久 | 欧美视频国产视频 | 黄色资源网站 | 五月丁色| 精品国产一区二区三区久久久蜜臀 | 91精品无人成人www | 久久精品香蕉视频 | 中文亚洲欧美日韩 | 香蕉精品视频在线观看 | 日韩成人av在线 | 午夜久久精品 | 日韩中文字幕免费视频 | 在线观看亚洲精品视频 | 日韩精品无码一区二区三区 | 久草视频在线观 | 色婷婷播放 | 夜夜骑日日操 | 午夜影院先 | 黄色成人免费电影 | av三级在线免费观看 | 中文字幕在线观看第一区 | 97在线超碰 | 综合激情av | 免费观看全黄做爰大片国产 | 偷拍福利视频一区二区三区 | 99热亚洲精品 | 成人中文字幕在线观看 | 精品亚洲成a人在线观看 | 欧美日韩午夜 | 久久99爱视频 | 日韩 在线a | 毛片www | 婷婷性综合 | 91麻豆精品国产91久久久使用方法 | 亚洲精色 | 网站免费黄 | 免费黄在线观看 | 在线国产小视频 | 啪啪免费视频网站 | 亚洲一级电影 | av在线永久免费观看 | 天天干天天在线 | 麻花天美星空视频 | 91理论片午午伦夜理片久久 | 国产亚洲精品精品精品 | 99综合视频 | 久久精品国产一区二区电影 | 国产精品一区欧美 | 亚洲国产福利视频 | 九七视频在线观看 | 日韩av在线网站 | 国产精品久久一区二区无卡 | 国产成人精品亚洲日本在线观看 | 成人免费在线视频观看 | 日韩欧美区 | 亚洲精品在线一区二区三区 | 欧美最猛性xxxxx亚洲精品 | 久久av影视| 四虎精品成人免费网站 | 亚洲免费av片 | 97色婷婷成人综合在线观看 | 久久草网| 国产小视频免费观看 | 五月天网站在线 | 韩国av电影网 | 中文字幕资源网 国产 | 99在线免费视频 | 日韩精品视频在线观看免费 | 天天操天天添 | 91av免费观看 | 日韩在线观看一区 | 久久国产一区二区 | av女优中文字幕在线观看 | 中文字幕在线视频一区 | 欧美激情视频三区 | 久久久久麻豆 | 天天玩天天干天天操 | 国产精品高潮在线观看 | 成人免费观看av | 最新国产福利 | 一区二区三区不卡在线 | 国产剧情av在线播放 | 成年人av在线播放 | 亚洲 欧美 日韩 综合 | 亚洲成人资源 | 天天激情 | 日韩在线免费 | 九九久久电影 | 天天操夜| 欧美最新另类人妖 | 综合婷婷丁香 | 亚洲精品一区二区三区新线路 | 久久亚洲影视 | 精品中文字幕在线观看 | 国产二区视频在线观看 | 亚洲午夜精品一区二区三区电影院 | 免费视频久久久久久久 | 91成人在线观看喷潮 | 在线免费视频一区 | 亚洲精品乱码久久久久久写真 | 国产一区免费 | 色婷在线| 天天操天天干天天 | 韩日电影在线免费看 | 久久99精品国产99久久6尤 | 在线免费观看不卡av | 成人影片在线免费观看 | 亚洲婷婷综合色高清在线 | 亚洲成人动漫在线观看 | 国产精品99久久99久久久二8 | 中文在线8新资源库 | 国产精品99精品久久免费 | 天天插天天色 | 国产精品国产亚洲精品看不卡 | 在线看日韩av | 亚洲国产精品999 | 久久影院一区 | 国产成视频在线观看 | 欧美黄网站| 在线精品视频在线观看高清 | 中文字幕在线看视频国产中文版 | 青青草国产精品视频 | 国产永久网站 | 午夜成人免费影院 | 色的网站在线观看 | 亚洲欧美日韩精品一区二区 | 九九综合九九 | 在线看中文字幕 | 婷婷丁香自拍 | 日本女人的性生活视频 | 中文字幕91 | 一区二区三区高清 | 成人免费大片黄在线播放 | 国产精品九九热 | 视频一区二区免费 | 在线色亚洲 | 黄色免费网站 | 国产玖玖视频 | 在线观看免费黄视频 | 国产高清小视频 | av黄色免费在线观看 | 狠狠狠狠干 | 玖玖精品在线 | 狠狠干夜夜操天天爽 | 久久久久久久久毛片 | 日韩视频中文字幕在线观看 | 国产 一区二区三区 在线 | 精品久久九九 | 色综合久久中文字幕综合网 | 欧美黑人巨大xxxxx | 天天操天天拍 | 91av在| 国产91九色视频 | 久亚洲| 亚洲精品免费在线 | 久久久久女人精品毛片九一 | 国产一区影院 | 日日日日日 | 伊人国产女 | 一级片黄色片网站 | 免费视频一级片 | 免费av网址在线观看 | 亚洲成人精品在线 | 日韩在线视频线视频免费网站 | 国产亚洲亚洲 | 欧美国产日韩一区二区 | 亚洲精品色婷婷 | 毛片888| 午夜视频在线观看一区二区三区 | 1024手机基地在线观看 | 中文字幕在线看片 | 欧美性色xo影院 | 久久人视频| 亚洲国内精品 | 久久久久成 | 超碰av在线 | 精品美女久久久久久免费 | 久久成人精品电影 | 三级黄色在线 | 一色屋精品视频在线观看 | 美女视频网站久久 | 久久久久久久久久久久久久电影 | 国产99久久九九精品免费 | 日韩视频在线不卡 | 欧洲精品亚洲精品 | 欧美激情第28页 | 久草久草在线观看 | 日本成址在线观看 | 精品久久1| 婷婷五月情| 中文字幕在线视频免费播放 | 国产69久久精品成人看 | 在线看成人片 | 国产99久久 | 日本久久久久久久久久 | 久久久久久蜜桃一区二区 | 在线 视频 亚洲 | 婷婷干五月 | www.夜夜骑.com | 欧美精品久久久久性色 | 色婷婷激情电影 | 91网页版在线观看 | 99re亚洲国产精品 | 国产美女在线免费观看 | 日本一区二区免费在线观看 | 狠狠干激情| 国产精品成人一区二区三区吃奶 | 91亚洲精品久久久蜜桃网站 | 国产成人精品区 | 国产一区播放 | 成人动漫一区二区 | 久久久久久久久综合 | 国产成人在线播放 | 日韩伦理片一区二区三区 | 韩国av免费观看 | 91成年人网站 | 久久亚洲欧美 | 国产婷婷 | 成人免费视频网址 | 伊人日日干 | 国产成人精品一区二 | 国产成人免费观看久久久 | 超碰97在线人人 | 色播五月激情综合网 | 伊人狠狠干 | 亚洲码国产日韩欧美高潮在线播放 | 91成人在线观看喷潮 | 毛片美女网站 | 江苏妇搡bbbb搡bbbb | 中文字幕一区二区三区在线视频 | 久久国产精品99久久久久久进口 | 国产精品 视频 | 91色蜜桃 | 激情五月亚洲 | 麻豆视频在线免费看 | 色94色欧美 | 久久艹精品 | 日韩在线观看电影 | 亚洲精品美女视频 | 成片免费| 99国产精品一区二区 | 91精品天码美女少妇 | 在线最新av | 91资源在线 | 午夜视频在线瓜伦 | 国产中文字幕一区 | 处女av在线| 国产在线视频导航 | 九九激情视频 | 欧美日韩国产综合一区二区 | 色婷婷av一区二 | 成人av电影免费 | 国产精品免费看久久久8精臀av | www.xxxx欧美 | 91av视频观看 | 成人av资源网站 | 中文字幕在线观看1 | 国产福利小视频在线 | 中文字幕91 | 日日干天夜夜 | 久草网视频在线观看 | 又爽又黄在线观看 | 欧美一级片 | 国产天天爽 | 精品国产aⅴ麻豆 | 99亚洲视频 | 狠狠躁夜夜躁人人爽视频 | 久草国产在线观看 | 国产无吗一区二区三区在线欢 | 国产在线观看网站 | 国产欧美在线一区二区三区 | 黄视频色网站 | 中文字幕色播 | 500部大龄熟乱视频使用方法 | 国产短视频在线播放 | 天堂久久电影网 | 精品国产自 | 手机成人在线电影 | 国产亚洲在线视频 | 日韩精品一区二区三区在线播放 | 五月婷婷综合网 | 91黄色影视 | 97超碰国产精品女人人人爽 | 国产免费一区二区三区网站免费 | 亚洲精品乱码白浆高清久久久久久 | 久久最新 | 欧美资源 | 国产亚洲成av人片在线观看桃 | 日韩在线视频看看 | 91精品视频在线观看免费 | 国产精品美女久久 | 久久av一区二区三区亚洲 | 国产精品久久久久久久久久白浆 | 人人插超碰 | 精品视频成人 | 色视频成人在线观看免 | 日韩三级在线 | 伊人婷婷 | 亚洲成人黄色 | 午夜精品剧场 | 久久免费成人网 | 精产嫩模国品一二三区 | 久久综合久久八八 | 人人添人人澡 | 97超碰色 | 亚洲精品视频在 | 欧美精品九九99久久 | 国产无套视频 | 中文字幕麻豆 | 伊人久久影视 | 日本最大色倩网站www | 又爽又黄在线观看 | 国产精品久久久久aaaa九色 | 人人爽久久久噜噜噜电影 | www178ccom视频在线 | 日韩3区| 国产精品中文字幕在线观看 | av在线亚洲天堂 | 91香蕉视频在线 | 国产成人精品一区二区在线观看 | 国产精品国产自产拍高清av | 中国精品一区二区 | 精品国产欧美一区二区三区不卡 | 日本高清免费中文字幕 | 亚洲视频在线看 | 999视频在线播放 | 欧美另类v | 国产精品久久久久久麻豆一区 | 国产精品国产三级国产不产一地 | 色综合久久精品 | 亚洲日本在线视频观看 | 夜色成人av | 成人av在线网 | 四虎在线观看精品视频 | 欧美激情第28页 | 在线网址你懂得 | 中文字幕av专区 | 久久久福利影院 | 日韩小视频 | 婷婷色综合色 | 免费av小说 | 欧美精品国产综合久久 | 日韩在观看线 | 天堂av中文字幕 | 最新一区二区三区 | 国产成人一区二区三区久久精品 | 午夜精品99久久免费 | 99热在线观看免费 | 欧美a级在线免费观看 | 国产中文字幕视频在线观看 | 欧美成人猛片 | 人人爽夜夜爽 | 色婷婷免费 | 超碰精品在线观看 | 欧美永久视频 | 六月天综合网 | 国内99视频| 91精品久久久久久久久久入口 | av在线免费在线 | 韩日电影在线 | 69国产盗摄一区二区三区五区 | 欧美激情视频三区 | 国产69久久 | 很黄很污的视频网站 | 久久精品视频在线免费观看 | 精品视频9999 | 婷婷六月天丁香 | 国产精品久久久久久久久久久不卡 | 在线免费中文字幕 | 一区二区三区av在线 | 久久高清av | 日韩精品久久久免费观看夜色 | 操碰av| 91久久偷偷做嫩草影院 | 99久久er热在这里只有精品66 | 在线观看日韩免费视频 | 波多野结衣日韩 | 欧美性护士| 国产在线观看地址 | 在线观看av免费 | 亚洲资源一区 | 久久国产精品色av免费看 | 干干日日 | 黄色大片免费网站 | 久久久久久国产精品999 | 国产精品自产拍在线观看蜜 | 久久av电影 | 97色噜噜 | 国产视频日韩视频欧美视频 | 午夜av不卡 | 国产在线超碰 | 国产高清一级 | 欧美日韩视频免费看 | 久久久久免费精品国产小说色大师 | 久久久久久久久亚洲精品 | 永久中文字幕 | 91精品国自产在线观看 | 免费99精品国产自在在线 | av黄免费看 | 国产亚洲激情视频在线 | 中文字幕一区二区三区四区 | 中文字幕在线观看的网站 | 国产福利a | 视频 天天草 | 国产午夜精品福利视频 | 日本久久视频 | 不卡av电影在线 | 免费在线观看av网站 | 国产一区二区三精品久久久无广告 | 欧洲性视频 | 五月婷婷六月综合 | 免费福利片2019潦草影视午夜 | 日韩a级免费视频 | 久久国产电影院 | 国产精品久久久久久一二三四五 | 久久精品最新 | 久久久午夜精品福利内容 | 欧美9999| 99久久999久久久精玫瑰 | 亚洲综合精品视频 | 中文字幕色综合网 | 97碰在线视频 | 500部大龄熟乱视频使用方法 | 欧美日本一区 | www.色午夜.com| 亚洲精品视频免费 | 美女黄频在线观看 | 2023av在线 | 在线观看 亚洲 | 免费高清在线观看成人 | 日韩网站一区 | www.久久久.cum | 夜色.com| 97色se | 国产精品美女久久久久久久 | 日韩二区三区在线 | 久草视频精品 | 精油按摩av | 亚洲成aⅴ人片久久青草影院 | 在线 高清 中文字幕 | 国产日韩视频在线 | 在线亚洲成人 | 国产一级二级视频 | 中文字幕日本电影 | 91在线精品视频 | 探花视频免费观看高清视频 | 国产精品毛片一区视频播 | 五月婷婷在线视频观看 | 亚洲精品小视频在线观看 | 亚洲春色成人 | 爱干视频 | 日韩理论电影在线观看 | 在线 影视 一区 | 国产黄色大全 | 超碰资源在线 | 91一区二区在线 | 黄污视频大全 | 美女视频一区二区 | 亚洲成av人片在线观看www | 蜜臀一区二区三区精品免费视频 | 亚洲专区 国产精品 | 在线免费观看亚洲视频 | 国产高清成人av | 中文字幕第一页在线播放 | 国产中年夫妇高潮精品视频 | 亚洲黄色大片 | 久久久久久网站 | 狠狠色综合网站久久久久久久 | 国产一区二区三区久久久 | 久久久久久久久久久福利 | 在线小视频你懂得 | 日韩手机视频 | 亚洲视频分类 | 一区二区中文字幕在线播放 | 中文一区在线 | 一区二区三区高清不卡 | 中文字幕精品一区二区精品 | 亚洲欧美偷拍另类 | www.激情五月.com | 在线国产不卡 | 亚洲国产无 | 狠狠狠色丁香综合久久天下网 | 国产婷婷视频在线 | 色婷久久 | 日日干天天爽 | 91精品国产九九九久久久亚洲 | 五月天综合网站 | 婷婷久久一区 | 成人97视频一区二区 | av在线一级 | 国产丝袜制服在线 | 亚洲综合色婷婷 | 欧美性黑人 | 成人av影院在线观看 | 四虎免费在线观看视频 | 国产成人免费观看 | 欧美日韩三级在线观看 | 西西www444 | 丁香花五月 | 五月天久久激情 | 久草香蕉在线视频 | 日韩爱爱网站 | 国产精品激情偷乱一区二区∴ | 免费观看午夜视频 | 四虎在线永久免费观看 | 9ⅰ精品久久久久久久久中文字幕 | 中文字幕精品一区二区精品 | 日韩精品三区四区 | 久久影视网 | 国产精品五月天 | 2019中文字幕第一页 | 美女网站视频免费都是黄 | 免费碰碰 | 中文字幕电影在线 | 人人添人人澡人人澡人人人爽 | 中文字幕第一 | 日本久久不卡视频 | 韩国av一区二区三区在线观看 | 亚洲成人黄色网址 | 亚洲一区二区三区精品在线观看 | 97视频在线免费播放 | 中文字幕欧美日韩va免费视频 | 亚洲精品高清在线 | 在线观看蜜桃视频 | 亚洲精品乱码久久久久 | 黄色网免费 | 精品999| 免费人成在线观看网站 | 黄色免费网站下载 | 欧美日韩免费网站 | 人人爽网站| 一区二区三区四区精品视频 | avwww在线| 999久久a精品合区久久久 | 久久久亚洲国产精品麻豆综合天堂 | 一级免费看视频 | 丁香六月综合网 | 精品亚洲网 | 伊人色综合网 | 日日麻批40分钟视频免费观看 | 在线中文字幕观看 | 天天操综 | 韩国av一区二区 | 日韩欧美一区二区三区黑寡妇 | 欧美亚洲精品在线观看 | 亚洲va综合va国产va中文 | 99久久99热这里只有精品 | 日日爱网址| 中文字幕首页 | 欧美精品在线一区 | 欧美激情视频一区二区三区免费 | 婷婷 中文字幕 | 欧美另类成人 | 岛国片在线 | 精品少妇一区二区三区在线 | 热re99久久精品国产66热 | 丁香婷婷激情国产高清秒播 | 人人爽人人搞 | 99久久精品无码一区二区毛片 | 在线黄网站| 国产精品成人av在线 | 免费高清在线视频一区· | 欧美精品中文在线免费观看 | 久久福利综合 | 麻豆传媒在线视频 | 成人手机在线视频 | 亚洲精选视频在线 | 天天天插| 免费看污污视频的网站 | 色黄久久久久久 | 欧美成人高清 | 五月天激情综合网 | 人人玩人人添人人澡超碰 | 久操中文字幕在线观看 | 国产成人免费网站 | 亚洲天天看 | 欧美日韩高清在线观看 | 亚洲欧美日韩一二三区 | 四虎欧美 | 狠狠躁日日躁狂躁夜夜躁 | 97色综合 | 成人av免费在线看 | 欧美精品一区二区三区一线天视频 | 国产亚洲字幕 | 亚洲成人av在线 | 亚洲欧美日韩不卡 | 在线中文字幕av观看 | 麻豆精品视频在线观看免费 | 又黄又刺激又爽的视频 | 黄色a一级视频 | 香蕉久久国产 | 国产午夜精品在线 | 成人久久综合 | 婷婷五天天在线视频 | 日本女人的性生活视频 | 91在线中文 | 波多野结衣电影久久 | 国产成视频在线观看 | 日本黄色a级大片 | 亚洲aⅴ乱码精品成人区 | 在线91视频 | 国产精品午夜久久久久久99热 | 国产在线久久久 | 午夜精品导航 | 在线视频欧美日韩 | av888av.com | 国产高清视频免费最新在线 | 日韩欧美视频免费看 | 91精品在线观看入口 | 免费性网站| 久草免费在线 | 九九视频一区 | 天天操天天是 | 欧美精品午夜 | 日日爽日日操 | 天天操夜夜操夜夜操 | www.香蕉视频在线观看 | 成人av高清| 天天色播 | 五月婷婷综合网 | 国产不卡视频在线 | 久久久网址 | 91探花系列在线播放 | 午夜av色| 成人h在线观看 | 欧美激情视频一二区 | 久久成人麻豆午夜电影 | 日韩久久影院 | 欧美一级欧美一级 | 日韩av电影中文字幕在线观看 | 婷婷国产在线 | 在线一二三四区 | 黄污网站在线观看 | 国产一级片一区二区三区 | 国产福利一区在线观看 | 国产精品免费小视频 | 久久影院中文字幕 | 久久躁日日躁aaaaxxxx | 又湿又紧又大又爽a视频国产 | 美女露久久 | 综合网久久 | 美女网站免费福利视频 | 久草视频在线资源 | 欧美日韩精品在线观看视频 | 狠狠干五月天 | 玖玖玖国产精品 | 精品国产一区二区三区久久影院 | 91av99| 在线观看免费黄色 | 日操操 | 国产精品一区在线观看你懂的 | 97av影院| 国产一级免费观看 | 在线观看网站你懂的 | 国产亚洲视频中文字幕视频 | 国产在线观看免费观看 | 欧美国产精品一区二区 | 在线一区电影 | 夜添久久精品亚洲国产精品 | 欧美综合在线观看 | 色综合久久久久久中文网 | 天堂在线视频免费观看 | 天堂久色 | 六月丁香婷婷网 | 欧美在线观看小视频 | 欧美一区二区三区在线 | 国产一区二区在线免费 | 二区在线播放 | 国产剧情在线一区 | 天天插狠狠干 | 欧美视频在线观看免费网址 | 黄色av三级在线 | 丁香五月亚洲综合在线 | 国产精品视频免费观看 | 91精品国产综合久久婷婷香蕉 | 久草a在线| 国产婷婷视频在线 | 麻豆久久一区二区 | 91.dizhi永久地址最新 | 免费av在线播放 | 草久电影 | 91久久奴性调教 | 久久蜜桃av | 99久久精品无免国产免费 | 激情影院在线观看 | 久久伊人精品天天 | 久久成人免费 | 91免费在线看片 | 久青草电影 | 天天草天天 | 一区三区在线欧 | 91在线超碰 | 国产精品久久久久久a | 五月婷婷六月丁香激情 | 久久久久久久久久影视 | 日本中文字幕在线电影 | www.久草.com| 国产资源免费 | 成人国产一区二区 | 在线观看av黄色 | 精品自拍网 | 国内精品中文字幕 | 久久国产精品99精国产 | 在线免费观看国产视频 | 国产视频观看 | 精品久久一区 | 一区二区视频在线观看免费 | 99热精品免费观看 | 久久在线一区 | 国产系列 在线观看 | 久久精品中文字幕免费mv | 黄色一集片 | 亚洲成人精品在线 | 97色在线视频 | 五月黄色 | 在线国产专区 | 亚洲综合欧美精品电影 | 国产精品亚洲成人 | 人人澡av| 精品国产精品久久一区免费式 | 欧美日韩中字 | 激情在线网站 | 日韩免费 | 中文字幕第一页av | 精品国产精品国产偷麻豆 | 亚洲精品五月 | 97免费视频在线播放 | 菠萝菠萝在线精品视频 | 日韩女同一区二区三区在线观看 | 粉嫩一区二区三区粉嫩91 | 日韩亚洲精品电影 | 国产精品久久一卡二卡 | 国产日韩精品欧美 | 99麻豆久久久国产精品免费 | 国产亚洲精品电影 | 久久艹国产视频 | www黄色 | 久精品视频在线观看 | 波多野结衣电影一区二区三区 | 三级av免费看 | 久久久久久久久久久电影 | 亚洲激情在线观看 | 国产午夜精品av一区二区 | 中文字幕中文字幕在线中文字幕三区 | 超碰97中文| 一区二区三区日韩在线 | 精品亚洲男同gayvideo网站 | 99视频网址| 欧美日韩精品免费观看 |