以前有个疑问,SurfaceView为什么可以开一个新的线程进行绘制,而其他的不行。我们知道View的布局绘制是在主线程执行的,通过ViewRootImpl的performTraversals方法开始驱动顶层DecorView和它的子View执行measure, layout, draw。同样是View类,那为什么SurfaceView的绘制操作却可以单独开新的线程去执行呢?
在这里先提前总结一下原理。View的绘制是由ViewRootImpl的Surface对象将绘制后的ui数据交给WindowManagerService,WindowManagerService会将多个Surface数据合并成一整屏的ui数据,交给SurfaceFlinger渲染对应的Layer。而SurfaceView内部维护着一块Surface用于ui数据的的绘制,同时在WindowManagerService端会创建一个新的绘图对象,对应着SurfaceFlinger的一个新的Layer,因此SurfaceView中绘制的数据就由新的Layer,而不是宿主DecorView的Layer;意思就是SurfaceView有和宿主DecorView对应的ViewRootImpl一样的一套绘制渲染模型,两者分别独立渲染。
现在简单讲讲SurfaceView的工作流程。
- 1.在onAttachedToWindow中进行对SurfaceView的初始化和准备工作。完成透明区域的请求,获取WindowManagerService的本地代理对象mSession,和对绘制的监听addOnPreDrawListener
- 2.在onWindowVisibilityChanged中将被显示时,调用updateWindow
- 3.updateWindow,初始化MyWindow对象,用于WindowManagerService通知SurfaceView的状态变化,mSession.relayout以请求WindowManagerService对Surface的UI进行布局,这样SurfaceFlinger就会为其创建一个独立的Layer绘图对象。这里同时会回调SurfaceHolder.Callback的surfaceCreated和surfaceChanged等方法,通知当前的SurfaceView是否准备好了。
- 4.然后我们就可以通过getHolder获取SurfaceHolder的对象,在新线程中,通过lockCanvas锁定画布,然后用Cavas对象进行绘制,内部是通过SurfaceView的Surface对象来取得Canvas对象,该Canvas通过JNI调用到底层的Surface的Canvas进行操作。
- 5.在onDetachedFromWindow中进行对SurfaceView的清理工作。移除绘制监听,调用updateWindow通知回调SurfaceHolder.Callback的surfaceDestroyed,mSession移除mWindow以使WindowManagerService解除对SurfaceView的操作。
再次总结,因为普通View走的是ViewRootImpl的绘制流程,在里面有对线程进行检查,非主线程的话会抛异常,目的就是实现单线程绘制模型,同时又要接受输入事情等。而SurfaceView有独立的绘制机制,比如独立的客户端Surface,WindowManagerService中独立的绘图对象,SurfaceFlinger中独立的Layer渲染。因为它里面可以只负责绘制,所以效率要更高。当然,SurfaceView的其他处理比如输入事件还是继承使用了View的那一套。
好了,简述完了之后,觉得还是有必要贴出些源码,让描述显得更加有依据一些。
public SurfaceView(Context context) {
super(context);
init();
}
public SurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public SurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
public SurfaceView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
private void init() {
setWillNotDraw(true);
}
此处初始化,目的是不进行View的那一套绘制,专心做新线程的绘制。
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
mParent.requestTransparentRegion(this);
mSession = getWindowSession();
mLayout.token = getWindowToken();
mLayout.setTitle("SurfaceView - " + getViewRootImpl().getTitle());
mViewVisibility = getVisibility() == VISIBLE;
if (!mGlobalListenersAdded) {
ViewTreeObserver observer = getViewTreeObserver();
observer.addOnScrollChangedListener(mScrollChangedListener);
observer.addOnPreDrawListener(mDrawListener);
mGlobalListenersAdded = true;
}
}
当SurfaceView被添加到Window上即将显示时,调用mParent.requestTransparentRegion(this);请求顶层View给自己留出空白区域,这样我们的SurfaceView才能不会被其他View遮挡住。然后这里获取了WindowManager的代理对象,以后后面对WindowManager的请求操作。然后添加滚动监听和绘制监听
private final ViewTreeObserver.OnScrollChangedListener mScrollChangedListener
= new ViewTreeObserver.OnScrollChangedListener() {
@Override
public void onScrollChanged() {
updateWindow(false, false);
}
};
private final ViewTreeObserver.OnPreDrawListener mDrawListener =
new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
// reposition ourselves where the surface is
mHaveFrame = getWidth() > 0 && getHeight() > 0;
updateWindow(false, false);
return true;
}
};
这样,即将要绘制的时候,就会执行updateWindow方法了。
@Override
protected void onWindowVisibilityChanged(int visibility) {
super.onWindowVisibilityChanged(visibility);
mWindowVisibility = visibility == VISIBLE;
mRequestedVisible = mWindowVisibility && mViewVisibility;
updateWindow(false, false);
}
SurfaceView依附的Window要显示或隐藏时,也同样调用updateWindow,那updateWindow是做什么操作的呢?
protected void updateWindow(boolean force, boolean redrawNeeded) {
.....
final boolean creating = mWindow == null;
final boolean formatChanged = mFormat != mRequestedFormat;
final boolean sizeChanged = mWindowSpaceWidth != myWidth || mWindowSpaceHeight != myHeight;
final boolean visibleChanged = mVisible != mRequestedVisible;
final boolean layoutSizeChanged = getWidth() != mLayout.width
|| getHeight() != mLayout.height;
//此处判断SurfaceView是否有发生变化,需要更新
if (force || creating || formatChanged || sizeChanged || visibleChanged
|| mUpdateWindowNeeded || mReportDrawNeeded || redrawNeeded) {
//需要刷新SurfaceView,更新mLayout这个SurfaceView的LayoutParams的信息
......
if (mWindow == null) {
//MyWindow是一个Binder类,用于WindowMangerService通知回调SurfaceView,这里创建MyWindow对象mWIndow,并通过mSession.addToDisplayWithoutInputChannel将这些参数传给WindowMangerService,通知它为SurfaceView创建一块不接收输入事件的Surface,以便后面绘图所用。
Display display = getDisplay();
mWindow = new MyWindow(this);
mLayout.type = mWindowType;
mLayout.gravity = Gravity.START|Gravity.TOP;
mSession.addToDisplayWithoutInputChannel(mWindow, mWindow.mSeq, mLayout,
mVisible ? VISIBLE : GONE, display.getDisplayId(), mContentInsets,
mStableInsets);
}
boolean realSizeChanged;
boolean reportDrawNeeded;
int relayoutResult;
//此处需要用锁锁住,防止其他线程同时修改
mSurfaceLock.lock();
try {
mUpdateWindowNeeded = false;
reportDrawNeeded = mReportDrawNeeded;
mReportDrawNeeded = false;
mDrawingStopped = !visible;
//通过mSession这个binder对象请求WindowMangerService为SurfaceView的UI进行布局,然后WindowMangerService就会填充mNewSurface这个Surface对象,以便后面可以通过它获取画布Canvas,进行绘图操作。
relayoutResult = mSession.relayout(
mWindow, mWindow.mSeq, mLayout, mWindowSpaceWidth, mWindowSpaceHeight,
visible ? VISIBLE : GONE,
WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY,
mWinFrame, mOverscanInsets, mContentInsets,
mVisibleInsets, mStableInsets, mOutsets, mBackdropFrame,
mConfiguration, mNewSurface);
if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
reportDrawNeeded = true;
}
if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+ "New surface: " + mNewSurface
+ ", vis=" + visible + ", frame=" + mWinFrame);
mSurfaceFrame.left = 0;
mSurfaceFrame.top = 0;
if (mTranslator == null) {
mSurfaceFrame.right = mWinFrame.width();
mSurfaceFrame.bottom = mWinFrame.height();
} else {
float appInvertedScale = mTranslator.applicationInvertedScale;
mSurfaceFrame.right = (int) (mWinFrame.width() * appInvertedScale + 0.5f);
mSurfaceFrame.bottom = (int) (mWinFrame.height() * appInvertedScale + 0.5f);
}
final int surfaceWidth = mSurfaceFrame.right;
final int surfaceHeight = mSurfaceFrame.bottom;
realSizeChanged = mLastSurfaceWidth != surfaceWidth
|| mLastSurfaceHeight != surfaceHeight;
mLastSurfaceWidth = surfaceWidth;
mLastSurfaceHeight = surfaceHeight;
} finally {
//此处释放锁
mSurfaceLock.unlock();
}
try {
redrawNeeded |= creating | reportDrawNeeded;
SurfaceHolder.Callback callbacks[] = null;
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, System.identityHashCode(this) + " "
+ "visibleChanged -- surfaceDestroyed");
callbacks = getSurfaceCallbacks();
//这里判断了Surface被销毁了,回调实现了SurfaceHolder.Callback的对象,大部分情况就是我们继承实现的SurfaceView了
for (SurfaceHolder.Callback c : callbacks) {
c.surfaceDestroyed(mSurfaceHolder);
}
}
}
//这里将最新状态的mNewSuface对象的数据更新到当前的mSurface中
mSurface.transferFrom(mNewSurface);
if (visible && mSurface.isValid()) {
if (!mSurfaceCreated && (surfaceChanged || visibleChanged)) {
mSurfaceCreated = true;
mIsCreating = true;
if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+ "visibleChanged -- surfaceCreated");
if (callbacks == null) {
callbacks = getSurfaceCallbacks();
}
//这里判断了Surface被创建了,回调实现了SurfaceHolder.Callback的对象,大部分情况就是我们继承实现的SurfaceView了
for (SurfaceHolder.Callback c : callbacks) {
c.surfaceCreated(mSurfaceHolder);
}
}
if (creating || formatChanged || sizeChanged
|| visibleChanged || realSizeChanged) {
if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+ "surfaceChanged -- format=" + mFormat
+ " w=" + myWidth + " h=" + myHeight);
if (callbacks == null) {
callbacks = getSurfaceCallbacks();
}
//这里判断了Surface被更新了,回调实现了SurfaceHolder.Callback的对象,大部分情况就是我们继承实现的SurfaceView了
for (SurfaceHolder.Callback c : callbacks) {
c.surfaceChanged(mSurfaceHolder, mFormat, myWidth, myHeight);
}
}
if (redrawNeeded) {
if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+ "surfaceRedrawNeeded");
if (callbacks == null) {
callbacks = getSurfaceCallbacks();
}
//这里判断了Surface需要被重绘,回调实现了SurfaceHolder.Callback的对象,大部分情况就是我们继承实现的SurfaceView了
for (SurfaceHolder.Callback c : callbacks) {
if (c instanceof SurfaceHolder.Callback2) {
((SurfaceHolder.Callback2)c).surfaceRedrawNeeded(
mSurfaceHolder);
}
}
}
}
} finally {
mIsCreating = false;
if (redrawNeeded) {
if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+ "finishedDrawing");
mSession.finishDrawing(mWindow);
}
mSession.performDeferredDestroy(mWindow);
}
} catch (RemoteException ex) {
Log.e(TAG, "Exception from relayout", ex);
}
} else {
//除了以上情况,如果是SufaceView的位置或大小发生改变,就进行UI线程的布局更新
// Calculate the window position in case RT loses the window
// and we need to fallback to a UI-thread driven position update
getLocationInWindow(mLocation);
final boolean positionChanged = mWindowSpaceLeft != mLocation[0]
|| mWindowSpaceTop != mLocation[1];
if (positionChanged || layoutSizeChanged) { // Only the position has changed
mWindowSpaceLeft = mLocation[0];
mWindowSpaceTop = mLocation[1];
// For our size changed check, we keep mLayout.width and mLayout.height
// in view local space.
mLocation[0] = mLayout.width = getWidth();
mLocation[1] = mLayout.height = getHeight();
transformFromViewToWindowSpace(mLocation);
mWinFrame.set(mWindowSpaceLeft, mWindowSpaceTop,
mLocation[0], mLocation[1]);
if (mTranslator != null) {
mTranslator.translateRectInAppWindowToScreen(mWinFrame);
}
if (!isHardwareAccelerated() || !mRtHandlingPositionUpdates) {
try {
if (DEBUG) Log.d(TAG, String.format("%d updateWindowPosition UI, " +
"postion = [%d, %d, %d, %d]", System.identityHashCode(this),
mWinFrame.left, mWinFrame.top,
mWinFrame.right, mWinFrame.bottom));
mSession.repositionChild(mWindow, mWinFrame.left, mWinFrame.top,
mWinFrame.right, mWinFrame.bottom, -1, mWinFrame);
} catch (RemoteException ex) {
Log.e(TAG, "Exception from relayout", ex);
}
}
}
}
}
可见,updateWindow的主要作用是SurfaceView更新的处理,包括mWindow,通知WindowMangerService中Surface的创建,还有Surface的创建,更新,销毁的通知回调等。
我们在看看MyWindow这个Binder类
private static class MyWindow extends BaseIWindow {
private final WeakReference<SurfaceView> mSurfaceView;
public MyWindow(SurfaceView surfaceView) {
//弱引用持有当前的SurfaceView,防止内存泄露
mSurfaceView = new WeakReference<SurfaceView>(surfaceView);
}
@Override
public void resized(Rect frame, Rect overscanInsets, Rect contentInsets,
Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw,
Configuration newConfig, Rect backDropRect, boolean forceLayout,
boolean alwaysConsumeNavBar) {
//WindowMangerService中SurfaceView对应的Surface大小发生变化,发消息通知窗口布局发生了变化
SurfaceView surfaceView = mSurfaceView.get();
if (surfaceView != null) {
if (DEBUG) Log.v(TAG, surfaceView + " got resized: w=" + frame.width()
+ " h=" + frame.height() + ", cur w=" + mCurWidth + " h=" + mCurHeight);
surfaceView.mSurfaceLock.lock();
try {
if (reportDraw) {
surfaceView.mUpdateWindowNeeded = true;
surfaceView.mReportDrawNeeded = true;
surfaceView.mHandler.sendEmptyMessage(UPDATE_WINDOW_MSG);
} else if (surfaceView.mWinFrame.width() != frame.width()
|| surfaceView.mWinFrame.height() != frame.height()
|| forceLayout) {
surfaceView.mUpdateWindowNeeded = true;
surfaceView.mHandler.sendEmptyMessage(UPDATE_WINDOW_MSG);
}
} finally {
surfaceView.mSurfaceLock.unlock();
}
}
}
@Override
public void dispatchAppVisibility(boolean visible) {
// The point of SurfaceView is to let the app control the surface.
}
@Override
public void dispatchGetNewSurface() {
//通知对应的新的Surface创建了,这里消息处理会调用updateWindow,在其中会获得最新的Surface填充到mNewSurface中
SurfaceView surfaceView = mSurfaceView.get();
if (surfaceView != null) {
Message msg = surfaceView.mHandler.obtainMessage(GET_NEW_SURFACE_MSG);
surfaceView.mHandler.sendMessage(msg);
}
}
@Override
public void windowFocusChanged(boolean hasFocus, boolean touchEnabled) {
Log.w("SurfaceView", "Unexpected focus in surface: focus=" + hasFocus + ", touchEnabled=" + touchEnabled);
}
@Override
public void executeCommand(String command, String parameters, ParcelFileDescriptor out) {
}
int mCurWidth = -1;
int mCurHeight = -1;
}
可见,MyWindow就是WindowMangerService用来通知SurfaceView的沟通工具,通知SurfaceView对应的Surface状态的变化。
最后就是mSurfaceHolder了,顾名思义就是Surface的持有者,通过它来操作Surface对象。
private final SurfaceHolder mSurfaceHolder = new SurfaceHolder() {
private static final String LOG_TAG = "SurfaceHolder";
@Override
public boolean isCreating() {
return mIsCreating;
}
@Override
public void addCallback(Callback callback) {
//添加callback回调
synchronized (mCallbacks) {
// This is a linear search, but in practice we'll
// have only a couple callbacks, so it doesn't matter.
if (mCallbacks.contains(callback) == false) {
mCallbacks.add(callback);
}
}
}
@Override
public void removeCallback(Callback callback) {
//移除callback回调
synchronized (mCallbacks) {
mCallbacks.remove(callback);
}
}
@Override
public void setFixedSize(int width, int height) {
if (mRequestedWidth != width || mRequestedHeight != height) {
mRequestedWidth = width;
mRequestedHeight = height;
requestLayout();
}
}
@Override
public void setSizeFromLayout() {
if (mRequestedWidth != -1 || mRequestedHeight != -1) {
mRequestedWidth = mRequestedHeight = -1;
requestLayout();
}
}
@Override
public void setFormat(int format) {
// for backward compatibility reason, OPAQUE always
// means 565 for SurfaceView
if (format == PixelFormat.OPAQUE)
format = PixelFormat.RGB_565;
mRequestedFormat = format;
if (mWindow != null) {
updateWindow(false, false);
}
}
/**
* @deprecated setType is now ignored.
*/
@Override
@Deprecated
public void setType(int type) { }
@Override
public void setKeepScreenOn(boolean screenOn) {
Message msg = mHandler.obtainMessage(KEEP_SCREEN_ON_MSG);
msg.arg1 = screenOn ? 1 : 0;
mHandler.sendMessage(msg);
}
/**
* Gets a {@link Canvas} for drawing into the SurfaceView's Surface
*
* After drawing into the provided {@link Canvas}, the caller must
* invoke {@link #unlockCanvasAndPost} to post the new contents to the surface.
*
* The caller must redraw the entire surface.
* @return A canvas for drawing into the surface.
*/
@Override
public Canvas lockCanvas() {
return internalLockCanvas(null);
}
/**
* Gets a {@link Canvas} for drawing into the SurfaceView's Surface
*
* After drawing into the provided {@link Canvas}, the caller must
* invoke {@link #unlockCanvasAndPost} to post the new contents to the surface.
*
* @param inOutDirty A rectangle that represents the dirty region that the caller wants
* to redraw. This function may choose to expand the dirty rectangle if for example
* the surface has been resized or if the previous contents of the surface were
* not available. The caller must redraw the entire dirty region as represented
* by the contents of the inOutDirty rectangle upon return from this function.
* The caller may also pass <code>null</code> instead, in the case where the
* entire surface should be redrawn.
* @return A canvas for drawing into the surface.
*/
@Override
public Canvas lockCanvas(Rect inOutDirty) {
return internalLockCanvas(inOutDirty);
}
private final Canvas internalLockCanvas(Rect dirty) {
//锁定并获取Canvas画布对象,以进行后面的绘画
mSurfaceLock.lock();
if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " + "Locking canvas... stopped="
+ mDrawingStopped + ", win=" + mWindow);
Canvas c = null;
if (!mDrawingStopped && mWindow != null) {
try {
c = mSurface.lockCanvas(dirty);
} catch (Exception e) {
Log.e(LOG_TAG, "Exception locking surface", e);
}
}
if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " + "Returned canvas: " + c);
if (c != null) {
mLastLockTime = SystemClock.uptimeMillis();
return c;
}
// If the Surface is not ready to be drawn, then return null,
// but throttle calls to this function so it isn't called more
// than every 100ms.
long now = SystemClock.uptimeMillis();
long nextTime = mLastLockTime + 100;
if (nextTime > now) {
try {
Thread.sleep(nextTime-now);
} catch (InterruptedException e) {
}
now = SystemClock.uptimeMillis();
}
mLastLockTime = now;
mSurfaceLock.unlock();
return null;
}
/**
* Posts the new contents of the {@link Canvas} to the surface and
* releases the {@link Canvas}.
*
* @param canvas The canvas previously obtained from {@link #lockCanvas}.
*/
@Override
public void unlockCanvasAndPost(Canvas canvas) {
mSurface.unlockCanvasAndPost(canvas);
mSurfaceLock.unlock();
}
@Override
public Surface getSurface() {
return mSurface;
}
@Override
public Rect getSurfaceFrame() {
return mSurfaceFrame;
}
};
可见,SurfaceHolder就是SurfaceView对外提供访问Surface的接口,这里访问Surface有做同步,为空时重复请求的处理。
到这里的话,SurfaceView的大部分逻辑都在这了,就先到这里了。