Android View绘制机制
说到View 绘制 必须要提到
Window WindowManager WindowManagerService ViewRootImpl View
这些之间关系搞不清楚,很难往深理解。以下是一些介绍
1.Window: 这是一个抽象类,表示一个窗口,具体实现有PhoneWindow,一般一个Activity对应一个PhoneWindow,Window通俗的说一块显示区域,有层级之分,系统Window层级最高。
2.WindowManager: 对于Window的操作通过WindowManager进行IPC操作通知
WindowManagerService完成,有三个方法addView updateViewLayout removeView。
4.WindowManagerService :这是一个系统服务,运行在一个独立的进程里,负责管理window的层级,不负责绘制。
5.ViewRootImpl:这个大伙都知道,负责绘制View,联系WindowManager和View。
6.View:绘制主体,不能单独被绘制,需要依附在Window上,才能显示。
总结流程就是:WindowManager使用IPC通知WindowManagerService进行添加Window,WindowManagerService负责管理Window层级,当添加完后,通过ViewRootImpl 测量 布局 绘制 View(描述绘制对象),
View会被绘制到画布上。
最后看下ViewRootImpl 的官方介绍然后了解下是怎么绘制View的
* The top of a view hierarchy, implementing the needed protocol between View
* and the WindowManager. This is for the most part an internal implementation
* 在View和WindowManager之间实现了必须的协议,view的顶层
先看下ViewRootImpl 是什么时候创建的呢?
在前面一篇文章 App的启动流程 得知
收到LAUNCH_ACTIVITY请求,然后去执行handleLaunchActivity->handleResumeActivity
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
.....
// 获取activity的window
r.window = r.activity.getWindow();
// 顶层DecorView 是在window里创建的
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
// 获取WindowManager
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (r.mPreserveWindow) {
a.mWindowAdded = true;
r.mPreserveWindow = false;
// Normally the ViewRoot sets up callbacks with the Activity
// in addView->ViewRootImpl#setView. If we are instead reusing
// the decor view we have to notify the view root that the
// callbacks may have changed.
ViewRootImpl impl = decor.getViewRootImpl();
if (impl != null) {
impl.notifyChildRebuilt();
}
}
if (a.mVisibleFromClient) {
if (!a.mWindowAdded) {
a.mWindowAdded = true;
// 这里面进行VIew的绘制
wm.addView(decor, l);
} else {
a.onWindowAttributesChanged(l);
}
........
}
这段代码可以知道activity包含Window, 实现类PhoneWindow 中可以获取DecorView,
然后通过WindowManager.addView(decorview,params) 把view添加到这个Window作为根节点,调用WindowManagerGlobal的addView。
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
if (display == null) {
throw new IllegalArgumentException("display must not be null");
}
if (!(params instanceof WindowManager.LayoutParams)) {
throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
}
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
if (parentWindow != null) {
parentWindow.adjustLayoutParamsForSubWindow(wparams);
} else {
// If there's no parent, then hardware acceleration for this view is
// set from the application's hardware acceleration setting.
final Context context = view.getContext();
if (context != null
&& (context.getApplicationInfo().flags
& ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
}
}
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
// Start watching for system property changes.
if (mSystemPropertyUpdater == null) {
mSystemPropertyUpdater = new Runnable() {
@Override public void run() {
synchronized (mLock) {
for (int i = mRoots.size() - 1; i >= 0; --i) {
mRoots.get(i).loadSystemProperties();
}
}
}
};
SystemProperties.addChangeCallback(mSystemPropertyUpdater);
}
int index = findViewLocked(view, false);
if (index >= 0) {
if (mDyingViews.contains(view)) {
// Don't wait for MSG_DIE to make it's way through root's queue.
mRoots.get(index).doDie();
} else {
throw new IllegalStateException("View " + view
+ " has already been added to the window manager.");
}
// The previous removeView() had not completed executing. Now it has.
}
// If this is a panel window, then find the window it is being
// attached to for future reference.
if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
final int count = mViews.size();
for (int i = 0; i < count; i++) {
if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
panelParentView = mViews.get(i);
}
}
}
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
// do this last because it fires off messages to start doing things
try {
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
}
}
接着会调用ViewRootImpl的setView
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
requestLayout();
// view measure layout draw之后通过IPC调用
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
}
然后调用requestLayout()进行绘制。
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
scheduleTraversals->doTraversal()->performTraversals()
performTraversals()代码奇长无比,近千行代码,主体代码已经有人总结了是以下几个方法
1.performMeasure(childWidthMeasureSpec, childHeightMeasureSpec)
2.performLayout(lp, mWidth, mHeight)
3.performDraw()
这三个方法分别对应view的measure layout draw这次不解释源码了 说下主题思路。
measure 测量大小 与之相关的最重要的是WidthMeasureSpec 和 HeightMeasureSpec 这两个参数由宽高和measue模式组成,参数来源于父组件,顶层view的参数来源于getRootMeasureSpec,默认屏幕宽高,MeasureSpec.EXACTLY组成。
layout 主要在ViewGroup上自定义布局,单个View默认左上角开始。
draw 主要用于单个View Canvas绘制自己需要的内容,ViewGroup一般就绘制背景色和子view等等简单操作。
最后需要调用 WindowSessioin的addToDisplay进行IPC通信,最后由WindowManagerService处理。