天王盖地虎,我是二百五,宝塔镇河妖,这还用你教。话不多说,我叫王菜鸟,我的名言是,菜鸟终究会长大,长大了就成菜鸡了。
简介
我们前面说了Activity能和UI有关具体的原因,那既然知道是ViewRoot是核心,那么就要知道怎么一个核心法,最后战场如何转移,转移后干什么了。而且前面,我们要说明performTraversals干什么,这部分其实不是我的重点,所以我这部分直接看别人的文章,把有用的东西复制过来了,我的重点在后面如何显示在第二部分。
第一个问题
private void performTraversals() {
......
//lp.width和lp.height在创建ViewGroup实例时值为MATCH_PARENT
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
......
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
......
performLayout(lp, desiredWindowWidth, desiredWindowHeight);
......
performDraw();
......
}
//执行rootView的测量
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
try {
//ViewGroup的measure()方法
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
//执行layout操作
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
int desiredWindowHeight) {
mLayoutRequested = false;
mScrollMayChange = true;
mInLayout = true;
final View host = mView;
......
try {
//viewRoot先进行layout
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
mInLayout = false;
//需要layout的子view的数量
int numViewsRequestingLayout = mLayoutRequesters.size();
if (numViewsRequestingLayout > 0) {
//需要layout的子view
ArrayList<View> validLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters,
false);
if (validLayoutRequesters != null) {
//如果view中有调用requestLayout()方法,则说明界面需要刷新
mHandlingLayoutInLayoutRequest = true;
int numValidRequests = validLayoutRequesters.size();
for (int i = 0; i < numValidRequests; ++i) {
final View view = validLayoutRequesters.get(i);
view.requestLayout();
}
//整个viewTree重新measure
measureHierarchy(host, lp, mView.getContext().getResources(),
desiredWindowWidth, desiredWindowHeight);
mInLayout = true;
//整个viewTree重新layout
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
mHandlingLayoutInLayoutRequest = false;
// 再次检查是否有view需要刷新
validLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters, true);
if (validLayoutRequesters != null) {
final ArrayList<View> finalRequesters = validLayoutRequesters;
// Post请求,在下一帧的显示的时候去执行刷新
getRunQueue().post(new Runnable() {
@Override
public void run() {
int numValidRequests = finalRequesters.size();
for (int i = 0; i < numValidRequests; ++i) {
final View view = finalRequesters.get(i);
view.requestLayout();
}
}
});
}
}
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
mInLayout = false;
}
private void performDraw() {
......
try {
draw(fullRedrawNeeded);
} finally {
mIsDrawing = false;
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
......
}
private void draw(boolean fullRedrawNeeded) {
......
if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) {
//使用硬件渲染,比如GPU
mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, this);
} else {
//使用软件渲染
if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
return;
}
}
......
}
/**
* @return true if drawing was successful, false if an error occurred
*/
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
boolean scalingRequired, Rect dirty) {
// Draw with software renderer.
final Canvas canvas;
......
try {
canvas.translate(-xoff, -yoff);
if (mTranslator != null) {
mTranslator.translateCanvas(canvas);
}
canvas.setScreenDensity(scalingRequired ? mNoncompatDensity : 0);
attachInfo.mSetIgnoreDirtyState = false;
mView.draw(canvas);
drawAccessibilityFocusedDrawableIfNeeded(canvas);
} finally {
if (!attachInfo.mSetIgnoreDirtyState) {
attachInfo.mIgnoreDirtyState = false;
}
}
......
return true;
}
也就是说我们终究会调用到View的三个方法
- onMeasure
- onLayout
- onDraw
我们根据上面个图,我们已经调用到measure,layout,draw方法了。
下来就是分别调用
- onMeasure
- onLayout
- onDraw
//final说明该函数不允许被子类override,不需要关注细节
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
......
//widthMeasureSpec,heightMeasureSpec是由parent决定的
onMeasure(widthMeasureSpec, heightMeasureSpec);
......
}
/**
*
* @param widthMeasureSpec horizontal space requirements as imposed by the parent.
*
* @param heightMeasureSpec vertical space requirements as imposed by the parent.
*
* @see #getMeasuredWidth()
* @see #getMeasuredHeight()
* @see #setMeasuredDimension(int, int)
* @see #getSuggestedMinimumHeight()
* @see #getSuggestedMinimumWidth()
* @see android.view.View.MeasureSpec#getMode(int)
* @see android.view.View.MeasureSpec#getSize(int)
*/
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//View类的默认实现,如果自定义view的话,需要我们自己override
//child的宽高有来自parent的widthMeasureSpec、heightMeasureSpec和子的MeasureSpecMode共同决定
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
注意onMeasure(int widthMeasureSpec, int heightMeasureSpec)入参的含义:
- widthMeasureSpec和heightMeasureSpec是parent暴露给child的尺寸
- widthMeasureSpec和heightMeasureSpec是32位的数值,其中高16位为MeasureSpecMode,低16位为MeasureSpecSize
- MeasureSpecMode有三种取值:
- MeasureSpec.EXACTLY:child为精准尺寸(layout_with=mach_parent、24dp的情况)
- MeasureSpec.AT_MOST:child为最大尺寸(layout_with=wrap_content的情况)
- MeasureSpec.UNSPECIFIED:child未指定尺寸
child的尺寸有parent穿过来的widthMeasureSpec、heightMeasureSpec和子的MeasureSpecMode共同决定
//非final类型,子类可以重载
public void layout(int l, int t, int r, int b) {
......
int oldL = mLeft;
int oldT = mTop;
int oldB = mBottom;
int oldR = mRight;
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);
......
}
......
}
//View的onlayout函数默认为空(如果自定义view中需要,可重载)
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
}
/**
* {@inheritDoc}
*/
@Override
public final void layout(int l, int t, int r, int b) {
if (!mSuppressLayout && (mTransition == null || !mTransition.isChangingLayout())) {
if (mTransition != null) {
mTransition.layoutChange(this);
}
//view的layout方法
super.layout(l, t, r, b);
} else {
mLayoutCalledWhileSuppressed = true;
}
}
/**
* 抽象方法,子类必须实现(因为内部必然存在多个view控件,需要layout)
*/
@Override
protected abstract void onLayout(boolean changed,
int l, int t, int r, int b);
/**
* ViewGroup的onLayout()方法都需要子类去实现
* 所以我们来看一下LinearLayout的实现
*/
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (mOrientation == VERTICAL) {
layoutVertical();
} else {
layoutHorizontal();
}
}
//以垂直方向的布局为例
void layoutVertical() {
......
final int count = getVirtualChildCount();
......
//遍历child
for (int i = 0; i < count; i++) {
final View child = getVirtualChildAt(i);
if (child == null) {
childTop += measureNullChild(i);
} else if (child.getVisibility() != GONE) {
final int childWidth = child.getMeasuredWidth();
final int childHeight = child.getMeasuredHeight();
......
//递归child调用layout
setChildFrame(child, childLeft, childTop + getLocationOffset(child),
childWidth, childHeight);
......
}
}
}
private void setChildFrame(View child, int left, int top, int width, int height) {
child.layout(left, top, left + width, top + height);
}
- View.layout方法可被重载,ViewGroup.layout为final的不可重载,ViewGroup.onLayout为abstract的,子类必须重载实现自己的位置逻辑。
- measure操作完成后得到的是对每个View经测量过的measuredWidth和measuredHeight,layout操作 完成之后得到的是对每个View进行位置分配后的mLeft、mTop、mRight、mBottom,这些值都是相对于父View来说的
- 凡是layout_XXX的布局属性基本都针对的是包含子View的ViewGroup的,当对一个没有父容器的View设置相关layout_XXX属性是没有任何意义的。
- 使用View的getWidth()和getHeight()方法来获取View测量的宽高,必须保证这两个方法在onLayout流程之后被调用才能返回有效值。
public void draw(Canvas canvas) {
......
/*
* Draw traversal performs several drawing steps which must be executed
* in the appropriate order:
*
* 1. Draw the background
* 2. If necessary, save the canvas' layers to prepare for fading
* 3. Draw view's content
* 4. Draw children
* 5. If necessary, draw the fading edges and restore layers
* 6. Draw decorations (scrollbars for instance)
*/
// Step 1, draw the background, if needed
......
if (!dirtyOpaque) {
drawBackground(canvas);
}
// skip step 2 & 5 if possible (common case)
......
// Step 2, save the canvas' layers
......
if (drawTop) {
canvas.saveLayer(left, top, right, top + length, null, flags);
}
......
// Step 3, draw the content
if (!dirtyOpaque) onDraw(canvas);
// Step 4, draw the children
dispatchDraw(canvas);
// Step 5, draw the fade effect and restore layers
......
if (drawTop) {
matrix.setScale(1, fadeHeight * topFadeStrength);
matrix.postTranslate(left, top);
fade.setLocalMatrix(matrix);
p.setShader(fade);
canvas.drawRect(left, top, right, top + length, p);
}
......
// Step 6, draw decorations (scrollbars)
onDrawScrollBars(canvas);
......
}
- View的onDraw()方法为空,需要用户自己实现
- 关于draw,官方的注释已经很清楚,我们需要注意的是第四步:递归调用完成viewTree的绘制
- dispatchdraw()为空,需要在子类去实现
/**
* 遍历各种类型的情况的child,并draw
*/
@Override
protected void dispatchDraw(Canvas canvas) {
boolean usingRenderNodeProperties = canvas.isRecordingFor(mRenderNode);
final int childrenCount = mChildrenCount;
final View[] children = mChildren;
......
for (int i = 0; i < childrenCount; i++) {
......
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
more |= drawChild(canvas, child, drawingTime);
}
}
while (transientIndex >= 0) {
......
if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE ||
transientChild.getAnimation() != null) {
more |= drawChild(canvas, transientChild, drawingTime);
}
......
}
if (mDisappearingChildren != null) {
......
for (int i = disappearingCount; i >= 0; i--) {
final View child = disappearingChildren.get(i);
more |= drawChild(canvas, child, drawingTime);
}
}
......
}
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
return child.draw(canvas, this, drawingTime);
}
第二杀Surface
我们在ViewRoot的成员变量中发现了Surface的创建
// These can be accessed by any thread, must be protected with a lock.
// Surface can never be reassigned or cleared (use Surface.clear()).
final Surface mSurface = new Surface();
既然和绘制有关,那我们就去draw中找痕迹
private void draw(boolean fullRedrawNeeded) {
Surface surface = mSurface;
if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
return;
}
}
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
boolean scalingRequired, Rect dirty) {
final Canvas canvas;
final int left = dirty.left;
final int top = dirty.top;
final int right = dirty.right;
final int bottom = dirty.bottom;
canvas = mSurface.lockCanvas(dirty);
mView.draw(canvas);
surface.unlockCanvasAndPost(canvas);
...
我们看到Surface会调用lockCanvas,然后DroceView会调用draw在canvas上面绘画,最后surface会调用unlockCanvasAndPost提交绘画。释放内存
其实我以为到这里就分析的差不多了,但实际上才开始下来我们继续看,因为调用顺序:
performTraversals过程中调用了
relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
boolean insetsPending) throws RemoteException {
float appScale = mAttachInfo.mApplicationScale;
boolean restore = false;
if (params != null && mTranslator != null) {
restore = true;
params.backup();
mTranslator.translateWindowLayout(params);
}
if (params != null) {
if (DBG) Log.d(TAG, "WindowLayout in layoutWindow:" + params);
}
mPendingConfiguration.seq = 0;
//Log.d(TAG, ">>>>>> CALLING relayout");
if (params != null && mOrigWindowType != params.type) {
// For compatibility with old apps, don't crash here.
if (mTargetSdkVersion < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
Slog.w(TAG, "Window type can not be changed after "
+ "the window is added; ignoring change of " + mView);
params.type = mOrigWindowType;
}
}
int relayoutResult = mWindowSession.relayout(
mWindow, mSeq, params,
(int) (mView.getMeasuredWidth() * appScale + 0.5f),
(int) (mView.getMeasuredHeight() * appScale + 0.5f),
viewVisibility, insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0,
mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
mPendingStableInsets, mPendingOutsets, mPendingConfiguration, mSurface);
//Log.d(TAG, "<<<<<< BACK FROM relayout");
if (restore) {
params.restore();
}
if (mTranslator != null) {
mTranslator.translateRectInScreenToAppWinFrame(mWinFrame);
mTranslator.translateRectInScreenToAppWindow(mPendingOverscanInsets);
mTranslator.translateRectInScreenToAppWindow(mPendingContentInsets);
mTranslator.translateRectInScreenToAppWindow(mPendingVisibleInsets);
mTranslator.translateRectInScreenToAppWindow(mPendingStableInsets);
}
return relayoutResult;
}
这个方法的流程图:
看到中间那个节点:
int relayoutResult = mWindowSession.relayout(
mWindow, mSeq, params,
(int) (mView.getMeasuredWidth() * appScale + 0.5f),
(int) (mView.getMeasuredHeight() * appScale + 0.5f),
viewVisibility, insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0,
mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
mPendingStableInsets, mPendingOutsets, mPendingConfiguration, mSurface);
我们还记得mWindowSession的地方吗?
public ViewRootImpl(Context context, Display display) {
mContext = context;
mWindowSession = WindowManagerGlobal.getWindowSession();
public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) {
if (sWindowSession == null) {
try {
InputMethodManager imm = InputMethodManager.getInstance();
IWindowManager windowManager = getWindowManagerService();
sWindowSession = windowManager.openSession(
new IWindowSessionCallback.Stub() {
@Override
public void onAnimatorScaleChanged(float scale) {
ValueAnimator.setDurationScale(scale);
}
},
imm.getClient(), imm.getInputContext());
} catch (RemoteException e) {
Log.e(TAG, "Failed to open window session", e);
}
}
return sWindowSession;
}
}
然后在WMS中:
@Override
public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client,
IInputContext inputContext) {
if (client == null) throw new IllegalArgumentException("null client");
if (inputContext == null) throw new IllegalArgumentException("null inputContext");
Session session = new Session(this, callback, client, inputContext);
return session;
}
所以上面的mWindowSession是个代理类代理的是WMS创建出来的Session
public int relayout(IWindow window, int seq, WindowManager.LayoutParams attrs,
int requestedWidth, int requestedHeight, int viewFlags,
int flags, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
Rect outVisibleInsets, Rect outStableInsets, Rect outsets, Configuration
outConfig,
Surface outSurface) {
int res = mService.relayoutWindow(this, window, seq, attrs,
requestedWidth, requestedHeight, viewFlags, flags,
outFrame, outOverscanInsets, outContentInsets, outVisibleInsets,
outStableInsets, outsets, outConfig, outSurface);
return res;
}
public int relayoutWindow(Session session, IWindow client, int seq,
WindowManager.LayoutParams attrs, int requestedWidth,
int requestedHeight, int viewVisibility, int flags,
Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
Rect outVisibleInsets, Rect outStableInsets, Rect outOutsets, Configuration outConfig,
Surface outSurface) {
WindowState win = windowForClientLocked(session, client, false);
SurfaceControl surfaceControl = winAnimator.createSurfaceLocked();
if (surfaceControl != null) {
outSurface.copyFrom(surfaceControl);
} else {
outSurface.release();
}
我们看到了SurfaceControl这个对象,最后的结果是我们传递进来的Surface outSurface有创建出来的SurfaceControl对象。
也就是说,我们当初在ViewRoot中创建的outSurface传入WMS,现在在这个方法中,我想到这里,就要验证一下,是不是Surface能通过Binder传递
public class Surface implements Parcelable {
哎呦握操可以啊小伙子。
我们ViewRootImpl中的Surface现在持有WMS中创建的SurfaceControl,这是干什么,要想知道干什么就必须要知道SurfaceControl干什么。
public class SurfaceControl {
private static native long nativeCreate(SurfaceSession session, String name,
int w, int h, int format, int flags)
throws OutOfResourcesException;
private static native void nativeRelease(long nativeObject);
private static native void nativeDestroy(long nativeObject);
long mNativeObject; // package visibility only for Surface.java access
上面几个方法这是要创建什么啊,销毁什么啊这是,销毁一个nativeObject根据名称,我们知道销毁的时mNativeObject,这个根据注释时Surface。
哎呦握操,创建Surface的工作跑到native去了,SurfaceControl持有Surface,哎呦握操。
static jlong nativeCreate(JNIEnv* env, jclass clazz, jobject sessionObj,
jstring nameStr, jint w, jint h, jint format, jint flags) {
ScopedUtfChars name(env, nameStr);
sp<SurfaceComposerClient> client(android_view_SurfaceSession_getClient(env, sessionObj));
sp<SurfaceControl> surface = client->createSurface(
String8(name.c_str()), w, h, format, flags);
if (surface == NULL) {
jniThrowException(env, OutOfResourcesException, NULL);
return 0;
}
surface->incStrong((void *)nativeCreate);
return reinterpret_cast<jlong>(surface.get());
}
这个不就是我前段时间写的那些东东吗,我当时是在C++层创建Surface看看他们之间的关系,现在好了,全跑到C++层了。
sp<SurfaceControl> SurfaceComposerClient::createSurface(
const String8& name,
uint32_t w,
uint32_t h,
PixelFormat format,
uint32_t flags)
{
sp<SurfaceControl> sur;
if (mStatus == NO_ERROR) {
sp<IBinder> handle;
sp<IGraphicBufferProducer> gbp;
status_t err = mClient->createSurface(name, w, h, format, flags,
&handle, &gbp);
ALOGE_IF(err, "SurfaceComposerClient::createSurface error %s", strerror(-err));
if (err == NO_ERROR) {
sur = new SurfaceControl(this, handle, gbp);
}
}
return sur;
}
到了这里,我们知道,原来ViewRoot中使用的就是我们通过WMS
WindowState win = windowForClientLocked(session, client, false);
WindowStateAnimator winAnimator = win.mWinAnimator;
SurfaceControl surfaceControl = winAnimator.createSurfaceLocked();
我们看到了吗?
final WindowState windowForClientLocked(Session session, IBinder client,
boolean throwOnError) {
WindowState win = mWindowMap.get(client);
if (win == null) {
RuntimeException ex = new IllegalArgumentException(
"Requested window " + client + " does not exist");
if (throwOnError) {
throw ex;
}
Slog.w(TAG, "Failed looking up window", ex);
return null;
}
return win;
}
final HashMap<IBinder, WindowState> mWindowMap = new HashMap<>();
那我们在哪里put呢?
public int addWindow(Session session, IWindow client, int seq,
WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
InputChannel outInputChannel) {
WindowState win = new WindowState(this, session, client, token,
attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent);
win.attach();
mWindowMap.put(client.asBinder(), win);
}
我们在WindowManagerService.addWindow中,构造中mWinAnimator=null
那到底这个在哪里赋值呢?
我们只能寄希望与attach()方法
void attach() {
if (WindowManagerService.localLOGV) Slog.v(
TAG, "Attaching " + this + " token=" + mToken
+ ", list=" + mToken.windows);
mSession.windowAddedLocked();
}
在Session的windowAddedLocked函数中创建了SurfaceSession对象。
void windowAddedLocked() {
if (mSurfaceSession == null) {
if (WindowManagerService.localLOGV) Slog.v(
WindowManagerService.TAG, "First window added to " + this + ", creating SurfaceSession");
mSurfaceSession = new SurfaceSession();
if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(
WindowManagerService.TAG, " NEW SURFACE SESSION " + mSurfaceSession);
mService.mSessions.add(this);
if (mLastReportedAnimatorScale != mService.getCurrentAnimatorScale()) {
mService.dispatchNewAnimatorScaleLocked(this);
}
}
mNumWindow++;
}
SurfaceSession对象的构造函数就调用了nativeCreate函数,返回值保存在mNativeClient中了。
public SurfaceSession() {
mNativeClient = nativeCreate();
}
static jlong nativeCreate(JNIEnv* env, jclass clazz) {
SurfaceComposerClient* client = new SurfaceComposerClient();
client->incStrong((void*)nativeCreate);
return reinterpret_cast<jlong>(client);
}
然后
void SurfaceComposerClient::onFirstRef() {
sp<ISurfaceComposer> sm(ComposerService::getComposerService());
if (sm != 0) {
sp<ISurfaceComposerClient> conn = sm->createConnection();
if (conn != 0) {
mClient = conn;
mStatus = NO_ERROR;
}
}
}
这里都是之前讲过的
我们成功将战场转移到了native层,也就是说,ViewRoot已经和native的Surface沟通好了。哈哈,这些从Activity到WMS,到native Surface有了一个大概印象了。
我们明天继续细化这个过程。