SurfaceView源码解读

surfaceView的建立

整体流程

SurfaveView_Create

performTraversals

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
private void performTraversals() {
boolean viewVisibilityChanged = mViewVisibility != viewVisibility
|| mNewSurfaceNeeded;
......
Rect frame = mWinFrame;
//default is true,indicate whether the view is first attached to the windows.
// if mFirst is true ,we should inital the attchinfo,and then dispatch it to its children
if (mFirst) {
.........
host.dispatchAttachedToWindow(mAttachInfo, 0);
mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(true);
dispatchApplyInsets(host);
//Log.i(TAG, "Screen on initialized: " + attachInfo.mKeepScreenOn);
} else {
.......
}

if (viewVisibilityChanged) {
mAttachInfo.mWindowVisibility = viewVisibility;
host.dispatchWindowVisibilityChanged(viewVisibility);
......
}
......

}

dispatchAttachToWindow

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/**
* @param info the {@link android.view.View.AttachInfo} to associated with
* this view
notify FOUR:
1.onAttachedToWindow()
2.OnAttachStateChangeListener.onViewAttachToWindow()
3.onWindowVisibilityChanged()
4.onVisibilityChanged()
*/
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
//System.out.println("Attached! " + this);
mAttachInfo = info;
.......
onAttachedToWindow();

ListenerInfo li = mListenerInfo;
final CopyOnWriteArrayList<OnAttachStateChangeListener> listeners =
li != null ? li.mOnAttachStateChangeListeners : null;
if (listeners != null && listeners.size() > 0) {
// NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
// perform the dispatching. The iterator is a safe guard against listeners that
// could mutate the list by calling the various add/remove methods. This prevents
// the array from being modified while we iterate it.
for (OnAttachStateChangeListener listener : listeners) {
listener.onViewAttachedToWindow(this);
}
}

int vis = info.mWindowVisibility;
if (vis != GONE) {
onWindowVisibilityChanged(vis);
}

// Send onVisibilityChanged directly instead of dispatchVisibilityChanged.
// As all views in the subtree will already receive dispatchAttachedToWindow
// traversing the subtree again here is not desired.
onVisibilityChanged(this, visibility);

......
}

onAttachToWindow

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
1.restore the session who communicate with windowManagerServices
2.do ViewTreeObserver notify
**/
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
mParent.requestTransparentRegion(this);
//mSession to communication with the window manager Services
//IWindowSession
mSession = getWindowSession();
mLayout.token = getWindowToken();
mLayout.setTitle("SurfaceView");
mViewVisibility = getVisibility() == VISIBLE;
if (!mGlobalListenersAdded) {
ViewTreeObserver observer = getViewTreeObserver();
observer.addOnScrollChangedListener(mScrollChangedListener);
observer.addOnPreDrawListener(mDrawListener);
mGlobalListenersAdded = true;
}
}

dispatchWindowVisibilityChanged

1
2
3
4
5
6
7
8
9
10
11
12
/**
* Dispatch a view visibility change down the view hierarchy.
* ViewGroups should override to route to their children.
* @param changedView The view whose visibility changed. Could be 'this' or
* an ancestor view.
* @param visibility The new visibility of changedView: {@link #VISIBLE},
* {@link #INVISIBLE} or {@link #GONE}.
*/
protected void dispatchVisibilityChanged(@NonNull View changedView,
@Visibility int visibility) {
onVisibilityChanged(changedView, visibility);
}

onVisibilityChanged

1
2
3
4
5
6
7
@Override
protected void onWindowVisibilityChanged(int visibility) {
super.onWindowVisibilityChanged(visibility);
mWindowVisibility = visibility == VISIBLE;
mRequestedVisible = mWindowVisibility && mViewVisibility;
updateWindow(false, false);
}

