一.Window和WindowManager
1.通过WindowManager添加Window的过程
代码如下:
mButton = new Button(this);
mLayoutParams = new WindowManager.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; //flag参数
mLayoutParams.gravity = Gravity.LEFT | Gravity.TOP;
mLayoutParams.x = 100; //设置坐标
mLayoutParams = 300;
mWindowManager.addView(mButton,mLayoutParams);
flags参数:
1)FLAG_NOT_FOCUSABLE:表示window不需要获取焦点,也不需要接受各种输入事件,这些事件最终会传递到下面具有焦点的Window。
2)FLAG_NOT_TOUCH_MODAL:默认开启。系统会将Window区域以外的单击事件传递给底层的Window,而在区域以内的单击事件则自己处理。
3)FLAG_SHOW_WHEN_LOCKED:可以让Window显示在锁屏上。
2.WindowManager的功能
WindowManager常用的功能只有三个方法:添加View、更新View和删除View。这三个方法定义在ViewManager中,而WindowManager继承ViewManager。
public interface ViewManager{
public void addView(View view,ViewGroup.LayoutParams params);
public void updateView(View view,ViewGroup.LayoutParams params);
public void removeView(View view);
}
二.Window内部机制
1.基本理解
Window是一个抽象的概念,每一个Window都对应着一个View和ViewRootImpl,Window和View是通过ViewRootImpl来建立联系的,也就是说Window不是实际存在的,View才是Window的实体,所以在实际使用中无法直接访问Window。
2.Window的添加过程
Window的添加过程需要WindowManager的addView()方法来进行添加。WindowManager是一个接口,真正实现是WindowManagerImpl类,其具体实现的代码如下:
public void addView(View view,ViewGroup.LayoutParams params){
mGlobal.addView(view,params,mDisplay,mParentWindow);
}
public void updateView(View view,ViewGroup.LayoutParams params){
mGlobal.updateView(view,params);
}
public void removeView(View view){
mGlobal.removeView(view,false);
}
在添加view的方法中,主要的操作工作都是WindowManagerGlobal来实现的,它添加view的方法主要分为以下几步:
(1)检查参数是否合法,如果是子Window那么还需要调整一些布局参数。
(2)创建ViewManagerImpl并将View添加到列表中。
private final ArrayList<View> mViews = new ArrayList<View>();
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
private final ArrayList<WindowManager.LayoutParams> mParams = new ArrayList<WindowManager.LayoutParams>();
private final ArraySet<View> mDyingViews = new ArraySet<View>(); //正在被删除的view对象
root = new ViewRootImpl(view.getContext(),display);
view.setLayoutParams(wparams); //设置view的布局参数
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
(3)通过ViewRootImpl来更新界面并完成Window的添加过程。这个过程主要是由ViewRootImpl的setView()方法来完成的。
3.Window的删除过程
Window的删除过程同样是由WindowManagerImp交由WindowManagerGlobal来实现的,其过程如下:
(1)首先调用WindowManagerGlobal的removeView(),实现如下:
public void removeView(View view,boolean immediate){
if(view == null){
throe new IllegalArgumentException("view must not be null");
}
synchronized(mLock){
int index = findViewLocked(view,true); //查找待删除的View索引
View curView = mRoots.get(index).getView();
removeViewLocked(index,immediate); //调用来进一步删除
if(curView == view){
return;
}
throw new IllegalStateException("Calling with view" + view + "but the ViewAncestor is attached" + curView);
}
}
(2)调用removeViewLocked()方法来进一步删除。
(3)然后通过ViewRootImpl来发送最后的删除工作的请求。在ViewRootImpl中提供了die()方法,该方法会发送一个请求删除消息后就会立刻返回,其实现如下:
boolean die(boolean immediate){
if(immediate && !mIsInTraversal){
doDie();
return false;
}
if(!mIsDrawing){
destroyHardwareRenderer();
}
else{
Log.e(Tag...);
}
mHandler.sendEmptyMessage(MSG_DIE);
return true;
}
(4)真正删除的工作是由dispatchDetachedFromWindow()方法来完成的。该方法主要左两件事:1.垃圾回收相关的工作 2.通过Session的remove方法删除nWindow 3.调用View的dispatchDetachedFromWindow()方法 4.调用WindowManagerGlobal的doRemoveView的方法刷新数据。
4.Window的更新过程
更新过程同样WindowManagerGlobal来实现的,其过程如下:
(1)先调用WindowManagerGlobal的updateViewLayout方法,实现如下:
public void updateViewLayout(View view,ViewGroup.LayoutParams params){
if(view == null){
throw new IllegalArgumentException("view must not be null");
}
if(!(params instance WindowManager.LayoutParams)){
throw new IllegalArguementException("Params must be WindowManager.LayoutParams");
}
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
view.setLayoutParams(wparams);
synchronized(mLock){
int index = findViewLocked(view,true);
ViewRootImpl root = mRoots.get(index);
mParams.remove(index);
root.setLayoutParams(wparams,false);
}
}
(2)在ViewRootImpl中通过scheduleTraversals()方法来对View重新布局,包括测量、布局和重绘。
三.Window的创建过程
1.Activity的Window创建过程
创建过程如图所示:
Activity的Window创建过程
2.Dialog的Window的创建过程
Dialog的Window创建过程3.Toast的Window创建过程
注意:
1)由于Toast具有定时取消的功能,所以系统采用Handler来实现。
2)在Toast的内部有两类IPC过程,第一类是Toast访问NotificationManagerService(NMS);第二类是NotificationManagerService回调Toast里的TN接口。
3)Toast属于系统Window,它内部视图由两种方式指定,第一种是系统默认的样式;第二种是通过setView()来指定一个自定义View。
4)Toast提供了show()和cancel()分别用于显示和隐藏Toast。
5)Toast的显示和隐藏都需要通过NMS来实现,由于NMS运行在系统进程中,故需通过远程调用的方式来进行显示和隐藏Toast。
6)NMS处理Toast的显示和隐藏请求时会跨进程回调TN中的方法,由于TN运行在Binder线程池中,故需通过Handler将其切换到当前线程(发送Toast请求的线程)。
显示/隐藏过程:
1)首先调用NMS中的enqueueToast()方法。在该方法中会将Toast请求封装为ToastRecord对象并将其添加到一个名为mToastQueue()的队列中。
2)之后NMS会通过showNextToastLocked()方法来显示当前的Toast,在该方法中Toast的显示是由ToastRecord()的callback来完成的,这个callback实际上就是Toast中的TN对象的远程Binder。
3)Toast显示之后,NMS还会通过scheduleTimeoutLocked()来发送一个延时消息,这就决定了Toast显示的时长。
4)Toast的隐藏也是通过ToastRecord()的callback来完成的,其过程也是一次IPC过程。
5)最后在callback中会调用show()和hide()方法来最终完成Toast的显示和隐藏,并且TN的handleShow会将Toast视图添加到Window中。
网友评论