android進階(八)-----Android中的window和windowManager

一、Window和WindowManager

1、爲了分析Window的工作機制,我們先了解一下如何使用WindowManager添加一個Window。

示例代碼:

Button btn = new Button(this);

btn.setText("button");

LayoutParams lp = new WindowManager.LayoutParams(LayoutParams.WRAP_CONTENT,

LayoutParams.WRAP_CONTENT,0,0,PixelFromat.TRANSPARENT);

lp.flags = LayoutParams.FLAG_NOT_TOUCH_MODAL | LayoutParams.FLAG_NOT_FOCUSABLE | LayoutParams.FLAG_SHOW_WHEN_LOCKED;

lp.gravity = Gravity.LEFT | Gravity.TOP;

lp.x= 100;

lp.y=300;

windowManager.addView(btn,lp);

2、Flags參數表示Window的屬性,常用的如下:

FLAG_NOT_FOCUSABLE:表示window不需要獲取焦點,也不需要接受各種輸入事件,此標記會同時啓用FLAG_NOT_TOUCH_MODAL,最終事件會直接傳遞給下層的具有焦點的window

FLAG_NOT_TOUCH_MODAL:系統會將當前window區域以外的單擊事件傳遞給底層window,當前window區域內的單擊事件則自己處理

FLAG_SHOW_WHEN_LOCKED:開啓此模式可以讓window顯示在鎖屏的界面上。

3、Type參數表示window類型,window有三種類型,分別應用window、子window、和系統window。

window是分層的,每個window都有對應的z-ordered,層級大的會覆蓋在層級小的上面。應用window的層級範圍是1-99,子window的層級範圍是1000-1999,系統window的層級範圍是2000-2999,這些層級對應這WindowManager.LayoutParams的type參數.如果想讓Window位於所有window的最上層,採用較大的層級即可。

WindowManager所提供的功能很簡單,常用的方法只有三個:

添加View、更新View、刪除View,這三個方法定義在ViewManager中,而WindowManager繼承了ViewManager。

 

二、window的內部機制

window是一個抽象的概念,每一個window都對應着一個View和一個ViewRootImpl,Window和View通過ViewRootImpl建立聯繫,因此Window並不是實際存在的,它是以View的形式存在

1、window的添加過程

window的添加過程需要通過WindowManager的addView來實現,WindowManager是一個接口,他的真正實現是WindowManagerImpl類。在WindowManagerImpl中Window的三大操作是實現如下

@Override

public void addView(View view,ViewGroup.LayoutParams params){

mGlobal.addView(view,params,mDisplay,mParentWindow);

}

@Override

public void updateViewLayout(View view,ViewGroup.LayoutParams params){

mGlobal.updateViewLayout(view,params);

}

@Override

public void removeView(View view){

mGlobal.removeView(view,false);

}

可以發現,WindowManagerImpl並沒有直接實現Window,而是全部交給WindowManagerGlobal來處理,WindowManagerGlobal以工廠形式向外提供自己的實例。

在WindowManagerGlobal中有一段代碼:

private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance()。

WindowManagerImpl這種工作模式是典型的橋接模式,將所有的操作全部委託給WindowManagerGlobal實現。WindowManagerGlobal的addView方法主要分爲如下幾步:

(1)檢查參數是否合法,如果是子Window那麼還需要調整一些佈局參數

if(view == null){

throw new IllegalArgumentException("view must not be null")

}

if(display == null){

throw new IllegalArgumentException("display must not be null");

}

if(!(params instanceof WindowManager.LayoutParams)){

throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");

}

final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;

if(parentWindow != null){

parentWindow.adjustLayoutParamsForSubWindow(wparams);

}

(2)創建ViewRootImpl並將View添加到列表中

private final ArrayList<View> mViews = new ArrayList<View>();

private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();

private final ArrayList<WindowManager.LayoutParams> mParams = new ArrayList<WindowManager.LayoutParams>();

private final ArraySet<View> mDyingViews = new ArraySet<VIew>();



root = new ViewRootImpl(view.getContext(),display);

view.setLayoutParams(wparams);

mViews.add(view);

mRoots.add(root);

mParams.add(wparams);

(3)通過ViewRootImpl來更新界面並完成Window的添加過程,View的繪製過程是由ViewRootImpl來完成的,在setView內部會通過requestLayout來完成異步刷新請求。scheduleTraversals實際是View繪製的入口:

public void requestLayout(){

if(!mHandlinglayoutInLayoutRequest){

checkThread();

mLayoutRequested = true;

scheduleTraversals();

}

}

2、Window的刪除過程

Window的刪除過程和添加過程一樣,都是先通過WindowManagerImpl,在進一步通過WindowManagerGlobal來實現。

