配色: 字号:
android界面框架-Window
2017-01-19 | 阅:  转:  |  分享 
  
android界面框架-Window



从纯sdk及framwork的角度看,android中界面框架相关的类型有:Window,WindowManager,View等。下面就以这几个类为出发点来概览下安卓开发的“界面架构”。



Window

该类接触不多,和它密切相关的View类就比较熟悉了。



Window和View的关系

View是可视界面上的一个矩形区域,它显示内容并接收各种交互事件。所有View形成一个ViewTree这样的结构,对应任何一个界面通过sdk自带的hierarchyviewer工具就可以看到所有View对象形成的视图树的形象的结构图,相信都不会陌生。



一般的,开发工作主要是利用系统及自定义控件组合完成各种界面,所以理解View的使用和原理更重要些。再进一步,以ViewTree为整体,再看它和window,系统服务之间的关系可以从整体上把握android中界面框架。



Window类的描述如下:



Window:Abstractbaseclassforatop-levelwindowlookandbehaviorpolicy.Aninstanceofthisclassshouldbeusedasthetop-levelviewaddedtothewindowmanager.ItprovidesstandardUIpoliciessuchasabackground,titlearea,defaultkeyprocessing,etc.



Window表示“窗口”的概念,类似桌面OS中的窗口的概念,它是对用户界面的一个逻辑划分。可以参考下Windows编程中对Window类的描述是:“Thepointofinteractionbetweenauserandastandaloneapplicationisawindow.”。每个窗口对应一个独立的交互(可以是完整屏幕大小的)界面。

可以认为窗口是系统区分不同界面(不同app,或者同一app的不同Activity等)的一个单位。窗口之间可以包含(容器和子窗口),可以重叠(窗口具有z轴深度)。

窗口本身没有显示内容的能力,它包含一个顶级的View对象来持有一棵ViewTree。

一句话概况:窗口是一个独立的可交互界面,不同窗口叠加显示,窗口包含View来显示内容。



android中的UI就是View组成的ViewTree来表达的,rootview或者说顶部(toplevel)的View对象作为对整个ViewTree执行消息传递,测量,布局和绘制等遍历操作的全局入口,持有此rootview就相当于持有对应的组成界面内容的ViewTree。



有一点就是,Window是一个框架层的概念,整个android中的“各种界面”是不同类型的Window对象。但是在应用层,我们创建不同的界面就是提供不同的“内容”View对象,然后指定其Window类型,而界面的创建,更新和关闭是通过操纵Window所包含的要显示的顶级View,而不会直接去操控Window对象。



创建Window

在一个新窗口显示一个View最简单的过程如下:



privatevoidopenNewWindow(){

Buttonbutton=newButton(this);

button.setText("ButtonOnNewWindow");

WindowManager.LayoutParamsparams

=newWindowManager.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,

ViewGroup.LayoutParams.WRAP_CONTENT);

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

params.x=220;

params.y=320;

params.type=WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;

params.flags=WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;

WindowManagerwindowManager=(WindowManager)getSystemService(WINDOW_SERVICE);

windowManager.addView(button,params);

}

创建Window是通过WindowManager实现的,addView(Viewview,WindowManager.LayoutParamsparams)方法接收要展示的rootview和窗口布局相关的参数。

执行上面的代码会创建一个新的窗口,并在屏幕坐标(220,320)的位置放置了一个Button。应用层要做的就是准备好Button对象,然后设置好相关布局参数,而Window对象的创建本身最终是通过系统服务完成的。类似Activity那样,Window对象的创建不是new出来的。



窗口的创建,更新和关闭操作都是WindowManager的工作。实际开发中很少和Window直接打交到。比如Activity对应一个窗口,但是对它的界面的各种操作好像都和Window无关,因为Window的确够“底层”了。



窗口类型

