DecorView对象的创建详解
来源:互联网 发布:dnf深度优化win 10 编辑:程序博客网 时间:2024/05/14 21:05
DecorVIew 的创建
我们通过一个示例来看看顶层View(DecorView)是怎么创建出来的。
示例:
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } }
在上面我们一般都会调用setContentView方法来设置布局。该方法调用了Activity的setContentView方法,我们去看看:
public class Activity extends ContextThemeWrapper implements LayoutInflater.Factory2, Window.Callback, KeyEvent.Callback, OnCreateContextMenuListener, ComponentCallbacks2, Window.OnWindowDismissedCallback {....... public void setContentView(int layoutResID) { getWindow().setContentView(layoutResID);//调用与之关联Window中的setContentView()方法 initWindowDecorActionBar(); }...... }
在父类Activity中其实就是调用与之关联的Window(真正的对象类型是PhoneWindow)setContentView()来创建DecorView对象。进入源码看看实现的过程:
进入Window类(实现对象类型是PhoneWindow)的setContentView():
public class PhoneWindow extends Window implements MenuBuilder.Callback { ...... public void setContentView(int layoutResID) { if (mContentParent == null) { installDecor(); } else { mContentParent.removeAllViews(); } mLayoutInflater.inflate(layoutResID, mContentParent); final Callback cb = getCallback(); if (cb != null) { cb.onContentChanged(); } } ...... }
可以看出,首先判断mContentParent 是否为null,如果是则调用installDecor(),否则移除所有的VIew。然后通过mLayoutInflater解析我们的布局文件设置到mContentParent。
接下来调用getCallback()获取CallBack对象。还记得Activity实现了Window.callBack吗?在创建Activity对象的时候,实现Window.callBack。然后在创建PhoneWindow对象的时候调用了setCallBack(this).因此这里获取到CallBack对象其实就是当前Activity本身。这里就是当PhoneWindow接收到系统的事件回调到Activity。
mContentParent 是什么呢?从这里就能看出来mContentParent是个ViewGroup且包裹我们整个布局文件(layout.xml)。
进入installDecor:
public class PhoneWindow extends Window implements MenuBuilder.Callback { ...... private void installDecor() { if (mDecor == null) { mDecor = generateDecor(); mDecor.setIsRootNamespace(true); } if (mContentParent == null) { mContentParent = generateLayout(mDecor); mTitleView = (TextView)findViewById(com.android.internal.R.id.title); if (mTitleView != null) { //根据FEATURE_NO_TITLE隐藏,或者设置mTitleView的值 ...... } } } } ......
代码可能有长,但主要的操作就generateDecor()创建出mDecor,mDecor是DecorView对象,而DecorView继承于FrameLayout。
generateLayout(mDecor)传入mDecor对象,设置mContentParent 。
最后就是根据FEATURE_NO_TITLE标记来是否显示Title。
进入generateDecor()方法:
protected DecorView generateDecor() { return new DecorView(getContext(), -1); }
简单粗暴,就是new出了一个DecorView对象。保存在成员变量mDecor中。
DecorView是Activity的顶级View,一般来说它内部包含标题栏和内容栏(layout.xml,即mContentParent)。内容栏是一定存在的,并且具体的id是‘content’。因此这个时候创建出的DecorView还是一个空白的FrameLayout;
接下来就是调用了generateLayout(mDecor)来初始化DecorView的结构。进入该方法:
protected ViewGroup generateLayout(DecorView decor) { ...... View in = mLayoutInflater.inflate(layoutResource, null); decor.addView(in, new ViewGroup.LayoutParams(FILL_PARENT, FILL_PARENT)); ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT); ...... }
这个方法很长,主要的就是加载具体的布局文件,然后添加到DecorView中。这些布局文件中都包含一个id为content的FrameLayout,将其引用返回给mContentParent。
这样DecorView的结构接初始化完成了。
回到setContentView方法中, 调用了mLayoutInflater.inflate(layoutResID, mContentParent);在这里就是把我们写的布局文件通过inflater加入到mContentParent中。
这样我们写的布局文件成功的添加到DecorView中的mContentParent。现在只是完成了DecorView的创建并初始化,我们还需要把这个创建并初始化完DecorView添加并显示到屏幕上,这里我们就需要用到WindowManager。但是现在的DecorView不能被WM所识别,还无法接收外界输入的信息。在ActivityThread的handleResumeActivity方法中,首先会调用Activity.onResume方法,接着调用Activity.makeVisible方法。正在makeVisible方法中DecorView真正的完成了添加和显示这个两个过程,Activity视图才能被用户所看到:
void makeVisible() { if (!mWindowAdded) { ViewManager wm = getWindowManager(); wm.addView(mDecor, getWindow().getAttributes()); mWindowAdded = true; } mDecor.setVisibility(View.VISIBLE); }
调用了 wm.addView(mDecor, getWindow().getAttributes());这里的wm真实的对象类型是WindowManagerImpl,在创建PhoneWindow对象的文章可以知道。
进入WindowManagerImpl.addView 方法:
@Override public void addView(View view, ViewGroup.LayoutParams params{ mGlobal.addView(view, params, mDisplay, mParentWindow);}
实际上是调用了 WindowManagerGlobal.addView。
public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) { ...... if (view == null) { ...... } ...... int index = findViewLocked(view, false);//查找是否添加过相同的View if (index >= 0) { } root = new ViewRootImpl(view.getContext(), display);//创建View与之对应的ViewRoot对象 view.setLayoutParams(wparams); mViews.add(view);//保存view到WindowManagerGlobal mRoots.add(root);//保存ViewRoot到WindowManagerGlobal mParams.add(wparams);//保存布局参数到WindowManagerGlobal ..... root.setView(view, wparams, panelParentView); ......}
这个方法很长,把不必要的删了。主要的操作,验证view的合法性,
在10行查找该view是否已经添加过至窗口。
然后创建该 View 对应的 ViewRoot,ViewRoot 控制着一个视图的结构(每次 addView 都会创建一个),里面包含了与 WindowManager 通信的 Binder 对象、View 所在界面的 ContextImpl、该视图结构的顶端的 DecorView等信息;
在19-21行把添加的 View、创建的 ViewRootImpl、布局参数添加到 WindowManagerGlobal 中去。用于判断以后添加的view是否已经被添加过。
最后就是ViewRootImpl.setView:
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { synchronized (this) { if (mView == null) { mView = view; // 为 mView 赋值,在这里其实是DecorView ...... } ...... requestLayout(); // 首次调度执行 layout,这里会触发 onAttachToWindow 和 创建 Surface ...... try { ...... res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), mAttachInfo.mContentInsets, mInputChannel); } catch (RemoteException e) { ...... } ...... } }}
在这里方法中,做了两个重要的操作,一、在8行中调用了requestLayout() 首次调度执行 layout。二、在12行中,通过与WMS通信把窗体添加到屏幕,至于是怎么实现的,这里我们不深究。
我们看看ViewRootImpl中requestLayout()方法:
/** * {@inheritDoc} */ public void requestLayout() { checkThread(); mLayoutRequested = true; scheduleTraversals(); }
这里调用scheduleTraversals()方法:
public void scheduleTraversals() { if (!mTraversalScheduled) { mTraversalScheduled = true; sendEmptyMessage(DO_TRAVERSAL); } }
这里发了一个 sendEmptyMessage(DO_TRAVERSAL)消息,由于ViewRootImpl是继承与Handler的,那我们进入ViewRootImpl.handleMessage()方法:
@Override public void handleMessage(Message msg) { switch (msg.what) { ........ case DO_TRAVERSAL: if (mProfile) { Debug.startMethodTracing("ViewAncestor"); } final long traversalStartTime; if (ViewDebug.DEBUG_LATENCY) { traversalStartTime = System.nanoTime(); mLastDrawDurationNanos = 0; } performTraversals(); if (ViewDebug.DEBUG_LATENCY) { long now = System.nanoTime(); mLastTraversalFinishedTimeNanos = now; } if (mProfile) { Debug.stopMethodTracing(); mProfile = false; } break; ...... }
在这个方法中, 在16行调用performTraversals()这个方法,该方法就是系统进行View 树遍历工作的核心函数,这个函数内部逻辑很复杂,但是主体逻辑很清晰,其执行的过程可简单的概括为:是否需要重新计算视图的大小(measure)、是否需要重新布局视图的位置(layout),以及是否需要重绘(Draw)。就是我们常说的View的绘制。
因此首次调用View的绘制,是通过在ViewRootImpl.setView()函数中调用了requestLayout()为开端的。
DecorView生成的过程:
把已经创建好并初始化好的DecorView添加并显示到屏幕的过程:
END。
- DecorView对象的创建详解
- DecorView与window的创建
- 浅谈activity的创建与DecorView的创建加载
- android decorView详解
- android DecorView的使用
- DecorView
- DecorView
- DecorView
- C++之创建对象的方式详解
- Activity的Window创建及DecorView的添加(Android开发艺术探索学习笔记)
- Android中View绘制过程(二) Activity的decorView , Window ,ViewRooImpl 分别什么时候创建
- Context对象创建详解
- java对象创建详解
- DecorView、PhoneWindow、ViewRootlmpl的关系
- 详解Java String字符串对象的创建及管理(1)
- 详解Java String字符串对象的创建及管理(2)
- javascript中的创建自定义对象的六种方式详解
- javascript中的创建自定义对象的六种方式详解
- 326. Power of Three
- Linux上pip安装cx_Oracle
- Netty初步之Hello World
- opentsdb源码分析--启动
- sqlserver 随机数
- DecorView对象的创建详解
- Java设计模式之单例模式
- oracle 去除重复字符函数
- Linked List Cycle
- cgroup: Control Group 分析 转载
- 关于php接收IE地址栏参数乱码问题
- linux命令集锦(连载)
- android开发壁纸的设置
- Android中自定义样式与View的构造函数中的第三个参数defStyle的意义