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底层源码就分析完啦。
以上就是本文的全部内容,希望对大家的学习有所帮助。