前面说过,安卓中的界面划分为一个个窗口,系统运行中各个不同的窗口可以叠加显示。和叠加相关的属性就是Z-ordered,它是正整数。通过Z-ordered值,系统来决定这些Window的覆盖顺序。但实际上并不是通过指定Z-ordered值来直接控制窗口的层叠,而是,系统提供了一组常量,被表示为窗口类型,不同窗口类型的常量对应一个Z-ordered的值范围,然后其它地方通过为Window指定type来间接控制其Z-ordered。Window类有一个setType的方法正是做这件事情的。不过在创建Window时就需要为其指定type,而我们不直接接触Window对象,WindowWindowManager.LayoutParam有一个type的变量,在通过WindowWindowManager.addView方法创建窗口时,窗口布局参数中指定需要的type——窗口类型——这是必须的,而且Windowtypecannotbechangedafterthewindowisadded。



窗口类型有:



系统窗口:如状态栏,Toast那样的,常量如TYPE_SYSTEM_xx的。

应用窗口:就是Activtiy。

子窗口:Dialog这样的,需要依附(attach)到其它窗口(作为子窗口的container,如Activity)。

WindowManager.LayoutParams有个flags的属性,用来控制窗口的可见性,透明,是否可以获得焦点等显示和交互的有关状态。



Window相关属性和方法

接下来直面Window类。它是一个抽象类,目前只有PhoneWindow是其实现类。可以通过下面截取的类型定义的代码片段对Window有个感官认识:



