天王蓋地虎,我是二百五,寶塔鎮河妖,這還用你教。話不多說,我叫王菜鳥,我的名言是,菜鳥終究會長大,長大了就成菜雞了。
簡介
我們前面說了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有了一個大概印象了。
我們明天繼續細化這個過程。