updateWindow

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
/**
*
1.use windowsManagerServices session to addToDisplayWithoutInputChannel
(add a new windowState to windowsManagerServices and without inputChannel)
2.use windowsManagerServices session to relayout
(use mNewSurface size to draw Canvas)
3.surfaceHolder callbacks (create,surfaceChange,surfaceDestory......)

tips:
WindowState代表WMS中的一个窗口,这和App端的Window类是不一样的,尽管很多时候一个Window类(即PhoneWindow)有一个对应的WindowState,但那不是绝对的。一个Activity在WMS中有对应的AppWindowToken,一个AppWindowToken又可以包含多个WindowState,因为除了主窗口外,还可能有子窗口和启动窗口。此外对于系统窗口,WindowState还可能不对应AppWindowToken。
*
*/
/** @hide */
protected void updateWindow(boolean force, boolean redrawNeeded) {
//......

if (force || creating || formatChanged || sizeChanged || visibleChanged
|| mLeft != mLocation[0] || mTop != mLocation[1]
|| mUpdateWindowNeeded || mReportDrawNeeded || redrawNeeded) {

// ......

if (mWindow == null) {
Display display = getDisplay();
mWindow = new MyWindow(this);
mLayout.type = mWindowType;
mLayout.gravity = Gravity.START|Gravity.TOP;
//viewRootImpl的setView也用了addToDisplay
mSession.addToDisplayWithoutInputChannel(mWindow, mWindow.mSeq, mLayout,
mVisible ? VISIBLE : GONE, display.getDisplayId(), mContentInsets,
mStableInsets);
}

boolean realSizeChanged;
boolean reportDrawNeeded;

int relayoutResult;
mSurfaceLock.lock();
//.......

relayoutResult = mSession.relayout(
mWindow, mWindow.mSeq, mLayout, mWidth, mHeight,
visible ? VISIBLE : GONE,
WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY,
mWinFrame, mOverscanInsets, mContentInsets,
mVisibleInsets, mStableInsets, mOutsets, mConfiguration,
mNewSurface);

......
// ----------------------------do surfaceHolder callBack ----------------------
final boolean surfaceChanged = (relayoutResult
& WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED) != 0;
if (mSurfaceCreated && (surfaceChanged || (!visible && visibleChanged))) {
mSurfaceCreated = false;
if (mSurface.isValid()) {
if (DEBUG) Log.i(TAG, "visibleChanged -- surfaceDestroyed");
callbacks = getSurfaceCallbacks();
for (SurfaceHolder.Callback c : callbacks) {
//surfaceDestory
c.surfaceDestroyed(mSurfaceHolder);
}
}
}

mSurface.transferFrom(mNewSurface);

if (visible && mSurface.isValid()) {
if (!mSurfaceCreated && (surfaceChanged || visibleChanged)) {
mSurfaceCreated = true;
mIsCreating = true;
if (DEBUG) Log.i(TAG, "visibleChanged -- surfaceCreated");
if (callbacks == null) {
callbacks = getSurfaceCallbacks();
}
for (SurfaceHolder.Callback c : callbacks) {
//surfaceCreated
c.surfaceCreated(mSurfaceHolder);
}
}
if (creating || formatChanged || sizeChanged
|| visibleChanged || realSizeChanged) {
if (DEBUG) Log.i(TAG, "surfaceChanged -- format=" + mFormat
+ " w=" + myWidth + " h=" + myHeight);
if (callbacks == null) {
callbacks = getSurfaceCallbacks();
}
for (SurfaceHolder.Callback c : callbacks) {
//surfaceChanged
c.surfaceChanged(mSurfaceHolder, mFormat, myWidth, myHeight);
}
}
if (redrawNeeded) {
if (DEBUG) Log.i(TAG, "surfaceRedrawNeeded");
if (callbacks == null) {
callbacks = getSurfaceCallbacks();
}
for (SurfaceHolder.Callback c : callbacks) {
if (c instanceof SurfaceHolder.Callback2) {
//surfaceRedrawNeeded
((SurfaceHolder.Callback2)c).surfaceRedrawNeeded(
mSurfaceHolder);
}
}
}
}
}
.......
}

surfaceView挖洞

整体流程

SurfaceView_Hold

requestTransparentRegion

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Override
public void requestTransparentRegion(View child) {
// the test below should not fail unless someone is messing with us
checkThread();
if (mView == child) {
//add view flag
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();
}
}

performTraversals

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
 private void performTraversals() {
......
// cache mView since it is used so much below...
final View host = mView;
......
final boolean didLayout = mLayoutRequested;
......
if (didLayout) {
......
host.layout(0, 0, host.mMeasuredWidth, host.mMeasuredHeight);
......
if ((host.mPrivateFlags & View.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 (!mTransparentRegion.equals(mPreviousTransparentRegion)) {
mPreviousTransparentRegion.set(mTransparentRegion);
// reconfigure window manager
try {
sWindowSession.setTransparentRegion(mWindow, mTransparentRegion);
} catch (RemoteException e) {
}
}
}
......
}

......
boolean cancelDraw = attachInfo.mTreeObserver.dispatchOnPreDraw();
if (!cancelDraw && !newSurface) {
......
draw(fullRedrawNeeded);
......
}
......
}
......
}

