参考资料
凯子哥带你学Framework· Activity界面显示全解析-上
凯子哥带你学Framework Activity界面显示全解析-下
重要:!!! Android 屏幕刷新机制
目录
- 1)简介
- 2)Window的内部机制
- 2.1)Window的添加过程
- 2.2)Window的删除过程
- 2.3)Window的更新过程
- 3)Window的创建过程
- 3.1)Activity的Window创建过程
- 3.2)Dialog的Window创建过程
- 3.3)Toast的Window创建过程
1)简介
- Window是一个抽象类,具体实现为PhoneWindow
- 只需要WindowManager即可创建一个Window
- WindowManager是个接口,是外界访问Window的入口,具体实现位于WindowManagerService(WMS),WindowManager和WindowManagerService的交互是一个IPC过程
- Android所有视图都是附加在Window上,通过Window来呈现,因此Window是View的直接管理者。如事件分发就是通过Window传递给DecorView。
- 站在系统角度,Window代表一块显示区域,系统并不关心Window内的绘制内容
//WindowManager可以通过下面两种方式进行获取
WindowManager mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
WindowManager mWindowManager = (WindowManager) getWindowManger();
//WindowManager添加一个Window
mWindowManager.addView(mButton, mLayoutParams);
2)Window的内部机制
Window是以View的形式存在,View的绘制流程从ViewRoot开始,ViewRoot对应ViewRootImpl类,它是连接WindowManager和DecorView的纽带。
public interface ViewManager
{
public void addView(View view, ViewGroup.LayoutParams params);
public void updateViewLayout(View view, ViewGroup.LayoutParams params);
public void removeView(View view);
}
->ViewManager.addView
->WindowManager.addView(也是接口,继承ViewManager)
->WindowManagerImpl.addView(实现了WindowManager接口)
->WindowManagerGlobal.addView(接受WindowManagerImpl的委托,并构建ViewRootImpl)
->ViewRootImpl.setView(ViewRooImpl是WindowManagerGlobal的addView方法中新建的对象)
->requestLayout(setView中的一个方法调用,调用performTraversals绘制整个view树)
->WindowSession.addToDisplay(在setView方法中,它是一个Binder对象,用于与WindowManagerService进行IPC通信)
->Session(WindowSession的具体实现)
->WindowManagerService(实现Window的添加)
ViewRootImpl负责管理视图树和与WMS交互,它是WindowManager和DecorView的纽带,与WMS交互是通过WindowSession。而且ViewRootImpl也负责UI界面的布局与渲染,负责把一些事件分发至Activity,以便Activity可以截获事件。大多数情况下,它管理Activity顶层视图DecorView,它相当于MVC模型中的Controller。
3)Window的创建过程
3.1)Activity的Window创建过程
Activity | 四大组件之一, 是存放View对象的容器,也是我们界面的载体,可以用来展示一个界面。它有一个SetContentView()方法 ,可以将我们定义的布局设置到界面上 |
View | 就是一个个视图的对象 |
Window | 抽象类,是一个顶层的窗口,它的唯一实例是PhoneWindow它提供标准的用户界面策略,如背景、标题、区域,默认按键处理等 |
Activity就像是一扇贴着窗花的窗口,Window就想上窗口上面的玻璃,而View对象就像一个个贴在玻璃上的窗花。
- Activity最终会由ActivityThread中的performLaunchActivity来完成启动,此方法内会通过类加载器创建Activity的实例对象,并调用attach关联所依赖的上下文。
- 在attach方法中,系统会创建Activity所属的Window对象,并获取了WindowManager对象。
mWindow = PolicyManager.makeNewWindow(this);
mWindow.setCallback(this);/设置回调函数,使得Activity可以处理一些事件,如dispatchTouchEvent()
- setContentView()调用了Window的setContentView(),将界面绘制交给了Window对象,也就是View通过Activity添加到了Window上面。
- DecorView是PhoneWindow的内部类,继承自FrameLayout,是最底层的界面
- 如果没有DecorView,则创建它
- 将View添加到DecorView的mContentParent对象中
- 回调Activity的onContentChanged()通知Activity视图已改变
- 经过上面步骤DecorView已初始化,且Activity的布局也被添加到了DecorView的mContentParent对象中。但这时DecorView还没有被WindowManager添加到Window中,
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);
}
在Activity的onResume()中,WindowManager会执行addView(mDecorView,getWindow().getAttributes())。至此Activity的视图才能被用户看见。
3.2)Dialog的Window创建过程
AlertDialog和Activity一样,内部有一个Window,我们构造AlertDialog.Builder,通过Builder设置Dialog各种属性,,这些参数会被放在一个名为P(AlertController类型)的变量中,在调用AlertDialog.Builder.create方法的时候,内部首先会new一个 AlertDialog,AlertDialog的父类Dialog的构造函数中会new一个PhoneWindow赋值给AlertDialog中的Window,并且为它设置了回调。AlertDialog创建之后执行apply方法,将P中的参数设置赋值给Dialog,后我们调用Dialog.show方法展示窗口,内部调用dispatchOnCreate,最终会走到setContentView,到此Dialog的Window和Dialog视图关联到了一起,最后执行mWindowManager.addView方法,通过WindowManager将DecorView添加到Window之中,此时Dialog显示在了我们面前。
- 在Activity中使用Dialog的时候,为什么有时候会报错“Unable to add window -- token is not valid; is your activity running?”?
答:
一般发生在Activity进入后台,Dialog没有主动Dismiss掉,然后从后台再次进入App的时候。
Window分为三种,子窗口,应用窗口和系统窗口,子窗口必须依附于一个上下文,就是Activity,因为它需要Activity的appToken,
子窗口的window,比如Dialog,想要显示必须保证appToken与Activity保持一致,当Activity销毁,再次回来的时候,Dialog试图重新创建,调用ViewRootImpl的setView()的时候会出问题,所以当Activity不可见的时候,主动Dismiss掉Dialog,否则会因为appToken为空crash。 - 在子线程中为什么不能显示Toast?
Toast源码是与NotificationManagerService进行IPC通信,
show()的时候,通过handler来接收,子线程中没有handler,所以无法显示。可以给子线程添加Looper
sService = INotificationManager.Stub.asInterface(ServiceManager.getService("notification"));
- 为什么不能在setContentView()之后设置某些Window属性标志?
public void setContentView(View view) {
getWindow().setContentView(view);
initWindowDecorActionBar();//初始化window属性
}
在Activity.setContentView()之后,Window的一些特征位将被锁定