0 概述
UI显示的3要素是:尺寸大小,位置,内容,在便利过程中分别对应以下三个函数
- performMeasure:大小
- performLayout :位置
- performDraw:绘制
performTraversal的实现主体
performTraversals{
if(needReMeasure){
performMeasure
}
if(needRelayout){
performLayout
}
if(needReDraw){
performDraw
}
}
1.performTraversals中涉及的部分重要变量
mView/host:mView是在ViewRootImpl管理下ViewTree的根节点,函数在执行过程中需要频繁使用这个变量,为了避免意外的篡改,performTraversals函数开头使用了一个本地变量host来指向它,后面的操作全部由这个变量来完成。
mFirst:判断是否是第一次执行performTraversals
mIsInTraversal:当前是否在执行Traversal
mFullRedrawNeeded:某些情况下需要重绘所有界面,这个变量将影响到后续的performDraw
mLayoutRequested:很多情况下,该变量都会被置为true,如View主动发起一个requestLayout,第一次调用performTraversals方法,当前的宽高和期望值不符
mWinFrame:由WMS计算得到的Frame
mFitSystemWindowInsets:设置内边距的初始值,以容纳包括状态栏输入法在内的系统窗口
mAttachInfo:当View被attached到window时,用于记录一系列相关信息,
mWidth/mHeight:通过主动请求WMS计算后得到的值,要注意和mDesiredWidth/mDesiredHeight的区别,mDesired这组值和mWinFrame保持一致,表示WMS期望的应用程序的宽和高
sWindowSession:ViewRoot与WMS的通信接口
2.三大步骤
2.1 performMeasure
private void performTraversals() {
if (mFirst || windowShouldResize || insetsChanged ||
viewVisibilityChanged || params != null) {
//层级1
if (!mStopped) {//层级2
boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
(relayoutResult&WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE) != 0);
if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()
|| mHeight != host.getMeasuredHeight() || contentInsetsChanged) {
//层级3
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
// Ask host how big it wants to be
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
private static int getRootMeasureSpec(int windowSize, int rootDimension) {
int measureSpec;
switch (rootDimension) {
case ViewGroup.LayoutParams.MATCH_PARENT:
measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
break;
case ViewGroup.LayoutParams.WRAP_CONTENT:
measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
break;
default:
measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
break;
}
return measureSpec;
}
层级1:
mFirst:第一次调用performTraversals
windowShouldResize:windowSize确定需要改变
viewVisibilityChanged:ViewRootImpl记录的Visibility和ViewTree根节点所记录的值产生差异时,说明可见性有变化;或者需要为这个窗口产生一个Surface,那么这个变量为true。
params:ViewTree的LayoutParams,应用程序可以通过多种方法来设置。
层级2:
mStopped:当Activity处于stop状态时,其对应的Window也同样会被stopped
层级3:
focusChangedDueToTouchMode:TouchMode是否会引起foucus变化
mWdith!=host.getMeasuredWidth():getMeasuredWidth是View经过onMeasure测量出来的宽度
mHeight != host.getMeasuredHeight():getMeasuredHeight是View经过onMeasure后测量出来的高度
contentInsetsChanged:
2.2 performMeasure
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
try {
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
} finally {
}
}
ViewRootImpl将控制权转给View树的根元素,真正的Traversal开始。真正的测量工作也是在onMeasure执行
2.3 onMeasure
widthMeasureSpec,heightMeasureSpec是该View对象的上一级父对象要求的宽高,层层而下传递到ViewTree中的各个元素,ViewTree的根元素mDecor的这两个值是由ViewRootImpl传入的,后者通过MeasureSpec.makeMeasureSpec来生成符合要求的spec
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
//measure调用onMeasur
onMeasure(widthMeasureSpec, heightMeasureSpec);
}
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
2.4Spec的格式如下
mode(高二位)+size
2.4.1 mode有三种
UNSPECIFIED=0;父对象没有强制要求子对象必须遵循哪些约束
EXACTLY:父对象要求子对象必须严格按照它给定的值来约束自己
AT_MOST:子对象可以自行选择给定范围内的值
2.5 FrameLayout的onMeasure分析
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int count = getChildCount();//子对象个数
//判断父对象的mode要求
final boolean measureMatchParentChildren =
MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||
MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
mMatchParentChildren.clear();
//所有子对象中测量到的最大高度和宽度
int maxHeight = 0;
int maxWidth = 0;
int childState = 0;
for (int i = 0; i < count; i++) {//循环处理所有子对象
final View child = getChildAt(i);//获取一个子对象
if (mMeasureAllChildren || child.getVisibility() != GONE) {//需要测量吗?
//step2:考虑padding和margin生成新的spec
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
//step3 获取最大值
maxWidth = Math.max(maxWidth,
child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
maxHeight = Math.max(maxHeight,
child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
childState = combineMeasuredStates(childState, child.getMeasuredState());
}
}
//step4 综合考虑其他因素
// 检查padding
maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground();
maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground();
// 检查建议的最小宽高值
maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
// 检查foreground北京宽高值
final Drawable drawable = getForeground();
if (drawable != null) {
maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());
maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());
}
//记录结果
setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
resolveSizeAndState(maxHeight, heightMeasureSpec,
childState << MEASURED_HEIGHT_STATE_SHIFT));
count = mMatchParentChildren.size();
if (count > 1) {
for (int i = 0; i < count; i++) {
final View child = mMatchParentChildren.get(i);
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
int childWidthMeasureSpec;
int childHeightMeasureSpec;
if (lp.width == LayoutParams.MATCH_PARENT) {
childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth() -
getPaddingLeftWithForeground() - getPaddingRightWithForeground() -
lp.leftMargin - lp.rightMargin,
MeasureSpec.EXACTLY);
} else {
childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
getPaddingLeftWithForeground() + getPaddingRightWithForeground() +
lp.leftMargin + lp.rightMargin,
lp.width);
}
if (lp.height == LayoutParams.MATCH_PARENT) {
childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight() -
getPaddingTopWithForeground() - getPaddingBottomWithForeground() -
lp.topMargin - lp.bottomMargin,
MeasureSpec.EXACTLY);
} else {
childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
getPaddingTopWithForeground() + getPaddingBottomWithForeground() +
lp.topMargin + lp.bottomMargin,
lp.height);
}
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
}
}
View只有padding,ViewGroup中各个子view之间是margin##
2.2 performLayout
performTraversals(){
final boolean didLayout = layoutRequested && !mStopped;
if (didLayout) {
performLayout(lp, desiredWindowWidth, desiredWindowHeight);
}
2.2.1 ViewRootImpl的performLayout
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
int desiredWindowHeight) {
final View host = mView;
try {
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
//......
}
}
2.2.2 View的layout方法
l,t,r,b表示该View对象与父对象边框的距离,如果changed为true,表名本次设置的边距与上一次相比发生了变化,或者flags强制要求layout,那么就调用onLayout。View中onLayout是空的,ViewGroup需要重载ViewGroup的onLayout方法
public void layout(int l, int t, int r, int b) {
boolean changed = isLayoutModeOptical(mParent) ?
setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
onLayout(changed, l, t, r, b);
}
mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
}
2.2.3 FrameLayout的onLayout方法
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
final int count = getChildCount();
final int parentLeft = getPaddingLeftWithForeground();
final int parentRight = right - left - getPaddingRightWithForeground();
final int parentTop = getPaddingTopWithForeground();
final int parentBottom = bottom - top - getPaddingBottomWithForeground();
mForegroundBoundsChanged = true;
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != GONE) {
//child设置的layout属性
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
//child在measure中测量到的宽度和高度
final int width = child.getMeasuredWidth();
final int height = child.getMeasuredHeight();
int gravity = lp.gravity;
final int layoutDirection = getLayoutDirection();
final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
//childLeft和childTop的计算
switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
case Gravity.LEFT:
childLeft = parentLeft + lp.leftMargin;
break;
case Gravity.CENTER_HORIZONTAL:
childLeft = parentLeft + (parentRight - parentLeft - width) / 2 +
lp.leftMargin - lp.rightMargin;
break;
case Gravity.RIGHT:
childLeft = parentRight - width - lp.rightMargin;
break;
default://NOTICE
childLeft = parentLeft + lp.leftMargin;
}
switch (verticalGravity) {
case Gravity.TOP:
childTop = parentTop + lp.topMargin;
break;
case Gravity.CENTER_VERTICAL:
childTop = parentTop + (parentBottom - parentTop - height) / 2 +
lp.topMargin - lp.bottomMargin;
break;
case Gravity.BOTTOM:
childTop = parentBottom - height - lp.bottomMargin;
break;
default:
childTop = parentTop + lp.topMargin;
}
child.layout(childLeft, childTop, childLeft + width, childTop + height);
}
}
}
TU 11-13 childLeft计算过程中涉及到的变量解释
2.3 performDraw
对象的layout确定之后,它才能在此基础上draw,函数performDraw是遍历流程中最后被调用的,将在画板上产生UI数据,然后在适当的时机由SurfaceFlinger进行整合,最终显示到屏幕上,绘制UI的核心如下:
- Surface:Surface是画板
- 绘制图形的方式:软件绘制 或 硬件绘制
- ViewTree各个元素的协调关系
2.3.1 drawSoftWare
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int yoff,
boolean scalingRequired, Rect dirty) {
// Draw with software renderer.
Canvas canvas;
try {
//区域
int left = dirty.left;
int top = dirty.top;
int right = dirty.right;
int bottom = dirty.bottom;
//锁定画布
canvas = mSurface.lockCanvas(dirty);
canvas.setDensity(mDensity);
} catch (Surface.OutOfResourcesException e) {
} catch (IllegalArgumentException e) {
}
try {
if (!canvas.isOpaque() || yoff != 0) {
canvas.drawColor(0, PorterDuff.Mode.CLEAR);
}
dirty.setEmpty();
mIsAnimating = false;
attachInfo.mDrawingTime = SystemClock.uptimeMillis();
mView.mPrivateFlags |= View.PFLAG_DRAWN;
try {
canvas.translate(0, -yoff);
if (mTranslator != null) {
mTranslator.translateCanvas(canvas);
}
canvas.setScreenDensity(scalingRequired ? mNoncompatDensity : 0);
attachInfo.mSetIgnoreDirtyState = false;
mView.draw(canvas);//真正的画画操作
//draw完成后的Canvas信息通过Surface提交给SurfaceFlinger,
//并由SurfaceFlinger统一合成渲染到FrameBuffer中,才能最终把界面显示到屏幕
drawAccessibilityFocusedDrawableIfNeeded(canvas);
} finally {
}
} finally {
try {
surface.unlockCanvasAndPost(canvas);
} catch (IllegalArgumentException e) {
}
}
return true;
}
AIDL
参考
http://www.infoq.com/cn/news/2015/09/android-weekly-react-native
http://blog.csdn.net/lmj623565791/article/details/38461079
http://blog.csdn.net/lmj623565791/article/details/47017485
http://www.infoq.com/cn/news/2015/07/android-weekly-m
http://www.race604.com/communicate-with-remote-service-1/
http://www.woaitqs.cc/android/2016/05/30/android-binder-proxy-and-token
http://m.blog.csdn.net/article/details?id=51243539
http://blog.csdn.net/bjp000111/article/details/51919640
http://www.jianshu.com/p/39c1c2bf39c6
http://blog.csdn.net/luoyanglizi/article/details/51958091
http://www.cnblogs.com/whoislcj/p/5509868.html
http://www.jianshu.com/p/e642db926c50
http://www.jianshu.com/p/a5c73da2e9be
http://mp.weixin.qq.com/s?__biz=MjM5NzA0ODU0NA==&mid=2247483695&idx=1&sn=a2fca99eb3c5f1bd81cbf345fb2e2adb&scene=23&srcid=0706huZ15GukdCiE1hBGsLKR#rd
http://blog.csdn.net/luoyanglizi/article/details/51980630
http://blog.csdn.net/luoyanglizi/article/details/52029091
http://blog.csdn.net/ankas/article/details/52625273
http://www.jianshu.com/p/cfb1d2a109a2
http://blog.csdn.net/ethanchiu/article/details/19488423
https://github.com/manishkpr/Android-AIDL-Example
https://github.com/race604/AIDLService-sample
https://github.com/afollestad/aidl-example