Android中Window添加View的底层原理
一、WIndow和windowManager
Window是一个抽象类,它的具体实现是PhoneWindow,创建一个window很简单,只需要创建一个windowManager即可,window具体实现在windowManagerService中,windowManager和windowManagerService的交互是一个IPC的过程。
下面是用windowManager的例子:
mFloatingButton=newButton(this); mFloatingButton.setText("window"); mLayoutParams=newWindowManager.LayoutParams( LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT,0,0, PixelFormat.TRANSPARENT); mLayoutParams.flags=LayoutParams.FLAG_NOT_TOUCH_MODAL |LayoutParams.FLAG_NOT_FOCUSABLE |LayoutParams.FLAG_SHOW_WHEN_LOCKED; mLayoutParams.type=LayoutParams.TYPE_SYSTEM_ERROR; mLayoutParams.gravity=Gravity.LEFT|Gravity.TOP; mLayoutParams.x=100; mLayoutParams.y=300; mFloatingButton.setOnTouchListener(this); mWindowManager.addView(mFloatingButton,mLayoutParams);
flags和type两个属性很重要,下面对一些属性进行介绍,首先是flags:
FLAG_NOT_TOUCH_MODAL表示不需要获取焦点,也不需要接收各种输入,最终事件直接传递给下层具有焦点的window。
FLAG_NOT_FOCUSABLE:在此window外的区域单击事件传递到底层window中。当前的区域则自己处理,这个一般都要设置,很重要。
FLAG_SHOW_WHEN_LOCKED:开启可以让window显示在锁屏界面上。
再来看下type这个参数:
window有三种类型:应用window,子window,系统window。应用类对应一个Activity,子Window不能单独存在,需要附属在父Window上,比如常用的Dialog。系统Window是需要声明权限再创建的window,如toast等。
window有z-ordered属性,层级越大,越在顶层。应用window层级1-99,子window1000-1999,系统2000-2999。这此层级对应着windowManager的type参数。系统层级常用的有两个TYPE_SYSTEM_OVERLAY或者TYPE_SYSTEM_ERROR。比如想用TYPE_SYSTEM_ERROR,只需
mLayoutParams.type=LayoutParams.TYPE_SYSTEM_ERROR。还要添加权限<uses-permissionandorid:name="android.permission.SYSTEM_ALERT_WINDOW"/>。
有了对window的基本认识之后,我们来看下它底层如何实现加载View的。
二、window的创建
其实Window的创建跟之前我写的一篇博客LayoutInflater源码分析有点相似。Window的创建是在Activity创建的attach方法中,通过PolicyManager的makeNewWindow方法。Activity中实现了Window的Callback接口,因此当window状态改变时就会回调Activity方法。如onAttachedToWindow等。PolicyManager的真正实现类是Policy,看下它的代码:
publicWindowmakeNewWindow(Contextcontext){ returnnewPhoneWindow(context); }
到此Window创建完成。
下面分析view是如何附属到window上的。看Activity的setContentView方法。
publicvoidsetContentView(intlayoutResID){ getWindow().setContentView(layoutResID); initWindowDecorActionBar(); }
两部分,设置内容和设置ActionBar。window的具体实现是PhoneWindow,看它的setContent。
publicvoidsetContentView(intlayoutResID){ //Note:FEATURE_CONTENT_TRANSITIONSmaybesetintheprocessofinstallingthewindow //decor,whenthemeattributesandthelikearecrystalized.Donotcheckthefeature //beforethishappens. if(mContentParent==null){ installDecor(); }elseif(!hasFeature(FEATURE_CONTENT_TRANSITIONS)){ mContentParent.removeAllViews(); } if(hasFeature(FEATURE_CONTENT_TRANSITIONS)){ finalScenenewScene=Scene.getSceneForLayout(mContentParent,layoutResID, getContext()); transitionTo(newScene); }else{ mLayoutInflater.inflate(layoutResID,mContentParent); } finalCallbackcb=getCallback(); if(cb!=null&&!isDestroyed()){ cb.onContentChanged(); } }
看到了吧,又是分析它。
这里分三步执行:
1.如果没有DecorView,在installDecor中的generateDecor()创建DecorView。之前就分析过,这次就不再分析它了。
2.将View添加到decorview中的mContentParent中。
3.回调Activity的onContentChanged接口。
经过以上操作,DecorView创建了,但还没有正式添加到Window中。在ActivityResumeActivity中首先会调用Activity的onResume,再调用Activity的makeVisible,makeVisible中真正添加view,代码如下:
voidmakeVisible(){ if(!mWindowAdded){ ViewManagerwm=getWindowManager(); wm.addView(mDecor,getWindow().getAttributes()); mWindowAdded=true; } mDecor.setVisibility(View.VISIBLE); }
通过上面的addView方法将View添加到Window。
三、Window操作View内部机制
1.window的添加
一个window对应一个view和一个viewRootImpl,window和view通过ViewRootImpl来建立联系,它并不存在,实体是view。只能通过windowManager来操作它。
windowManager的实现类是windowManagerImpl。它并没有直接实现三大操作,而是委托给WindowManagerGlobal。addView的实现分为以下几步:
1).检查参数是否合法。
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); }else{ //Ifthere'snoparentandwe'rerunningonLorabove(orinthe //systemcontext),assumewewanthardwareacceleration. finalContextcontext=view.getContext(); if(context!=null &&context.getApplicationInfo().targetSdkVersion>=Build.VERSION_CODES.LOLLIPOP){ wparams.flags|=WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; } }
2).创建ViewRootImpl并将View添加到列表中。
root=newViewRootImpl(view.getContext(),display); view.setLayoutParams(wparams); mViews.add(view); mRoots.add(root); mParams.add(wparams);
3).通过ViewRootImpl来更新界面并完成window的添加过程。
root.setView(view,wparams,panelParentView);
上面的root就是ViewRootImpl,setView中通过requestLayout()来完成异步刷新,看下requestLayout:
publicvoidrequestLayout(){ if(!mHandlingLayoutInLayoutRequest){ checkThread(); mLayoutRequested=true; scheduleTraversals(); } }
接下来通过WindowSession来完成window添加过程,WindowSession是一个Binder对象,真正的实现类是Session,window的添加是一次IPC调用。
try{ mOrigWindowType=mWindowAttributes.type; mAttachInfo.mRecomputeGlobalAttributes=true; collectViewAttributes(); res=mWindowSession.addToDisplay(mWindow,mSeq,mWindowAttributes, getHostVisibility(),mDisplay.getDisplayId(), mAttachInfo.mContentInsets,mAttachInfo.mStableInsets,mInputChannel); }catch(RemoteExceptione){ mAdded=false; mView=null; mAttachInfo.mRootView=null; mInputChannel=null; mFallbackEventHandler.setView(null); unscheduleTraversals(); setAccessibilityFocus(null,null); thrownewRuntimeException("Addingwindowfailed",e); }
在Session内部会通过WindowManagerService来实现Window的添加。
publicintaddToDisplay(IWindowwindow,intseq,WindowManager.LayoutParamsattrs, intviewVisibility,intdisplayId,RectoutContentInsets,RectoutStableInsets, InputChanneloutInputChannel){ returnmService.addWindow(this,window,seq,attrs,viewVisibility,displayId, outContentInsets,outStableInsets,outInputChannel); }
在WindowManagerService内部会为每一个应用保留一个单独的session。
2.window的删除
看下WindowManagerGlobal的removeView:
publicvoidremoveView(Viewview,booleanimmediate){ if(view==null){ thrownewIllegalArgumentException("viewmustnotbenull"); } synchronized(mLock){ intindex=findViewLocked(view,true); ViewcurView=mRoots.get(index).getView(); removeViewLocked(index,immediate); if(curView==view){ return; } thrownewIllegalStateException("Callingwithview"+view +"buttheViewAncestorisattachedto"+curView); } }
首先调用findViewLocked来查找删除view的索引,这个过程就是建立数组遍历。然后再调用removeViewLocked来做进一步的删除。
privatevoidremoveViewLocked(intindex,booleanimmediate){ ViewRootImplroot=mRoots.get(index); Viewview=root.getView(); if(view!=null){ InputMethodManagerimm=InputMethodManager.getInstance(); if(imm!=null){ imm.windowDismissed(mViews.get(index).getWindowToken()); } } booleandeferred=root.die(immediate); if(view!=null){ view.assignParent(null); if(deferred){ mDyingViews.add(view); } } }
真正删除操作是viewRootImpl来完成的。windowManager提供了两种删除接口,removeViewImmediate,removeView。它们分别表示异步删除和同步删除。具体的删除操作由ViewRootImpl的die来完成。
booleandie(booleanimmediate){ //Makesurewedoexecuteimmediatelyifweareinthemiddleofatraversalorthedamage //donebydispatchDetachedFromWindowwillcausehavoconreturn. if(immediate&&!mIsInTraversal){ doDie(); returnfalse; } if(!mIsDrawing){ destroyHardwareRenderer(); }else{ Log.e(TAG,"Attemptingtodestroythewindowwhiledrawing!\n"+ "window="+this+",title="+mWindowAttributes.getTitle()); } mHandler.sendEmptyMessage(MSG_DIE); returntrue; }
由上可知如果是removeViewImmediate,立即调用doDie,如果是removeView,用handler发送消息,ViewRootImpl中的Handler会处理消息并调用doDie。重点看下doDie:
voiddoDie(){ checkThread(); if(LOCAL_LOGV)Log.v(TAG,"DIEin"+this+"of"+mSurface); synchronized(this){ if(mRemoved){ return; } mRemoved=true; if(mAdded){ dispatchDetachedFromWindow(); } if(mAdded&&!mFirst){ destroyHardwareRenderer(); if(mView!=null){ intviewVisibility=mView.getVisibility(); booleanviewVisibilityChanged=mViewVisibility!=viewVisibility; if(mWindowAttributesChanged||viewVisibilityChanged){ //Iflayoutparamshavebeenchanged,firstgivethem //tothewindowmanagertomakesureithasthecorrect //animationinfo. try{ if((relayoutWindow(mWindowAttributes,viewVisibility,false) &WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME)!=0){ mWindowSession.finishDrawing(mWindow); } }catch(RemoteExceptione){ } } mSurface.release(); } } mAdded=false; } WindowManagerGlobal.getInstance().doRemoveView(this); }
主要做四件事:
1.垃圾回收相关工作,比如清数据,回调等。
2.通过Session的remove方法删除Window,最终调用WindowManagerService的removeWindow
3.调用dispathDetachedFromWindow,在内部会调用onDetachedFromWindow()和onDetachedFromWindowInternal()。当view移除时会调用onDetachedFromWindow,它用于作一些资源回收。
4.通过doRemoveView刷新数据,删除相关数据,如在mRoot,mDyingViews中删除对象等。
voiddoRemoveView(ViewRootImplroot){ synchronized(mLock){ finalintindex=mRoots.indexOf(root); if(index>=0){ mRoots.remove(index); mParams.remove(index); finalViewview=mViews.remove(index); mDyingViews.remove(view); } } if(HardwareRenderer.sTrimForeground&&HardwareRenderer.isAvailable()){ doTrimForeground(); } }
3.更新window
看下WindowManagerGlobal中的updateViewLayout。
publicvoidupdateViewLayout(Viewview,ViewGroup.LayoutParamsparams){ if(view==null){ thrownewIllegalArgumentException("viewmustnotbenull"); } if(!(paramsinstanceofWindowManager.LayoutParams)){ thrownewIllegalArgumentException("ParamsmustbeWindowManager.LayoutParams"); } finalWindowManager.LayoutParamswparams=(WindowManager.LayoutParams)params; view.setLayoutParams(wparams); synchronized(mLock){ intindex=findViewLocked(view,true); ViewRootImplroot=mRoots.get(index); mParams.remove(index); mParams.add(index,wparams); root.setLayoutParams(wparams,false); } }
通过viewRootImpl的setLayoutParams更新viewRootImpl的layoutParams,接着scheduleTraversals对view重新布局,包括测量,布局,重绘,此外它还会通过WindowSession来更新window。这个过程由WindowManagerService实现。这跟上面类似,就不再重复,到此Window底层源码就分析完啦。
以上就是本文的全部内容,希望对大家的学习有所帮助。