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的分析。
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的分析。
相关文章推荐
- MFC程序的启动过程——先全局对象theApp(第一入口),后WinMain(真正入口),会引爆pApp->InitInstance从而创建窗口(程序员入口)
- App的启动过程(6)面向应用程序的本地窗口surface
- Android中将布局文件/View添加至窗口过程分析 ---- 从setContentView()谈起(写的很好,这个不是从启动app说的,说的是UI是怎么绘制的)
- Android6.0 WMS(九) WMS切换Activity窗口(App Transition)的过程分析
- Android的APP启动过程分析
- Activity启动及窗口创建过程详解
- Android窗口管理服务WindowManagerService显示Activity组件的启动窗口(Starting Window)的过程分析
- Android窗口管理服务WindowManagerService切换Activity窗口(App Transition)的过程分析
- 应用窗口activity的启动过程
- ios5 app启动过程中执行到过程分析
- 框架层理解Activity生命周期(APP启动过程)
- ios app 启动过程
- 005-iOS App程序启动过程
- Android窗口管理服务WindowManagerService显示Activity组件的启动窗口(Starting Window)的过程分析
- android app启动过程笔记
- 框架层理解Activity生命周期(APP启动过程)
- Android窗口管理服务WindowManagerService显示Activity组件的启动窗口(Starting Window)的过程分析
- titanium开发实例--社交APP二之注册窗口
- Android窗口管理服务WindowManagerService切换Activity窗口(App Transition)的过程分析
- Android窗口管理服务WindowManagerService切换Activity窗口(App Transition)的过程分析