public void removeView(View view,boolean immediate){

if(view == null){

throw new IllegalArgumentException("view must not be null")

}

synchronized(mLock){

int index = findViewLocked(view,true);

View curView = mRoots.get(index).getView();

removeViewLocked(index,immediate);

if(curView == view){

return;

}

throw new IllegalArgumentException("Calling with view"+view+"but the ViewAncestor is attached to"+curView);

}

}

public void removeViewLocked(int index,boolean immediate){

ViewRootImpl root = mRoots.get(index);

View view = root.getView();

if(view != null){

InputMethodManager imm = InputMethodManager.getInstance();

if(imm != null){

imm.windowDismissed(mViews.get(index).getWindowToken());

}

}

boolean deferred = root.die(immediate);

if(view != null){

view.assignParent(null);

if(deferred){

mDyingViews.add(view);

}

}

}

removeViewLocked通過ViewRootImpl來完成刪除操作。windowManager提供了兩種刪除接口removeView和removeViewImmediate,分別表示異步刪除和同步刪除

boolean die(boolean immediate){

if(immediate && !mIsInTraversal){

doDie();

return false;

}

if(!mIsDrawing){

destroyHasdwareRenderer();

}

mHandler.sendEmptyMessage(MSG_DIE);

return true;

}

3、Window的更新過程

Window的更新過程要看WindowManagerGlobal的updateViewLayout方法。

代碼如下:

public void updateViewLayout(View view,ViewGroup.LayoutParams params){

if(view == null){

throw new IllegalArgumentException("view must not be null");

}

if(!(params instanceof WindowManager.LayoutParams)){

throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");

}

final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;

view.setLayoutParams(wparams);

synchronized(mLock){

int index = findViewLocked(view,true);

ViewRootImpl root = mRoots.get(index);

mParams.remove(index);

mParams.add(index,wparams);

root.setLayoutParams(wparams,false);

}

}

 

三:Window的創建過程

1、Activity的Window創建過程

activity啓動過程最終由ActivityThread中的performLaunchActivity()來完成整個啓動過程,在這個方法內部會通過類加載器創建Activity的實例對象,並調用attach方法爲其關聯運行過程中所依賴的一系列上下文環境變量。

在activity的attach方法中,系統會創建Activity所屬的Window對象併爲其設置回調接口,Window對象的創建是通過PolicyManager的makeNewWindow方法實現的。由於Activity實現了Window的Callback接口,因此當Window接收到外界的狀態改變時就會回調Activity的方法。

Activity的視圖附屬在window流程:

Activity將具體實現交給Window處理,而Window的具體實現是PhoneWindow,PhoneWindow的setContentView方法大致遵循以下幾個步驟:

(1)如果沒有DecorView,就創建他

DecorView是FrameLayout,是activity中頂級View,一般內部包含標題欄和內容欄

(2)將View添加到DecorView的mContentParent中

(3)回調Activity的onContentChanged方法通知Activity視圖已經發生改變

 

2、Dialog的Window創建過程

Dialog的Window的創建過程和Activity類似。有以下幾個步驟:

(1)創建Window

Dialog中Window的創建同樣是通過PolicyManager的makeNewWindow方法來完成的

(2)初始化DecorView並將Dialog的視圖添加到DecorView中

(3)將DecorView添加到Window中並顯示

 

3、Toast的Window創建過程

Toast和Dialog不同,他的工作過程比較複雜。Toast也是基於Window來實現的,但是Toast具有定時取消的功能,所以系統採用了Handler。在Toast的內部有兩類IPC過程,第一類是Toast訪問NotificationManagerService,第二類是NotificationManagerService回調Toast裏的TN接口。

Toast屬於系統Window,它內部的視圖由兩種方式指定,一種是系統默認的樣式,另一種是通過setView方法來指定一個自定義View。Toast提供了show和cancel分別用於顯示和隱藏Toast。

代碼如下:

public void show(){

if(mNextView == null){

throw new RuntimeException("setView must have been called");

}

INotificationManager service = getService();

String pkg = mContext.getOpPackageName();

TN tn = mTN;

tn.mNextView = mNextView;

try{

service.enqueueToast(pkg,tn,mDuration);

}catch(RemoteException e){

}

}

public void cancel(){

mTN.hide();

try{

getService().cancelToast(mContext.getPackageName(),mTN);

}catch(RemoteException e){

}

}

顯示和隱藏Toast都需要通過NMS來實現,由於NMS運行在系統的進程中,所以只能通過遠程調用的方式來顯示和隱藏Toast。需要注意的是TN這個類是一個Binder類,在Toast和NMS進行IPC的過程中,當NMS處理Toast的顯示或隱藏請求時會跨進程回調TN中的方法,這個時候由於TN運行在Binder線程池中,所以需要通過Handler將其切換到當前線程中

 

發佈了68 篇原創文章 · 獲贊 17 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章