上一篇文章講了SurfaceView創(chuàng)建Surface的過(guò)程,接下來(lái)我們來(lái)看下SurfaceView是如何"挖洞"的。說(shuō)起"挖洞",本質(zhì)上其實(shí)就是設(shè)置一塊區(qū)域,在最后繪制的時(shí)候不要對(duì)這塊區(qū)域進(jìn)行繪制即可
不過(guò)在講"挖洞"之前,我們首先來(lái)思考一個(gè)問(wèn)題:為什么要"挖洞"呢?我們先來(lái)看下面這段代碼:
protected void updateSurface() {
...代碼省略...
mSurfaceControl.setLayer(mSubLayer);
...代碼省略...
}
還是在SurfaceView的updateSurface方法里。mSubLayer默認(rèn)是APPLICATION_MEDIA_SUBLAYER, mSurfaceControl.setLayer(mSubLayer)就是講mSubLayer傳給SurfaceFlinger。如果碰到有兩個(gè)SurfaceView的時(shí)候我們通常會(huì)讓其中一個(gè)需要顯示在上面的surfaceView調(diào)用setZOrderMediaOverlay(true)。
public void setZOrderMediaOverlay(boolean isMediaOverlay) {
mSubLayer = isMediaOverlay
? APPLICATION_MEDIA_OVERLAY_SUBLAYER : APPLICATION_MEDIA_SUBLAYER;
}
APPLICATION_MEDIA_OVERLAY_SUBLAYER值是-1,APPLICATION_MEDIA_SUBLAYER的值是-2,mSubLayer的值越大,就越顯示在上面。所以SurfaceView會(huì)處于當(dāng)前宿主窗口的下方。因此才需要將SurfaceView所對(duì)應(yīng)的那塊區(qū)域設(shè)置成透明才能讓SurfaceView顯示出來(lái)。
"挖洞"首先還是從ViewRootImpl#performTraversals開始說(shuō)起:
private void performTraversals() {
...代碼省略...
if (mFirst) {
...代碼省略...
host.dispatchAttachedToWindow(mAttachInfo, 0);
mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(true);
dispatchApplyInsets(host);
//Log.i(mTag, "Screen on initialized: " + attachInfo.mKeepScreenOn);
}
...代碼省略...
mFirst = false;
...代碼省略....
}
mFirst在初始化的時(shí)候是true,只有第一次調(diào)用performTravesals的時(shí)候才會(huì)執(zhí)行里面的代碼,執(zhí)行一次以后,mFirst就會(huì)設(shè)置為false。
這段代碼主要看host.dispatchAttachedToWindow(mAttachInfo, 0);這個(gè)host我們?cè)谏弦黄仓v到過(guò)就是DecorView。而DecorView是繼承ViewGroup的,另外本身并沒(méi)有重寫dispatchAttachedToWindow方法,所以我們直接看ViewGroup的#dispatchAttachedToWindow方法即可:
@Override
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
mGroupFlags |= FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;
super.dispatchAttachedToWindow(info, visibility);
mGroupFlags &= ~FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;
final int count = mChildrenCount;
final View[] children = mChildren;
for (int i = 0; i < count; i++) {
final View child = children[i];
child.dispatchAttachedToWindow(info,
combineVisibility(visibility, child.getVisibility()));
}
final int transientCount = mTransientIndices == null ? 0 : mTransientIndices.size();
for (int i = 0; i < transientCount; ++i) {
View view = mTransientViews.get(i);
view.dispatchAttachedToWindow(info,
combineVisibility(visibility, view.getVisibility()));
}
}
這里面主要就是調(diào)用了View的dispatchAttachedToWindow,而View的dispatchAttachedToWindow里面又會(huì)調(diào)用onAttachToWindow方法,而我們本篇的主角是SurfaceView,所以這里就直接給出SurfaceView的onAttachToWindow方法:
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
getViewRootImpl().addWindowStoppedCallback(this);
mWindowStopped = false;
mViewVisibility = getVisibility() == VISIBLE;
updateRequestedVisibility();
mAttachedToWindow = true;
mParent.requestTransparentRegion(SurfaceView.this);
if (!mGlobalListenersAdded) {
ViewTreeObserver observer = getViewTreeObserver();
observer.addOnScrollChangedListener(mScrollChangedListener);
observer.addOnPreDrawListener(mDrawListener);
mGlobalListenersAdded = true;
}
}
我們看到這里面執(zhí)行了 mParent.requestTransparentRegion(SurfaceView.this);這么一句代碼,這句代碼看名字應(yīng)該就是請(qǐng)求父View測(cè)量一下當(dāng)前SurfaceView的位置大小。那就繼續(xù)看下去來(lái)印證下是否是這樣子的,mParent就是View的父View即對(duì)應(yīng)的ViewGroup,
@Override
public void requestTransparentRegion(View child) {
if (child != null) {
child.mPrivateFlags |= View.PFLAG_REQUEST_TRANSPARENT_REGIONS;
if (mParent != null) {
mParent.requestTransparentRegion(this);
}
}
}
ViewGroup里面依然會(huì)調(diào)用mParent.requestTransparentRegion方法,那么最終肯定會(huì)到頂層的ViewGroup也就是DecorView,它的mParent便是ViewRootImpl,所以重新進(jìn)入ViewRootImpl看看:
@Override
public void requestTransparentRegion(View child) {
// the test below should not fail unless someone is messing with us
checkThread();
if (mView == child) {
mView.mPrivateFlags |= View.PFLAG_REQUEST_TRANSPARENT_REGIONS;
// Need to make sure we re-evaluate the window attributes next
// time around, to ensure the window has the correct format.
mWindowAttributesChanged = true;
mWindowAttributesChangesFlag = 0;
requestLayout();
}
}
ViewRootImpl做了兩件事:
- 將mView的mPrivateFlags邏輯或上View.PFLAG_REQUEST_TRANSPARENT_REGIONS,相當(dāng)于給mPrivateFlags設(shè)置了PFLAG_REQUEST_TRANSPARENT_REGIONS的屬性
- 第二件事就是requestLayout,這個(gè)方法最終會(huì)重新調(diào)用ViewRootImpl的performTraversals方法??磥?lái)"挖洞"過(guò)程估計(jì)也是在這個(gè)方法里面了。
private void performTraversals() {
...代碼省略...
//當(dāng)執(zhí)行RequestLayout的時(shí)候,layoutRequested參數(shù)為true,由于當(dāng)前窗口沒(méi)有被關(guān)閉,因此mStopped必然是false,所以didLayout是true
final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
boolean triggerGlobalLayoutListener = didLayout
|| mAttachInfo.mRecomputeGlobalAttributes;
if (didLayout) {
//這里會(huì)執(zhí)行ViewGroup的onLayout方法
performLayout(lp, mWidth, mHeight);
// By this point all views have been sized and positioned
// We can compute the transparent area
//對(duì)應(yīng)了之前的requestTransparentRegion方法,將PFLAG_REQUEST_TRANSPARENT_REGIONS賦值給了mPrivateFlags,所以此處條件會(huì)進(jìn)入。
if ((host.mPrivateFlags & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) != 0) {
// start out transparent
// TODO: AVOID THAT CALL BY CACHING THE RESULT?
host.getLocationInWindow(mTmpLocation);
mTransparentRegion.set(mTmpLocation[0], mTmpLocation[1],
mTmpLocation[0] + host.mRight - host.mLeft,
mTmpLocation[1] + host.mBottom - host.mTop);
host.gatherTransparentRegion(mTransparentRegion);
if (mTranslator != null) {
mTranslator.translateRegionInWindowToScreen(mTransparentRegion);
}
if (!mTransparentRegion.equals(mPreviousTransparentRegion)) {
mPreviousTransparentRegion.set(mTransparentRegion);
mFullRedrawNeeded = true;
// reconfigure window manager
try {
mWindowSession.setTransparentRegion(mWindow, mTransparentRegion);
} catch (RemoteException e) {
}
}
}
if (DBG) {
System.out.println("======================================");
System.out.println("performTraversals -- after setFrame");
host.debug();
}
}
...代碼省略...
}
這里(host.mPrivateFlags & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) != 0條件滿足,所以會(huì)進(jìn)入里面,然后調(diào)用host.gatherTransparentRegion。host就是DecorView對(duì)象。
@Override
public boolean gatherTransparentRegion(Region region) {
boolean statusOpaque = gatherTransparentRegion(mStatusColorViewState, region);
boolean navOpaque = gatherTransparentRegion(mNavigationColorViewState, region);
boolean decorOpaque = super.gatherTransparentRegion(region);
// combine bools after computation, so each method above always executes
return statusOpaque || navOpaque || decorOpaque;
}
DecorView會(huì)調(diào)用父類ViewGroup的gatherTransparentRegion。如果沒(méi)猜錯(cuò),ViewGroup應(yīng)該不會(huì)做多少處理直接分發(fā)給對(duì)應(yīng)的子View做相應(yīng)的處理:
@Override
public boolean gatherTransparentRegion(Region region) {
// If no transparent regions requested, we are always opaque.
final boolean meOpaque = (mPrivateFlags & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) == 0;
if (meOpaque && region == null) {
// The caller doesn't care about the region, so stop now.
return true;
}
super.gatherTransparentRegion(region);
// Instead of naively traversing the view tree, we have to traverse according to the Z
// order here. We need to go with the same order as dispatchDraw().
// One example is that after surfaceView punch a hole, we will still allow other views drawn
// on top of that hole. In this case, those other views should be able to cut the
// transparent region into smaller area.
final int childrenCount = mChildrenCount;
boolean noneOfTheChildrenAreTransparent = true;
if (childrenCount > 0) {
final ArrayList<View> preorderedList = buildOrderedChildList();
final boolean customOrder = preorderedList == null
&& isChildrenDrawingOrderEnabled();
final View[] children = mChildren;
for (int i = 0; i < childrenCount; i++) {
final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
if (!child.gatherTransparentRegion(region)) {
noneOfTheChildrenAreTransparent = false;
}
}
}
if (preorderedList != null) preorderedList.clear();
}
return meOpaque || noneOfTheChildrenAreTransparent;
}
確實(shí)ViewGroup里面就是做了Child View的遍歷,然后對(duì)每個(gè)View做gatherTransparentRegion處理,然后計(jì)算出對(duì)應(yīng)需要透明的區(qū)域。本文主角是SurfaceView,所以直接關(guān)注SurfaceView的gatherTransparentRegion即可:
@Override
public boolean gatherTransparentRegion(Region region) {
if (isAboveParent() || !mDrawFinished) {
return super.gatherTransparentRegion(region);
}
boolean opaque = true;
if ((mPrivateFlags & PFLAG_SKIP_DRAW) == 0) {
// this view draws, remove it from the transparent region
opaque = super.gatherTransparentRegion(region);
} else if (region != null) {
int w = getWidth();
int h = getHeight();
if (w>0 && h>0) {
getLocationInWindow(mLocation);
// otherwise, punch a hole in the whole hierarchy
int l = mLocation[0];
int t = mLocation[1];
region.op(l, t, l+w, t+h, Region.Op.UNION);
}
}
if (PixelFormat.formatHasAlpha(mRequestedFormat)) {
opaque = false;
}
return opaque;
}
看到這里獲取了SurfaceView的寬高,然后計(jì)算除了SurfaceView在屏幕上的具體位置,然后對(duì)region進(jìn)行重新賦值。
所以gatherTransparentRegion主要是為了計(jì)算出需要設(shè)置透明區(qū)域的范圍。后續(xù)我們需要告訴SurfaceFlinger這塊透明區(qū)域的具體位置。那么我們?cè)俅位氐絍iewRootImpl去。
if (!mTransparentRegion.equals(mPreviousTransparentRegion)) {
mPreviousTransparentRegion.set(mTransparentRegion);
mFullRedrawNeeded = true;
// reconfigure window manager
try {
mWindowSession.setTransparentRegion(mWindow, mTransparentRegion);
} catch (RemoteException e) {
}
}
我們看到在剛才調(diào)用gatherTransparentRegion方法的條件里面,有上面這段代碼,當(dāng)當(dāng)前需要設(shè)置的透明區(qū)域不跟之前的相同時(shí),通過(guò)mWindowSession的setTransparentRegion方法進(jìn)行設(shè)置。mWindowSession是一個(gè)IWindowSession接口。Session類實(shí)現(xiàn)了IWindowSession,是一個(gè)遠(yuǎn)程的進(jìn)程,通過(guò)Binder進(jìn)行通訊
public class Session extends IWindowSession.Stub
implements IBinder.DeathRecipient {
@Override
public void setTransparentRegion(IWindow window, Region region) {
mService.setTransparentRegionWindow(this, window, region);
}
}
Session里面沒(méi)有做特殊的處理,直接交給了mService處理,此處的mService 便是WindowManagerService。
void setTransparentRegionWindow(Session session, IWindow client, Region region) {
long origId = Binder.clearCallingIdentity();
try {
synchronized (mWindowMap) {
WindowState w = windowForClientLocked(session, client, false);
if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
"transparentRegionHint=" + region, false);
if ((w != null) && w.mHasSurface) {
w.mWinAnimator.setTransparentRegionHintLocked(region);
}
}
} finally {
Binder.restoreCallingIdentity(origId);
}
}
然后調(diào)用WindowStateAnimator的setTransparentRegionHintLocked方法
class WindowStateAnimator {
void setTransparentRegionHintLocked(final Region region) {
if (mSurfaceController == null) {
Slog.w(TAG, "setTransparentRegionHint: null mSurface after mHasSurface true");
return;
}
mSurfaceController.setTransparentRegionHint(region);
}
}
class WindowSurfaceController {
void setTransparentRegionHint(final Region region) {
if (mSurfaceControl == null) {
Slog.w(TAG, "setTransparentRegionHint: null mSurface after mHasSurface true");
return;
}
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION setTransparentRegion");
mService.openSurfaceTransaction();
try {
mSurfaceControl.setTransparentRegionHint(region);
} finally {
mService.closeSurfaceTransaction();
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
"<<< CLOSE TRANSACTION setTransparentRegion");
}
}
}
最終調(diào)用了 mSurfaceControl.setTransparentRegionHint(region);,這個(gè)mSurfaceControl就是SurfaceControlWithBackground,是不是覺(jué)得有點(diǎn)熟悉?沒(méi)錯(cuò),就是上一篇在創(chuàng)建Surface的時(shí)候也是這個(gè)類在操作。
@Override
public void setTransparentRegionHint(Region region) {
super.setTransparentRegionHint(region);
if (mBackgroundControl == null) {
return;
}
mBackgroundControl.setTransparentRegionHint(region);
}
而SurfaceControlWithBackground則是調(diào)用了mBackgroundControl.setTransparentRegionHint(region);,之后會(huì)調(diào)用nativeSetTransparentRegionHint方法,看這名字就是要進(jìn)入C++層了,那我們就進(jìn)去探探究竟吧。
frameworks/base/core/jni/android_view_SurfaceControl.cpp
static void nativeSetTransparentRegionHint(JNIEnv* env, jclass clazz, jlong transactionObj,
jlong nativeObject, jobject regionObj) {
...代碼省略...
{
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
transaction->setTransparentRegionHint(ctrl, reg);
}
}
這里獲取了SurfaceComposerClient對(duì)象,然后調(diào)用了SurfaceComposerClient的setTransparentRegionHint方法。
frameworks/native/libs/gui/SurfaceComposerClient.cpp
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setTransparentRegionHint(
const sp<SurfaceControl>& sc,
const Region& transparentRegion) {
layer_state_t* s = getLayerState(sc);
if (!s) {
mStatus = BAD_INDEX;
return *this;
}
s->what |= layer_state_t::eTransparentRegionChanged;
s->transparentRegion = transparentRegion;
return *this;
}
最終賦值給了SurfaceFlinger。下面給出對(duì)應(yīng)的時(shí)序圖:

至此SurfaceView的"挖洞"過(guò)程結(jié)束,那么下一篇就開始講SurfaceView的繪制的過(guò)程了。