gatherTransparentRegion

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@Override
public boolean gatherTransparentRegion(Region region) {
if (mWindowType == WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) {
return super.gatherTransparentRegion(region);
}

boolean opaque = true;
if ((mPrivateFlags & PFLAG_SKIP_DRAW) == 0) {
//need draw not transparent
// this view draws, remove it from the transparent region
opaque = super.gatherTransparentRegion(region);
} else if (region != null) {
//caculate the trasparent region
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;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/**
* This is used by the RootView to perform an optimization when
* the view hierarchy contains one or several SurfaceView.
* SurfaceView is always considered transparent, but its children are not,
* therefore all View objects remove themselves from the global transparent
* region (passed as a parameter to this function).
*
* @param region The transparent region for this ViewAncestor (window).
*
* @return Returns true if the effective visibility of the view at this
* point is opaque, regardless of the transparent region; returns false
* if it is possible for underlying windows to be seen behind the view.
*
* {@hide}
*/
public boolean gatherTransparentRegion(Region region) {
final AttachInfo attachInfo = mAttachInfo;
if (region != null && attachInfo != null) {
final int pflags = mPrivateFlags;
if ((pflags & PFLAG_SKIP_DRAW) == 0) {
// The SKIP_DRAW flag IS NOT set, so this view draws. We need to
// remove it from the transparent region.
final int[] location = attachInfo.mTransparentLocation;
getLocationInWindow(location);
region.op(location[0], location[1], location[0] + mRight - mLeft,
location[1] + mBottom - mTop, Region.Op.DIFFERENCE);
} else {
if (mBackground != null && mBackground.getOpacity() != PixelFormat.TRANSPARENT) {
// The SKIP_DRAW flag IS set and the background drawable exists, we remove
// the background drawable's non-transparent parts from this transparent region.
applyDrawableToTransparentRegion(mBackground, region);
}
if (mForegroundInfo != null && mForegroundInfo.mDrawable != null
&& mForegroundInfo.mDrawable.getOpacity() != PixelFormat.TRANSPARENT) {
// Similarly, we remove the foreground drawable's non-transparent parts.
applyDrawableToTransparentRegion(mForegroundInfo.mDrawable, region);
}
}
}
return true;
}

surfaceView的绘制

整体流程

SurfaceView_Flow

getHolder

1
2
3
4
5
6
7
8
9
10
11
12
/**
* Return the SurfaceHolder providing access and control over this
* SurfaceView's underlying surface.
*
##control surface
*
* @return SurfaceHolder The holder of the surface.
*/
public SurfaceHolder getHolder() {
//继承surfaceView的View获取holer,通过它来获取surfaceView的surface
return mSurfaceHolder;
}

lockCanvas

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class SurfaceView extends View {
......
final ReentrantLock mSurfaceLock = new ReentrantLock();
final Surface mSurface = new Surface();
......
private SurfaceHolder mSurfaceHolder = new SurfaceHolder() {
......
public Canvas lockCanvas() {
return internalLockCanvas(null);
}
......
private final Canvas internalLockCanvas(Rect dirty) {
if (mType == SURFACE_TYPE_PUSH_BUFFERS) {
throw new BadSurfaceTypeException(
"Surface type is SURFACE_TYPE_PUSH_BUFFERS");
}
mSurfaceLock.lock();
......
Canvas c = null;
if (!mDrawingStopped && mWindow != null) {
Rect frame = dirty != null ? dirty : mSurfaceFrame;
try {
c = mSurface.lockCanvas(frame);
} catch (Exception e) {
Log.e(LOG_TAG, "Exception locking surface", e);
}
}
......
if (c != null) {
mLastLockTime = SystemClock.uptimeMillis();
return c;
}
......
mSurfaceLock.unlock();
return null;
}
......
}
......
}

unlockCanvasAndPost

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class SurfaceView extends View {
......
final ReentrantLock mSurfaceLock = new ReentrantLock();
final Surface mSurface = new Surface();
......
private SurfaceHolder mSurfaceHolder = new SurfaceHolder() {
......
public void unlockCanvasAndPost(Canvas canvas) {
mSurface.unlockCanvasAndPost(canvas);
mSurfaceLock.unlock();
}
......
}
......
}

一般步骤

1
2
3
4
5
6
SurfaceView sv = (SurfaceView )findViewById(R.id.surface_view);
SurfaceHolder sh = sv.getHolder();
Cavas canvas = sh.lockCanvas()
//Draw something on canvas
......
sh.unlockCanvasAndPost(canvas);