android从setContentView到window显示机制

xiaoxiao2021-02-27  313

android  从setContentView到window显示机制

参考文章:

http://blog.csdn.net/yanbober/article/details/46361191

http://blog.csdn.net/yanbober/article/details/45970721

https://mp.weixin.qq.com/s/zNXi6g7AAWz3lzxocXwZyg

http://blog.csdn.net/yhaolpz/article/details/68936932

 

setContentView

涉及到的基本对象:

Window:是一个抽象类,提供了绘制窗口的一组通用API。

PhoneWindow:是Window的具体继承实现类。而且该类内部包含了一个DecorView对象。每个Activity都有。

DecorView:是PhoneWindow的内部类,是FrameLayout的子类,是对FrameLayout进行功能的修饰(所以叫DecorXXX),是所有应用窗口的根View。

WindowManagerGlobal:他是个单例对象,这个单例每个应用程序只有唯一的一个。它维护了本应用程序内所有Window的DecorView,以及与每一个DecorView对应关联的ViewRootImpl。WindowManager只是一个代理,实际的管理功能是通过WindowManagerGlobal实现的

ViewRootImplViewRootImpl不是一个View,而是负责管理视图,它和系WindowManagerService进行交互,并且管理着DecorView制和窗口状,它是视图树绘制的起点。

Window是个抽象概念:每一个Window对应着一个View和ViewRootImpl,Window通过ViewRootImpl来和View建立联系,View是Window存在的实体,只能通过WindowManager来访问Window。

WindowManager的实现是WindowManagerImpl其再委托给WindowManagerGlobal来对Window进行操作,其中有四个List分别储存对应的View、ViewRootImpl、WindowManger.LayoutParams和正在被删除的View

Window的实体是存在于远端的WindowMangerService中,所以增删改Window在本端是修改上面的几个List然后通过ViewRootImpl重绘View,通过WindowSession(每个应用一个)在远端修改Window。

Activity创建Window:Activity会在attach()中创建Window并设置其回调(onAttachedToWindow()、dispatchTouchEvent()),Activity的Window是由Policy类创建PhoneWindow实现的。然后通过Activity#setContentView()调用PhoneWindow的setContentView。

setContentView整体过程:

1.     创建一个DecorView的对象mDecor,该mDecor对象将作为整个应用窗口的根视图。

2.     依据Feature等style theme创建不同的窗口修饰布局文件,并且通过findViewById获取Activity布局文件该存放的地方(窗口修饰布局文件中id为content的FrameLayout)。

3.     将Activity的布局文件添加至id为content的FrameLayout内。

4.     当setContentView设置显示OK以后会回调Activity的onContentChanged方法。Activity的各种View的findViewById()方法等都可以放到该方法中,系统会帮忙回调。

LayoutINflater机制

setContentView过程中,我们最后调用了LayoutInflator来生成对应的View

Inflate方法inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot):

1.     inflate(xmlId, null); 只创建temp的View,然后直接返回temp。

2.     inflate(xmlId, parent); 创建temp的View,然后执行root.addView(temp,params);最后返回root。

3.     inflate(xmlId, parent, false); 创建temp的View,然后执行temp.setLayoutParams(params);然后再返回temp。

4.     inflate(xmlId, parent, true); 创建temp的View,然后执行root.addView(temp,params);最后返回root。

5.     inflate(xmlId, null, false); 只创建temp的View,然后直接返回temp。

6.     inflate(xmlId, null, true); 只创建temp的View,然后直接返回temp。

需要注意:

1View的layout_width和layout_height设置的是View在ViewGroup即父布局布局中的大小的

2因此,如果没有父布局parent,则我们设置的子布局就正确处理

Window显示机制

Window和windowManager的生成和关系:

Context的WindowManager对每个APP来说是一个全局单例的,而Activity的WindowManager是每个Activity都会新创建一个的(其实你从上面分析的两个实例化WindowManagerImpl的构造函数参数传递就可以看出来,Activity中Window的WindowManager成员在构造实例化时传入给WindowManagerImpl中mParentWindow成员的是当前Window对象,而ContextImpl的static块中单例实例化WindowManagerImpl时传入给WindowManagerImpl中mParentWindow成员的是null值)。

Activity的WindowManager当Activity结束时WindowManager就无效了,而application的WindowManager在app退出时都一直存在。

Activity实际并不会去控制UI视图,它主要是去控制生命周期和处理事件,真正的视图控制是Window。

 

WindowManager 是一个接口,它的真正实现是WindowManagerImpl 类WindowManagerImpl 并没有直接实现 Window 的三大操作(addView,updateViewLayout,removeView),而是交给了 WindowManagerGlobal 来处理。

执行过程:

 

 

Diaglog的显示

 

在Dialog中我们接收到activity传入的context,然后去获取windowManager,去创建window然后再与与windowMnager关联,这样我们dialog就可以在activity的基础上进行显示。

Show()中通过获取window的decorView我们添加相应的布局就完成了。

Dismiss()中将decor从window中移除,并释放,然后将window展示关闭就完成了。

Dialog的实质无非也是使用WindowManager的addView、updateViewLayout、removeView进行一些操作展示。

Popupwindow

 

PopWindow的实质无非也是使用WindowManager的addView、updateViewLayout、removeView进行一些操作展示。与Dialog不同的地方是没有新new Window而已(也就没法设置callback,无法消费事件,也就是前面说的PopupWindow弹出后可以继续与依赖的Activity进行交互的原因)。

在dialog中,我们新建了一个window,window对象可以添加监听等方法,新window就覆盖在了原来window之上。而popupwindow是直接通过windowManager来添加view,也就是在当前的window层做了相应的操作。

Toast

这里的windowmManager是application产生的。也就用了windowMManager的addView方法和removeView来做的相应的处理。

所以:

在使用Toast时context参数尽量使用getApplicationContext(),可以有效的防止静态引用导致的内存泄漏。

 

 

 

 

 

转载请注明原文地址: https://www.6miu.com/read-2846.html

最新回复(0)