您的位置:首页 > 移动开发

App的启动过程(4)在WMS中注册窗口

2017-06-01 17:10 225 查看
Step3、在WMS中注册窗口

WMS中的窗口window是一个抽象的概念,由WindowState来描述状态。跟PhoneWindow不是一个概念。接着root.setView()分析。

/* ViewRootImpl.java */

ViewRootImpl除了作为ViewTree的管理者,另一个作用就是跟WMS通信,它在构造时就建立了与WMS通信的通道,这里的WindowSession是匿名的BinderServer,所以要通过WindowManagerService这个实名BinderServer的接口openSession间接获得。

public ViewRootImpl(Context context,Display display) {

//ViewRoot跟WMS是通过WindowSession通信的,

         mWindowSession= WindowManagerGlobal.getWindowSession();

//ViewRoot接下来会通过mWindowSession提供一个mWindow对象给WMS,以方便WMS跟viewroot的通信

         mWindow= new W(this);

}

public void setView(){

//这是执行ViewTree的第一次遍历,应用程序在很多情况下都会遍历viewtree,如:1)、应用刚启动;2)、用户产生的触摸、按键等事件,影响到UI界面了;3)、内部自发的layout请求;4)、View的属性、可见性发生了变化等。

         requestLayout();

//这个函数会调用到WMS的addWindow,向WMS申请注册一个窗口,同时把W对象(mWindow)传给WMS

         res= mWindowSession.addToDisplay(mWindow,…);

}

equestLayout()àscheduleTraversals();

void scheduleTraversals() {

//向Choreography注册一个CALLBACK_TRAVERSAL回调,驱动layout的执行,Choreography是Vsync事件的接受者,同时会把Vsync事件转发给对Vsync感兴趣的注册者。

         mChoreographer.postCallback(

Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);

}

//最终的遍历是在mTraversalRunnable这个线程中执行的,一旦Vsync事件到来,run函数就会被调用。

final class TraversalRunnable implementsRunnable {

         publicvoid run() {

                   doTraversal(); àperformTraversals();   }       

}

private void performTraversals() {

//遍历分以下三步:尺寸,位置,绘制

         performMeasure(childWidthMeasureSpec,childHeightMeasureSpec);

         performLayout(lp,mWidth, mHeight);

         performDraw();

}

private void performMeasure(intchildWidthMeasureSpec, int childHeightMeasureSpec){

//只是简单了调用了ViewTree顶层元素的measure函数,把请求交给了View树的根元素,View中有measure和onMeasure两个函数,在扩展一个View组件时,尽量只重载onMeasure,真正的测量是在onMeasure中进行的。

         mView.measure(childWidthMeasureSpec,childHeightMeasureSpec);

         àonMeasure()

测量过程中,循环遍历每一个子view,综合考虑每个view的属性,mode,size,layoutParams,padding,mergin等

}

private void performLayout(){

//经过performMeasure,viewtreee中元素的大小基本确定,接下来是位置测量。

         host.layout(0,0, host.getMeasuredWidth(), host.getMeasuredHeight());

         à onLayout(changed, l, t, r, b);

//同样时交给了ViewTree的根元素host,然后调用具体扩展组件的onLayout函数,对其子对象做布局调整。

}

private void performDraw() {

//对象的layout确定后,就能在此基础上执行Draw,绘制UI的核心:1)、Surface画板,UI数据正常存储的地方;2)、图形绘制的方式,硬件还是软件;3)、ViewTree中个元素的协调关系。以下是软件渲染的方式。

         drawSoftware(surface,mAttachInfo, xOffset, yOffset, scalingRequired, dirty);

}

private boolean drawSoftware(){

//取得一个canvas对象,是一个画图的工具集,底层实现是surface,

         canvas= mSurface.lockCanvas(dirty);

//左边变换

         canvas.translate(-xoff,-yoff);

         mTranslator.translateCanvas(canvas);

//从顶层元素开始遍历绘制

         mView.draw(canvas);

//绘制完毕,释放canvas通过surface提交绘制结果给surfaceflinger,然后合成渲染到FrameBuffer,显示到屏幕。

         surface.unlockCanvasAndPost(canvas);

}

到这里ViewTree的遍历就分析完了,draw的进一步分析先等等,前面setView时,还有一个重要任务向WMS注册窗口,先看addWindow的处理。

/* WindowManagerService.java*/

这个函数的参数中没有View相关的变量,其实WMS并不关心viewTree表达的具体UI,它只要知道应用界面的大小、层级值就可以了。这里有一个参数InputChannel是跟InputmanagerService相关的,因为IMS要把事件传给窗口,就要获得窗口的信息。

public int addWindow(Session session, IWindow client,int seq, WindowManager.LayoutParams attrs, int viewVisibility, int displayId,Rect outContentInsets, Rect outStableInsets, Rect outOutsets, InputChanneloutInputChannel){

//权限检查,如果应用窗口,直接ok,系统窗口要细分

         int res =mPolicy.checkAddPermission(attrs, appOp);

//一个IWindow只允许添加唯一的窗口,避免重复添加,

if(mWindowMap.containsKey(client.asBinder()))

//查找子窗口的父窗口

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

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

//检查窗口的有效性,这里有个attrs.token,是Ibinder类型,代表这个窗口的主人,就是startActivity时,AMS为每个Activity创建的ActivityRecord对象,通过mWindowManager.addAppToken把这个信息记录到mTokenMap中,如果没有这步,这里添加窗口就会失败,如果这attrs.token在mTolenmap中找不到对应的WindowToken,并且又是这几种窗口类型:APPLICATION_WINDOW,TYPE_INPUT_METHOD,TYPE_WALLPAPER,TYPE_DREAM…会出错返回。

         WindowTokentoken = mTokenMap.get(attrs.token);

if (type >=FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW)

         ……

//为这个窗口新增一个windowstate对象,

         WindowStatewin = new WindowState(this, session, client, token,…);

//客户端已经死亡了,比如应用crash了

         if(win.mDeathRecipient == null)

//调整窗口属性

         mPolicy.adjustWindowParamsLw(win.mAttrs);

//把新增的token加到tokenmap中

         if(addToken) { mTokenMap.put(attrs.token, token); }

//将所有window按顺序排列,因为新增window,可能影响已经排好序的windowlist,这里会牵扯到窗口的类型,窗口的层级,计算位置插入:windows.add(i,
window);

         addWindowToListInOrderLocked(win,true);

//给排好序的windowlist中的各窗口分配层级值

         mLayersController.assignLayersLocked(displayContent.getWindowList());

}

private void addWindowToListInOrderLocked(){

//系统当前的所有窗口,

         WindowListwindows = win.getWindowList();

//应用程序名下的window数量

token.windows.

//应用程序已有窗口,因为app类型的窗口等级最低,只要把它放在此应用窗口列表的最低端就可以,如果应用程序没有窗口,比较复杂,根据baselayer找参考位置

placeWindowBefore(wSublayer>= 0 ? attached : w, win);

placeWindowAfter(…);

windows.add(i +1, win);

}

 

以上是WMS端窗口的添加,下面接着ViewTree遍历中最后一步的Draw的分析。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