publicabstractclassWindow{

//一系列的FEATURE_xx常量,还记得在Activity中requestWindowFeature方法吗?

publicstaticfinalintFEATURE_xxx



/

APIfromaWindowbacktoitscaller.

Thisallowstheclienttointerceptkey

dispatching,panelsandmenus,etc.

/

privateCallbackmCallback;

//Theinterfacethatappsusetotalktothewindowmanager.

privateWindowManagermWindowManager;



privateWindowmContainer;

privateWindowmActiveChild;

//Thecurrentwindowattributes.

privatefinalWindowManager.LayoutParamsmWindowAttributes=

newWindowManager.LayoutParams();



/

TheIDthatthemainlayoutintheXMLlayoutfileshouldhave.

/

publicstaticfinalintID_ANDROID_CONTENT=com.android.internal.R.id.content;



/

APIfromaWindowbacktoitscaller.Thisallowstheclientto

interceptkeydispatching,panelsandmenus,etc.

/

publicinterfaceCallback{

publicbooleandispatchTouchEvent(MotionEventevent);

publicvoidonAttachedToWindow();

//...

}

publicabstractLayoutInflatergetLayoutInflater();

publicabstractvoidsetContentView(Viewview,ViewGroup.LayoutParamsparams);



/

Retrievethetop-levelwindowdecorview(containingthestandard

windowframe/decorationsandtheclient''scontentinsideofthat),which

canbeaddedasawindowtothewindowmanager.



Notethatcallingthisfunctionforthefirsttime"locksin"

variouswindowcharacteristicsasdescribedin

{@link#setContentView(View,android.view.ViewGroup.LayoutParams)}.





@returnReturnsthetop-levelwindowdecorview.

/

publicabstractViewgetDecorView();



publicabstractvoidsetTitle(CharSequencetitle);

publicvoidsetIcon(@DrawableResintresId){}

}

有三个值得关注的:



Window.Callback

该接口使得依靠(可以确定的是所有界面都是通过Window展示的)Window来显示内容的其它对象——主要就是Activity——接收Window发送的交互等事件和消息回调。例如Activity.onAttachedToWindow()这样的回调。



getDecorView

DecorView就是和Window关联的用来显示内容的ViewTree的root。所有地方都使用Window来显示内容,不同的Window使用者会需要不同的DecorView,比如Activity就需要DecorView本身具备ActionBar这类的“界面装饰部分”。而Dialog明显没有。



setContentView

Window显示的自定义内容。Activity中的setContentView正是调用关联的Window对象的此方法。将界面内容附加到DecorView作为其子树。



PhoneWindow

PhoneWindow作为目前Window的唯一子类,它的大致定义如下:



publicclassPhoneWindowextendsWindow{

//Thisisthetop-levelviewofthewindow,containingthewindowdecor.

privateDecorViewmDecor;



//Thisistheviewinwhichthewindowcontentsareplaced.Itiseither

//mDecoritself,orachildofmDecorwherethecontentsgo.

privateViewGroupmContentParent;



privateTextViewmTitleView;

privateImageViewmLeftIconView;

privateActionBarViewmActionBar;



@Override

publicvoidinjectInputEvent(InputEventevent){

getViewRootImpl().dispatchInputEvent(event);

}



privateViewRootImplgetViewRootImpl(){

if(mDecor!=null){

ViewRootImplviewRootImpl=mDecor.getViewRootImpl();

if(viewRootImpl!=null){

returnviewRootImpl;

}

}

thrownewIllegalStateException("viewnotadded");

}



privatefinalclassDecorViewextendsFrameLayout{

//...

}

}

DecorView

PhoneWindow的内部类DecorView作为Window关联的ViewTree的rootview。它用来放置ActionBarView,Title和Icon等一些在Activity中经常访问得到的界面元素。而应用自身提供的contentView是作为DecorView的childView存在的。



ViewRootImpl

从View组成ViewTree的角度看,任意的ViewGroup子类都可以做为root,但Window操作ViewTree时需要对rootview做特殊对待,ViewRootImpl正是用来表达rootview,是Window和ViewTree之间的交互接口:



/

Thetopofaviewhierarchy,implementingtheneededprotocolbetweenView

andtheWindowManager.Thisisforthemostpartaninternalimplementation

detailof{@linkWindowManagerGlobal}.



{@hide}

/

@SuppressWarnings({"EmptyCatchBlock","PointlessBooleanExpression"})

publicfinalclassViewRootImplimplementsViewParent{

//...

}

在View中方法ViewRootImplgetViewRootImpl()可以获得一个View关联的ViewRootImpl对象。在ViewTree关联到Window后,每个View会获得一个AttachInfo对象,里面保存了rootView和ViewRootImpl这样的对象来访问根视图。



至于ViewRootImpl到底有哪些作用,后面分析WindowManager的操作时会接触到。目前为止,Window和View的大致概念和它们之间的关系交代完毕。下面需要从WindowManager来继续探索界面框架的工作原理。



WindowManager

概念windowmanager表示用来管理操作Window的角色。稍后会知道真实的动作完全是通过IPC调用系统服务WindowManagerService完成的,我们(调用api的客户端代码)接触到的是一个代理对象。而WindowManager正是IPC的接口描述。



接口WindowManager继承了ViewManager,它的定义是:



/InterfacetoletyouaddandremovechildviewstoanActivity.Togetaninstance

ofthisclass,call{@linkandroid.content.Context#getSystemService(java.lang.String)Context.getSystemService()}.

/

publicinterfaceViewManager{

//AssignthepassedLayoutParamstothepassedViewandaddtheviewtothewindow.

publicvoidaddView(Viewview,ViewGroup.LayoutParamsparams);

publicvoidupdateViewLayout(Viewview,ViewGroup.LayoutParamsparams);

publicvoidremoveView(Viewview);

}

上面的三个方法是面向Window的,分别用来添加,更新、和移除View。



Window是界面框架的基本单位,每个可视的独立交互的界面是一个Window,而界面可视元素是View组成的,View必须依附到Window来被最终绘制和显示。SDK提供的界面又分为不同的Window类型,Dialog,StatusBar,Toast,Activity等,它们都是拥有View并依靠Window来显示内容的“界面类”。



ViewManager的这三个方法几乎就是WindowManager的所有职责,也是我们可以“间接”和Window打交道的方式。addView时会创建新的Window并将传递的View作为其呈现的内容。removeView时也就销毁了Window。



接口WindowManager继承ViewManager:



publicinterfaceWindowManagerextendsViewManager{

publicstaticclassLayoutParamsextendsViewGroup.LayoutParams

implementsParcelable{

//View的left,top

publicintx,y;

/

Thegeneraltypeofwindow.Therearethreemainclassesof

windowtypes:Applicationwindows、Systemwindows、Sub-windows。

/

publicinttype;



//Variousbehavioraloptions/flags.

publicintflags;



//...

}

}

WindowManager.LayoutParams就是和Window中View相关的布局参数。



WindowManagerService

就像Activity的创建一样,Window的创建不是像View那样new出来的。而是通过IPC调用系统服务WindowManagerService完成的。如果接触过RemoteViews的话很容易明白类似的设计。

WindowManager的实现类是WindowManagerImpl:



/

Provideslow-levelcommunicationwiththesystemwindowmanagerfor

operationsthatareboundtoaparticularcontext,displayorparentwindow.

Instancesofthisobjectaresensitivetothecompatibilityinfoassociated

withtherunningapplication.



Thisobjectimplementsthe{@linkViewManager}interface,

allowingyoutoaddanyViewsubclassasatop-levelwindowonthescreen.

Additionalwindowmanagerspecificlayoutparametersaredefinedfor

controloverhowwindowsaredisplayed.Italsoimplementsthe{@linkWindowManager}

interface,allowingyoutocontrolthedisplaysattachedtothedevice.



ApplicationswillnotnormallyuseWindowManagerdirectly,insteadrelying

onthehigher-levelfacilitiesin{@linkandroid.app.Activity}and

{@linkandroid.app.Dialog}.



Evenforlow-levelwindowmanageraccess,itisalmostnevercorrecttouse

thisclass.Forexample,{@linkandroid.app.Activity#getWindowManager}

providesawindowmanagerforaddingwindowsthatareassociatedwiththat

activity--thewindowmanagerwillnotnormallyallowyoutoaddarbitrary

windowsthatarenotassociatedwithanactivity.



@seeWindowManager

@seeWindowManagerGlobal

@hide

/

publicfinalclassWindowManagerImplimplementsWindowManager{

privatefinalWindowManagerGlobalmGlobal=WindowManagerGlobal.getInstance();



@Override

publicvoidaddView(Viewview,ViewGroup.LayoutParamsparams){

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

}



@Override

publicvoidremoveView(Viewview){

mGlobal.removeView(view,false);

}



//...

}

可以看到,WindowManagerImpl将实际操作委托给WindowManagerGlobalmGlobal完成。WindowManagerGlobal是单例的。



publicfinalclassWindowManagerGlobal{

privatefinalArrayListmViews=newArrayList();

privatefinalArrayListmRoots=newArrayList();

privatefinalArrayListmParams=

newArrayList();



publicvoidaddView(Viewview,ViewGroup.LayoutParamsparams,

Displaydisplay,WindowparentWindow){

if(view==null){

thrownewIllegalArgumentException("viewmustnotbenull");

}

if(display==null){

thrownewIllegalArgumentException("displaymustnotbenull");

}

if(!(paramsinstanceofWindowManager.LayoutParams)){

thrownewIllegalArgumentException("ParamsmustbeWindowManager.LayoutParams");

}



finalWindowManager.LayoutParamswparams=(WindowManager.LayoutParams)params;

if(parentWindow!=null){

parentWindow.adjustLayoutParamsForSubWindow(wparams);

}



ViewRootImplroot;

ViewpanelParentView=null;



synchronized(mLock){

//Startwatchingforsystempropertychanges.

if(mSystemPropertyUpdater==null){

mSystemPropertyUpdater=newRunnable(){

@Overridepublicvoidrun(){

synchronized(mLock){

for(inti=mRoots.size()-1;i>=0;--i){

mRoots.get(i).loadSystemProperties();

}

}

}

};

SystemProperties.addChangeCallback(mSystemPropertyUpdater);

}



intindex=findViewLocked(view,false);

if(index>=0){

if(mDyingViews.contains(view)){

//Don''twaitforMSG_DIEtomakeit''swaythroughroot''squeue.

mRoots.get(index).doDie();

}else{

thrownewIllegalStateException("View"+view

+"hasalreadybeenaddedtothewindowmanager.");

}

//ThepreviousremoveView()hadnotcompletedexecuting.Nowithas.

}



//Ifthisisapanelwindow,thenfindthewindowitisbeing

//attachedtoforfuturereference.

if(wparams.type>=WindowManager.LayoutParams.FIRST_SUB_WINDOW&&

wparams.type<=WindowManager.LayoutParams.LAST_SUB_WINDOW){

finalintcount=mViews.size();

for(inti=0;i
if(mRoots.get(i).mWindow.asBinder()==wparams.token){

panelParentView=mViews.get(i);

}

}

}



root=newViewRootImpl(view.getContext(),display);



view.setLayoutParams(wparams);



mViews.add(view);

mRoots.add(root);

mParams.add(wparams);

}



//dothislastbecauseitfiresoffmessagestostartdoingthings

try{

root.setView(view,wparams,panelParentView);

}catch(RuntimeExceptione){

//BadTokenExceptionorInvalidDisplayException,cleanup.

synchronized(mLock){

finalintindex=findViewLocked(view,false);

if(index>=0){

removeViewLocked(index,true);

}

}

throwe;

}

}





//...

}

mViews、mRoots、mParams几个参数保存了所有管理的Window的相关状态。WindowManager是管理所有Window的。

上面ViewRootImpl就是在addView时被创建的。

最后ViewRootImpl.setView方法。



//ViewRootImpl.java

publicvoidsetView(Viewview,WindowManager.LayoutParamsattrs,ViewpanelParentView){

//...

requestLayout();

//...

res=mWindowSession.addToDisplay(mWindow,mSeq,mWindowAttributes,

getHostVisibility(),mDisplay.getDisplayId(),

mAttachInfo.mContentInsets,mInputChannel);

//...

}



@Override

publicvoidrequestLayout(){

checkThread();

mLayoutRequested=true;

scheduleTraversals();

}

方法scheduleTraversals()是对ViewTree的遍历入口。最终的WindowSession.addToDisplay完成IPC调用来创建Window用来显示界面。



此间涉及到IWindowSession,IWindow.Stub等都是典型的aidl相关的技术了。

分析到这里,理解了Window、WindowManager和View之间的工作关系后就在全局上把握了界面框架。

简单地说,所有需要“界面”的地方,都需要通过一个Window。而具体过程,就是使用WindowManager来构造,提供需要显示地View,并设置好布局参数。

Dialog、PopupWindow,Activity这些经常用来搭建界面的类型,都是封装了Window。



比如,Activity的以下相关方法,都是和Window密切相关的:



voidonAttachedToWindow();

voidsetContentView(intlayoutResID);

WindowgetWindow();

WindowManagergetWindowManager();

voidonDetacwww.baiyuewang.nethedFromWindow();

无论如何,了解了Window、WindowManager等概念,对这些high-level的UI类型的工作原理就更加深入了。



总结

Window是独立交互的界面单位。android中所有界面都是不同类型的Window。

View组成ViewTree来表达显示内容。

ViewTree的每个View都指向rootView和ViewRootImpl。

ViewTree依附Window,Window持有ViewTree及ViewRootImpl。Window传递事件给ViewTree,对ViewTree

执行遍历操作,完成测量、布局、绘制显示。

WindowManager负责调用系统服务完成Window的管理操作。

Window是系统服务管理的界面对象,它是系统分发界面交互事件、完成界面显示相关操作的接口。

Window和View是界面框架的不同分级,系统级和UI元素,使得界面框架的设计更为清晰。

Window是界面框架较底层的实现,更高级别上,都是使用Activity、Dialog等这些封装了基于Window的高级UI类型来完成界面构建的。

好像很简单的几个概念吧,拖拖拉拉说了一大堆~~

献花(0)
+1
(本文系thedust79首藏)