文章目录
- 画布surface
- ViewRoot的创建&setView分析
- setView
- requestLayout
- ViewRoot和WMS的关系
- activity的UI绘制
- draw
- surface
- jni层分析
- Surface无参构造
- SurfaceSession
- SurfaceSession_init
- surface的有参构造
- Surface_copyFrom
- Surface_writeToParcel
- Surface_readFromParcel
- 总结
- 参考资料
上一篇我们分析到了Android创建了UI控件,但是作画的地方在哪里呢?
这里继续进行分析。
画布surface
Handle onto a raw buffer that is being managed by the screen compositor.
A Surface is generally created by or from a consumer of image buffers (such as a android.graphics.SurfaceTexture, android.media.MediaRecorder, or android.renderscript.Allocation), and is handed to some kind of producer (such as OpenGL, MediaPlayer, or android.hardware.camera2.CameraDevice#createCaptureSession) to draw into.
Note: A Surface acts like a weak reference to the consumer it is associated with. By itself it will not keep its parent consumer from being reclaimed.
https://developer.android.com/reference/kotlin/android/view/Surface?hl=en
从官方文档的描述,我们可以知道surface其实就是我们苦苦思索的”在哪里作画”的画布。相机、OpenGL、媒体播放器可以作为画家在上面作画。
Handle onto a raw buffer that is being managed by the screen compositor.这里说的 screen compositor其实就是SurfaceFlinger。我们可以把他们来一次具象化理解:
我们需要理解的是SurfaceFlinger和Surface的联系,这对于我们开发Android很重要。
ViewRoot的创建&setView分析
frameworks/base/core/java/android/view/ViewRoot.java构造函数:```java
public ViewRoot(Context context) {super();if (MEASURE_LATENCY && lt == null) {lt = new LatencyTimer(100, 1000);}// For debug only//++sInstanceCount;// Initialize the statics when this class is first instantiated. This is// done here instead of in the static block because Zygote does not// allow the spawning of threads.getWindowSession(context.getMainLooper());mThread = Thread.currentThread();mLocation = new WindowLeaked(null);mLocation.fillInStackTrace();mWidth = -1;mHeight = -1;mDirty = new Rect();mTempRect = new Rect();mVisRect = new Rect();mWinFrame = new Rect();mWindow = new W(this, context);mInputMethodCallback = new InputMethodCallback(this);mViewVisibility = View.GONE;mTransparentRegion = new Region();mPreviousTransparentRegion = new Region();mFirst = true; // true for the first time the view is addedmAdded = false;mAttachInfo = new View.AttachInfo(sWindowSession, mWindow, this, this);mViewConfiguration = ViewConfiguration.get(context);mDensity = context.getResources().getDisplayMetrics().densityDpi;}
getWindowSession:
public static IWindowSession getWindowSession(Looper mainLooper) {synchronized (mStaticInit) {if (!mInitialized) {try {InputMethodManager imm = InputMethodManager.getInstance(mainLooper);//binder通信的写法,很熟悉sWindowSession = IWindowManager.Stub.asInterface(ServiceManager.getService("window")).openSession(imm.getClient(), imm.getInputContext());mInitialized = true;} catch (RemoteException e) {}}return sWindowSession;}}
这个函数其实会建立Activity里面ViewRoot和WindowManagerService的关系。
我们知道,WindowManagerService由SystemServer进程启动,surfaceflinger服务也在这个进程中。所以activity的显示是需要跨进程通信的。
setView
接着分析
/*** We have one child*///第一个参数是decorviewpublic void setView(View view, WindowManager.LayoutParams attrs,View panelParentView) {synchronized (this) {if (mView == null) {mView = view;mWindowAttributes.copyFrom(attrs);attrs = mWindowAttributes;Resources resources = mView.getContext().getResources();CompatibilityInfo compatibilityInfo = resources.getCompatibilityInfo();mTranslator = compatibilityInfo.getTranslator();if (mTranslator != null || !compatibilityInfo.supportsScreen()) {mSurface.setCompatibleDisplayMetrics(resources.getDisplayMetrics(),mTranslator);}boolean restore = false;if (mTranslator != null) {restore = true;attrs.backup();mTranslator.translateWindowLayout(attrs);}if (DEBUG_LAYOUT) Log.d(TAG, "WindowLayout in setView:" + attrs);if (!compatibilityInfo.supportsScreen()) {attrs.flags |= WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW;}mSoftInputMode = attrs.softInputMode;mWindowAttributesChanged = true;mAttachInfo.mRootView = view;mAttachInfo.mScalingRequired = mTranslator != null;mAttachInfo.mApplicationScale =mTranslator == null ? 1.0f : mTranslator.applicationScale;if (panelParentView != null) {mAttachInfo.mPanelParentWindowToken= panelParentView.getApplicationWindowToken();}mAdded = true;int res; /* = WindowManagerImpl.ADD_OKAY; */// Schedule the first layout -before- adding to the window// manager, to make sure we do the relayout before receiving// any other events from the system.//重点requestLayout();try {//重点res = sWindowSession.add(mWindow, mWindowAttributes,getHostVisibility(), mAttachInfo.mContentInsets);} catch (RemoteException e) {mAdded = false;mView = null;mAttachInfo.mRootView = null;unscheduleTraversals();throw new RuntimeException("Adding window failed", e);} finally {if (restore) {attrs.restore();}}if (mTranslator != null) {mTranslator.translateRectInScreenToAppWindow(mAttachInfo.mContentInsets);}mPendingContentInsets.set(mAttachInfo.mContentInsets);mPendingVisibleInsets.set(0, 0, 0, 0);if (Config.LOGV) Log.v("ViewRoot", "Added window " + mWindow);if (res < WindowManagerImpl.ADD_OKAY) {mView = null;mAttachInfo.mRootView = null;mAdded = false;unscheduleTraversals();switch (res) {case WindowManagerImpl.ADD_BAD_APP_TOKEN:case WindowManagerImpl.ADD_BAD_SUBWINDOW_TOKEN:throw new WindowManagerImpl.BadTokenException("Unable to add window -- token " + attrs.token+ " is not valid; is your activity running?");case WindowManagerImpl.ADD_NOT_APP_TOKEN:throw new WindowManagerImpl.BadTokenException("Unable to add window -- token " + attrs.token+ " is not for an application");case WindowManagerImpl.ADD_APP_EXITING:throw new WindowManagerImpl.BadTokenException("Unable to add window -- app for token " + attrs.token+ " is exiting");case WindowManagerImpl.ADD_DUPLICATE_ADD:throw new WindowManagerImpl.BadTokenException("Unable to add window -- window " + mWindow+ " has already been added");case WindowManagerImpl.ADD_STARTING_NOT_NEEDED:// Silently ignore -- we would have just removed it// right away, anyway.return;case WindowManagerImpl.ADD_MULTIPLE_SINGLETON:throw new WindowManagerImpl.BadTokenException("Unable to add window " + mWindow +" -- another window of this type already exists");case WindowManagerImpl.ADD_PERMISSION_DENIED:throw new WindowManagerImpl.BadTokenException("Unable to add window " + mWindow +" -- permission denied for this window type");}throw new RuntimeException("Unable to add window -- unknown error code " + res);}view.assignParent(this);mAddedTouchMode = (res&WindowManagerImpl.ADD_FLAG_IN_TOUCH_MODE) != 0;mAppVisible = (res&WindowManagerImpl.ADD_FLAG_APP_VISIBLE) != 0;}}}
这里做了几件事情,保存传入的decorview,调用了requestLayout,跨进程通信调用了add函数。
requestLayout
frameworks/base/core/java/android/view/ViewRoot.java/*** {@inheritDoc}*/public void requestLayout() {checkThread();mLayoutRequested = true;scheduleTraversals();}
frameworks/base/core/java/android/view/ViewRoot.javapublic void scheduleTraversals() {if (!mTraversalScheduled) {mTraversalScheduled = true;sendEmptyMessage(DO_TRAVERSAL);}}
ViewRoot和WMS进行了交互,ViewRoot调用openSession拿到了IWindowSession,调用它的add函数,把W类型的mWindow对象作为参数传入。
ViewRoot和WMS的关系
来看下WMS的openSession
frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java@Overridepublic IWindowSession openSession(IWindowSessionCallback callback) {return new Session(this, callback);}
frameworks/base/services/java/com/android/server/WindowManagerService.javapublic int add(IWindow window, WindowManager.LayoutParams attrs,int viewVisibility, Rect outContentInsets) {return addWindow(this, window, attrs, viewVisibility, outContentInsets);}
public int addWindow(Session session, IWindow client,WindowManager.LayoutParams attrs, int viewVisibility,Rect outContentInsets) {...//重点win = new WindowState(session, client, token,attachedWindow, attrs, viewVisibility);if (win.mDeathRecipient == null) {// Client has apparently died, so there is no reason to// continue.Slog.w(TAG, "Adding window client " + client.asBinder()+ " that is dead, aborting.");return WindowManagerImpl.ADD_APP_EXITING;}mPolicy.adjustWindowParamsLw(win.mAttrs);res = mPolicy.prepareAddWindowLw(win, attrs);if (res != WindowManagerImpl.ADD_OKAY) {return res;}// From now on, no exceptions or errors allowed!res = WindowManagerImpl.ADD_OKAY;final long origId = Binder.clearCallingIdentity();if (addToken) {mTokenMap.put(attrs.token, token);mTokenList.add(token);}//重点win.attach();mWindowMap.put(client.asBinder(), win);...return res;}
WindowState的attach:
void attach() {if (localLOGV) Slog.v(TAG, "Attaching " + this + " token=" + mToken+ ", list=" + mToken.windows);mSession.windowAddedLocked();}
void windowAddedLocked() {if (mSurfaceSession == null) {if (localLOGV) Slog.v(TAG, "First window added to " + this + ", creating SurfaceSession");//重点mSurfaceSession = new SurfaceSession();if (SHOW_TRANSACTIONS) Slog.i(TAG, " NEW SURFACE SESSION " + mSurfaceSession);mSessions.add(this);}mNumWindow++;}
到这里,我们得总结一番:
activity的UI绘制
@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case DO_TRAVERSAL:if (mProfile) {Debug.startMethodTracing("ViewRoot");}performTraversals();if (mProfile) {Debug.stopMethodTracing();mProfile = false;}break;}
performTraversals:
private void performTraversals() {...// cache mView since it is used so much below...final View host = mView;//重点relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
...if (!cancelDraw && !newSurface) {mFullRedrawNeeded = false;//绘制draw(fullRedrawNeeded);
...
relayoutWindow:
private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,boolean insetsPending) throws RemoteException {...//重点int relayoutResult = sWindowSession.relayout(mWindow, params,(int) (mView.mMeasuredWidth * appScale + 0.5f),(int) (mView.mMeasuredHeight * appScale + 0.5f),viewVisibility, insetsPending, mWinFrame,mPendingContentInsets, mPendingVisibleInsets,mPendingConfiguration, mSurface);//mSurface...return relayoutResult;}
draw
frameworks/base/core/java/android/view/ViewRoot.java
private void draw(boolean fullRedrawNeeded) {Surface surface = mSurface;//viewroot的成员变量...Rect dirty = mDirty;if (mUseGL) {if (!dirty.isEmpty()) {Canvas canvas = mGlCanvas;if (mGL != null && canvas != null) {mGL.glDisable(GL_SCISSOR_TEST);mGL.glClearColor(0, 0, 0, 0);mGL.glClear(GL_COLOR_BUFFER_BIT);mGL.glEnable(GL_SCISSOR_TEST);mAttachInfo.mDrawingTime = SystemClock.uptimeMillis();mAttachInfo.mIgnoreDirtyState = true;mView.mPrivateFlags |= View.DRAWN;int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);try {canvas.translate(0, -yoff);if (mTranslator != null) {mTranslator.translateCanvas(canvas);}canvas.setScreenDensity(scalingRequired? DisplayMetrics.DENSITY_DEVICE : 0);mView.draw(canvas);if (Config.DEBUG && ViewDebug.consistencyCheckEnabled) {mView.dispatchConsistencyCheck(ViewDebug.CONSISTENCY_DRAWING);}} finally {canvas.restoreToCount(saveCount);}mAttachInfo.mIgnoreDirtyState = false;mEgl.eglSwapBuffers(mEglDisplay, mEglSurface);checkEglErrors();if (SHOW_FPS || Config.DEBUG && ViewDebug.showFps) {int now = (int)SystemClock.elapsedRealtime();if (sDrawTime != 0) {nativeShowFPS(canvas, now - sDrawTime);}sDrawTime = now;}}}if (scrolling) {mFullRedrawNeeded = true;scheduleTraversals();}return;}...Canvas canvas;try {int left = dirty.left;int top = dirty.top;int right = dirty.right;int bottom = dirty.bottom;//lock canvascanvas = surface.lockCanvas(dirty);...try {if (!dirty.isEmpty() || mIsAnimating) {long startTime = 0L;if (DEBUG_ORIENTATION || DEBUG_DRAW) {Log.v("ViewRoot", "Surface " + surface + " drawing to bitmap w="+ canvas.getWidth() + ", h=" + canvas.getHeight());//canvas.drawARGB(255, 255, 0, 0);}if (Config.DEBUG && ViewDebug.profileDrawing) {startTime = SystemClock.elapsedRealtime();}// If this bitmap's format includes an alpha channel, we// need to clear it before drawing so that the child will// properly re-composite its drawing on a transparent// background. This automatically respects the clip/dirty region// or// If we are applying an offset, we need to clear the area// where the offset doesn't appear to avoid having garbage// left in the blank areas.if (!canvas.isOpaque() || yoff != 0) {canvas.drawColor(0, PorterDuff.Mode.CLEAR);}dirty.setEmpty();mIsAnimating = false;mAttachInfo.mDrawingTime = SystemClock.uptimeMillis();mView.mPrivateFlags |= View.DRAWN;if (DEBUG_DRAW) {Context cxt = mView.getContext();Log.i(TAG, "Drawing: package:" + cxt.getPackageName() +", metrics=" + cxt.getResources().getDisplayMetrics() +", compatibilityInfo=" + cxt.getResources().getCompatibilityInfo());}int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);try {canvas.translate(0, -yoff);if (mTranslator != null) {mTranslator.translateCanvas(canvas);}canvas.setScreenDensity(scalingRequired? DisplayMetrics.DENSITY_DEVICE : 0);//在canvas中绘制mView.draw(canvas);} finally {mAttachInfo.mIgnoreDirtyState = false;canvas.restoreToCount(saveCount);}if (Config.DEBUG && ViewDebug.consistencyCheckEnabled) {mView.dispatchConsistencyCheck(ViewDebug.CONSISTENCY_DRAWING);}if (SHOW_FPS || Config.DEBUG && ViewDebug.showFps) {int now = (int)SystemClock.elapsedRealtime();if (sDrawTime != 0) {nativeShowFPS(canvas, now - sDrawTime);}sDrawTime = now;}if (Config.DEBUG && ViewDebug.profileDrawing) {EventLog.writeEvent(60000, SystemClock.elapsedRealtime() - startTime);}}} finally {//unlock画布surface.unlockCanvasAndPost(canvas);}if (LOCAL_LOGV) {Log.v("ViewRoot", "Surface " + surface + " unlockCanvasAndPost");}if (scrolling) {mFullRedrawNeeded = true;scheduleTraversals();}}
总结下,ViewRoot能处理Handler的消息,Activity的显示就是由ViewRoot在它的performTraversals函数中完成的。
activity的绘制就是从mSurface中lock一块canvas,然后交给mView去画画,最后unlock并释放这块canvas。
surface
在ViewRoot构造时候,会创建一个surface,viewroot通过IWindowSession和WMS交互,WMS中调用的attach函数会构造一个SurfaceSession。而ViewRoot在performTraversal的过程会调用IWindowSession的relayout函数。
从relayout开始,relayout是一个跨进程调用,实际工作由WMS完成。
客户端的relayoutWindow:
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;int relayoutResult = sWindowSession.relayout(mWindow, params,(int) (mView.mMeasuredWidth * appScale + 0.5f),(int) (mView.mMeasuredHeight * appScale + 0.5f),viewVisibility, insetsPending, mWinFrame,mPendingContentInsets, mPendingVisibleInsets,mPendingConfiguration, mSurface);//传递了surfaceif (restore) {params.restore();}if (mTranslator != null) {mTranslator.translateRectInScreenToAppWinFrame(mWinFrame);mTranslator.translateRectInScreenToAppWindow(mPendingContentInsets);mTranslator.translateRectInScreenToAppWindow(mPendingVisibleInsets);}return relayoutResult;}
服务端的relayoutWindow
frameworks/base/services/java/com/android/server/WindowManagerService.javapublic int relayoutWindow(Session session, IWindow client,WindowManager.LayoutParams attrs, int requestedWidth,int requestedHeight, int viewVisibility, boolean insetsPending,Rect outFrame, Rect outContentInsets, Rect outVisibleInsets,Configuration outConfig, Surface outSurface) {boolean displayed = false;boolean inTouchMode;boolean configChanged;long origId = Binder.clearCallingIdentity();synchronized(mWindowMap) {WindowState win = windowForClientLocked(session, client, false);if (win == null) {return 0;}win.mRequestedWidth = requestedWidth;win.mRequestedHeight = requestedHeight;if (attrs != null) {mPolicy.adjustWindowParamsLw(attrs);}int attrChanges = 0;int flagChanges = 0;if (attrs != null) {flagChanges = win.mAttrs.flags ^= attrs.flags;attrChanges = win.mAttrs.copyFrom(attrs);}if (DEBUG_LAYOUT) Slog.v(TAG, "Relayout " + win + ": " + win.mAttrs);if ((attrChanges & WindowManager.LayoutParams.ALPHA_CHANGED) != 0) {win.mAlpha = attrs.alpha;}final boolean scaledWindow =((win.mAttrs.flags & WindowManager.LayoutParams.FLAG_SCALED) != 0);if (scaledWindow) {// requested{Width|Height} Surface's physical size// attrs.{width|height} Size on screenwin.mHScale = (attrs.width != requestedWidth) ?(attrs.width / (float)requestedWidth) : 1.0f;win.mVScale = (attrs.height != requestedHeight) ?(attrs.height / (float)requestedHeight) : 1.0f;} else {win.mHScale = win.mVScale = 1;}boolean imMayMove = (flagChanges&(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM |WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)) != 0;boolean focusMayChange = win.mViewVisibility != viewVisibility|| ((flagChanges&WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) != 0)|| (!win.mRelayoutCalled);boolean wallpaperMayMove = win.mViewVisibility != viewVisibility&& (win.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0;win.mRelayoutCalled = true;final int oldVisibility = win.mViewVisibility;win.mViewVisibility = viewVisibility;if (viewVisibility == View.VISIBLE &&(win.mAppToken == null || !win.mAppToken.clientHidden)) {displayed = !win.isVisibleLw();if (win.mExiting) {win.mExiting = false;win.mAnimation = null;}if (win.mDestroying) {win.mDestroying = false;mDestroySurface.remove(win);}if (oldVisibility == View.GONE) {win.mEnterAnimationPending = true;}if (displayed) {if (win.mSurface != null && !win.mDrawPending&& !win.mCommitDrawPending && !mDisplayFrozen&& mPolicy.isScreenOn()) {applyEnterAnimationLocked(win);}if ((win.mAttrs.flags& WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON) != 0) {if (DEBUG_VISIBILITY) Slog.v(TAG,"Relayout window turning screen on: " + win);win.mTurnOnScreen = true;}int diff = 0;if (win.mConfiguration != mCurConfiguration&& (win.mConfiguration == null|| (diff=mCurConfiguration.diff(win.mConfiguration)) != 0)) {win.mConfiguration = mCurConfiguration;if (DEBUG_CONFIGURATION) {Slog.i(TAG, "Window " + win + " visible with new config: "+ win.mConfiguration + " / 0x"+ Integer.toHexString(diff));}outConfig.setTo(mCurConfiguration);}}if ((attrChanges&WindowManager.LayoutParams.FORMAT_CHANGED) != 0) {// To change the format, we need to re-build the surface.win.destroySurfaceLocked();displayed = true;}try {//win就是WinStat,创建本地的surface对象Surface surface = win.createSurfaceLocked();if (surface != null) {//在outSurface调用copyFrom,将本地surface拷贝到outSurface中outSurface.copyFrom(surface);win.mReportDestroySurface = false;win.mSurfacePendingDestroy = false;if (SHOW_TRANSACTIONS) Slog.i(TAG," OUT SURFACE " + outSurface + ": copied");} else {// For some reason there isn't a surface. Clear the// caller's object so they see the same state.outSurface.release();}} catch (Exception e) {Slog.w(TAG, "Exception thrown when creating surface for client "+ client + " (" + win.mAttrs.getTitle() + ")",e);Binder.restoreCallingIdentity(origId);return 0;}if (displayed) {focusMayChange = true;}if (win.mAttrs.type == TYPE_INPUT_METHOD&& mInputMethodWindow == null) {mInputMethodWindow = win;imMayMove = true;}if (win.mAttrs.type == TYPE_BASE_APPLICATION&& win.mAppToken != null&& win.mAppToken.startingWindow != null) {// Special handling of starting window over the base// window of the app: propagate lock screen flags to it,// to provide the correct semantics while starting.final int mask =WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED| WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD| WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON;WindowManager.LayoutParams sa = win.mAppToken.startingWindow.mAttrs;sa.flags = (sa.flags&~mask) | (win.mAttrs.flags&mask);}} else {win.mEnterAnimationPending = false;if (win.mSurface != null) {if (DEBUG_VISIBILITY) Slog.i(TAG, "Relayout invis " + win+ ": mExiting=" + win.mExiting+ " mSurfacePendingDestroy=" + win.mSurfacePendingDestroy);// If we are not currently running the exit animation, we// need to see about starting one.if (!win.mExiting || win.mSurfacePendingDestroy) {// Try starting an animation; if there isn't one, we// can destroy the surface right away.int transit = WindowManagerPolicy.TRANSIT_EXIT;if (win.getAttrs().type == TYPE_APPLICATION_STARTING) {transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE;}if (!win.mSurfacePendingDestroy && win.isWinVisibleLw() &&applyAnimationLocked(win, transit, false)) {focusMayChange = true;win.mExiting = true;mKeyWaiter.finishedKey(session, client, true,KeyWaiter.RETURN_NOTHING);} else if (win.isAnimating()) {// Currently in a hide animation... turn this into// an exit.win.mExiting = true;} else if (win == mWallpaperTarget) {// If the wallpaper is currently behind this// window, we need to change both of them inside// of a transaction to avoid artifacts.win.mExiting = true;win.mAnimating = true;} else {if (mInputMethodWindow == win) {mInputMethodWindow = null;}win.destroySurfaceLocked();}}}if (win.mSurface == null || (win.getAttrs().flags& WindowManager.LayoutParams.FLAG_KEEP_SURFACE_WHILE_ANIMATING) == 0|| win.mSurfacePendingDestroy) {// We are being called from a local process, which// means outSurface holds its current surface. Ensure the// surface object is cleared, but we don't want it actually// destroyed at this point.win.mSurfacePendingDestroy = false;outSurface.release();if (DEBUG_VISIBILITY) Slog.i(TAG, "Releasing surface in: " + win);} else if (win.mSurface != null) {if (DEBUG_VISIBILITY) Slog.i(TAG,"Keeping surface, will report destroy: " + win);win.mReportDestroySurface = true;outSurface.copyFrom(win.mSurface);}}if (focusMayChange) {//System.out.println("Focus may change: " + win.mAttrs.getTitle());if (updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES)) {imMayMove = false;}//System.out.println("Relayout " + win + ": focus=" + mCurrentFocus);}// updateFocusedWindowLocked() already assigned layers so we only need to// reassign them at this point if the IM window state gets shuffledboolean assignLayers = false;if (imMayMove) {if (moveInputMethodWindowsIfNeededLocked(false) || displayed) {// Little hack here -- we -should- be able to rely on the// function to return true if the IME has moved and needs// its layer recomputed. However, if the IME was hidden// and isn't actually moved in the list, its layer may be// out of data so we make sure to recompute it.assignLayers = true;}}if (wallpaperMayMove) {if ((adjustWallpaperWindowsLocked()&ADJUST_WALLPAPER_LAYERS_CHANGED) != 0) {assignLayers = true;}}mLayoutNeeded = true;win.mGivenInsetsPending = insetsPending;if (assignLayers) {assignLayersLocked();}configChanged = updateOrientationFromAppTokensLocked();performLayoutAndPlaceSurfacesLocked();if (displayed && win.mIsWallpaper) {updateWallpaperOffsetLocked(win, mDisplay.getWidth(),mDisplay.getHeight(), false);}if (win.mAppToken != null) {win.mAppToken.updateReportedVisibilityLocked();}outFrame.set(win.mFrame);outContentInsets.set(win.mContentInsets);outVisibleInsets.set(win.mVisibleInsets);if (localLOGV) Slog.v(TAG, "Relayout given client " + client.asBinder()+ ", requestedWidth=" + requestedWidth+ ", requestedHeight=" + requestedHeight+ ", viewVisibility=" + viewVisibility+ "\nRelayout returning frame=" + outFrame+ ", surface=" + outSurface);if (localLOGV || DEBUG_FOCUS) Slog.v(TAG, "Relayout of " + win + ": focusMayChange=" + focusMayChange);inTouchMode = mInTouchMode;}if (configChanged) {sendNewConfiguration();}Binder.restoreCallingIdentity(origId);return (inTouchMode ? WindowManagerImpl.RELAYOUT_IN_TOUCH_MODE : 0)| (displayed ? WindowManagerImpl.RELAYOUT_FIRST_TIME : 0);}
createSurfaceLocked
frameworks/base/services/java/com/android/server/WindowManagerService.java:WindowStateSurface createSurfaceLocked() {if (mSurface == null) {mReportDestroySurface = false;mSurfacePendingDestroy = false;mDrawPending = true;mCommitDrawPending = false;mReadyToShow = false;if (mAppToken != null) {mAppToken.allDrawn = false;}int flags = 0;if (mAttrs.memoryType == MEMORY_TYPE_PUSH_BUFFERS) {flags |= Surface.PUSH_BUFFERS;}if ((mAttrs.flags&WindowManager.LayoutParams.FLAG_SECURE) != 0) {flags |= Surface.SECURE;}if (DEBUG_VISIBILITY) Slog.v(TAG, "Creating surface in session "+ mSession.mSurfaceSession + " window " + this+ " w=" + mFrame.width()+ " h=" + mFrame.height() + " format="+ mAttrs.format + " flags=" + flags);int w = mFrame.width();int h = mFrame.height();if ((mAttrs.flags & LayoutParams.FLAG_SCALED) != 0) {// for a scaled surface, we always want the requested// size.w = mRequestedWidth;h = mRequestedHeight;}// Something is wrong and SurfaceFlinger will not like this,// try to revert to sane valuesif (w <= 0) w = 1;if (h <= 0) h = 1;mSurfaceShown = false;mSurfaceLayer = 0;mSurfaceAlpha = 1;mSurfaceX = 0;mSurfaceY = 0;mSurfaceW = w;mSurfaceH = h;try {mSurface = new Surface(mSession.mSurfaceSession, mSession.mPid,mAttrs.getTitle().toString(),0, w, h, mAttrs.format, flags);if (SHOW_TRANSACTIONS) Slog.i(TAG, " CREATE SURFACE "+ mSurface + " IN SESSION "+ mSession.mSurfaceSession+ ": pid=" + mSession.mPid + " format="+ mAttrs.format + " flags=0x"+ Integer.toHexString(flags)+ " / " + this);} catch (Surface.OutOfResourcesException e) {Slog.w(TAG, "OutOfResourcesException creating surface");reclaimSomeSurfaceMemoryLocked(this, "create");return null;} catch (Exception e) {Slog.e(TAG, "Exception creating surface", e);return null;}if (localLOGV) Slog.v(TAG, "Got surface: " + mSurface+ ", set left=" + mFrame.left + " top=" + mFrame.top+ ", animLayer=" + mAnimLayer);if (SHOW_TRANSACTIONS) {Slog.i(TAG, ">>> OPEN TRANSACTION");if (SHOW_TRANSACTIONS) logSurface(this,"CREATE pos=(" + mFrame.left + "," + mFrame.top + ") (" +mFrame.width() + "x" + mFrame.height() + "), layer=" +mAnimLayer + " HIDE", null);}Surface.openTransaction();//打开一个事务try {try {mSurfaceX = mFrame.left + mXOffset;mSurfaceY = mFrame.top + mYOffset;mSurface.setPosition(mSurfaceX, mSurfaceY);mSurfaceLayer = mAnimLayer;mSurface.setLayer(mAnimLayer);mSurfaceShown = false;mSurface.hide();if ((mAttrs.flags&WindowManager.LayoutParams.FLAG_DITHER) != 0) {if (SHOW_TRANSACTIONS) logSurface(this, "DITHER", null);mSurface.setFlags(Surface.SURFACE_DITHER,Surface.SURFACE_DITHER);}} catch (RuntimeException e) {Slog.w(TAG, "Error creating surface in " + w, e);reclaimSomeSurfaceMemoryLocked(this, "create-init");}mLastHidden = true;} finally {if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION");Surface.closeTransaction();//关闭事务}if (localLOGV) Slog.v(TAG, "Created surface " + this);}return mSurface;}
jni层分析
Surface无参构造
frameworks/base/core/java/android/view/Surface.java/*** Create an empty surface, which will later be filled in by* readFromParcel().* {@hide}*/public Surface() {if (DEBUG_RELEASE) {mCreationStack = new Exception();}//CompatibleCanvas继承自CanvasmCanvas = new CompatibleCanvas();}
canvas 根据文档可以知道,canvas相关的类有几个。
- Bitmap:存储像素,就是画布
- canvas:记载画图的工作,比如画圆形、方形
- drawing primitive:在计算机图形学中绘制基本形状的过程。基本形状通常指的是点、线和三角形。在OpenGL或其他图形渲染API中,绘制基本形状是构建复杂图形的基础。
- paint:描述绘画时候使用的颜色、风格(实线、虚线)
canvas一般会封装一块bitmap,作画就基于这块bitmap。
SurfaceSession
frameworks/base/core/java/android/view/SurfaceSession.java
/*** An instance of this class represents a connection to the surface* flinger, in which you can create one or more Surface instances that will* be composited to the screen.* {@hide}*/
public class SurfaceSession {/** Create a new connection with the surface flinger. */public SurfaceSession() {init();//native函数}
看下这个init的实现:
SurfaceSession_init
frameworks/base/core/jni/android_view_Surface.cpp
static void SurfaceSession_init(JNIEnv* env, jobject clazz)
{
//创建了SurfaceComposerClient对象sp<SurfaceComposerClient> client = new SurfaceComposerClient;client->incStrong(clazz);//在java对象中保存它env->SetIntField(clazz, sso.client, (int)client.get());
}
surface的有参构造
frameworks/base/core/java/android/view/Surface.java/*** create a surface with a name* {@hide}*/public Surface(SurfaceSession s,//传入SurfaceSessionint pid, String name, int display, int w, int h, int format, int flags)throws OutOfResourcesException {if (DEBUG_RELEASE) {mCreationStack = new Exception();}mCanvas = new CompatibleCanvas();//native函数,传递了display,w h是宽高init(s,pid,name,display,w,h,format,flags);mName = name;}
看下 init(s,pid,name,display,w,h,format,flags);
frameworks/base/core/jni/android_view_Surface.cpp
static void Surface_init(JNIEnv* env, jobject clazz, jobject session,jint pid, jstring jname, jint dpy, jint w, jint h, jint format, jint flags)
{if (session == NULL) {doThrow(env, "java/lang/NullPointerException");return;}//从 SurfaceSession对象中取出之前创建的之前创建的SurfaceComposerClient对象SurfaceComposerClient* client =(SurfaceComposerClient*)env->GetIntField(session, sso.client);//SurfaceControlsp<SurfaceControl> surface;if (jname == NULL) {surface = client->createSurface(pid, dpy, w, h, format, flags);} else {const jchar* str = env->GetStringCritical(jname, 0);const String8 name(str, env->GetStringLength(jname));env->ReleaseStringCritical(jname, str);//调用SurfaceComposerClient的createSurface,返回的是是SurfaceControlsurface = client->createSurface(pid, name, dpy, w, h, format, flags);}if (surface == 0) {doThrow(env, OutOfResourcesException);return;}//把SurfaceControl设置到java层的surface对象中setSurfaceControl(env, clazz, surface);
}
Surface_copyFrom
static void Surface_copyFrom(JNIEnv* env, jobject clazz, jobject other)
{if (clazz == other)return;if (other == NULL) {doThrow(env, "java/lang/NullPointerException", NULL);return;}/** This is used by the WindowManagerService just after constructing* a Surface and is necessary for returning the Surface reference to* the caller. At this point, we should only have a SurfaceControl.*/const sp<SurfaceControl>& surface = getSurfaceControl(env, clazz);const sp<SurfaceControl>& rhs = getSurfaceControl(env, other);if (!SurfaceControl::isSameSurface(surface, rhs)) {// we reassign the surface only if it's a different one// otherwise we would loose our client-side state.setSurfaceControl(env, clazz, rhs);}
}
Surface_writeToParcel
static void Surface_writeToParcel(JNIEnv* env, jobject clazz, jobject argParcel, jint flags)
{Parcel* parcel = (Parcel*)env->GetIntField(argParcel, no.native_parcel);if (parcel == NULL) {doThrow(env, "java/lang/NullPointerException", NULL);return;}const sp<SurfaceControl>& control(getSurfaceControl(env, clazz));SurfaceControl::writeSurfaceToParcel(control, parcel);if (flags & PARCELABLE_WRITE_RETURN_VALUE) {setSurfaceControl(env, clazz, 0);}
}
Surface_readFromParcel
static void Surface_readFromParcel(JNIEnv* env, jobject clazz, jobject argParcel)
{Parcel* parcel = (Parcel*)env->GetIntField( argParcel, no.native_parcel);if (parcel == NULL) {doThrow(env, "java/lang/NullPointerException", NULL);return;}const sp<Surface>& control(getSurface(env, clazz));sp<Surface> rhs = new Surface(*parcel);if (!Surface::isSameSurface(control, rhs)) {// we reassign the surface only if it's a different one// otherwise we would loose our client-side state.setSurface(env, clazz, rhs);}
}
这里面WMS的surface调用了带参的SurfaceSession构造函数,ViewRoot的surface调用了无参的构造函数。
copyFrom就是把WMS的surface信息,拷贝到ViewRoot中的surface中。这其中使用了AIDL,xxx.aidl文件可以转换为java文件.
总结
我们来回顾和总结一路过来的分析,为后续破解surfaceflinger做准备。
创建了一个SurfaeComposerClient,调用它的createSurface,拿到一个SurfaceControl对象。调用SurfaceControl的writeToParcel把信息写道parcel中。根据parcel的信息构造一个surface对象,并保存到java层的mSurface对象中。
这样viewroot得到了一个native的surface对象。
参考资料
《深入理解Android:卷1》