【Android surface 】二:源码分析App的surface创建过程

文章目录

  • 画布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》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/817991.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Hive上篇: 一篇文章带你使用Hive!深入了解Hive!学会Hive!】

前言&#xff1a; &#x1f49e;&#x1f49e;大家好&#xff0c;我是书生♡&#xff0c;本篇文章主要分享的是大数据开发中hive的相关技术&#xff0c;什么是Hive&#xff1f;怎么使用Hive&#xff1f;怎么安装部署&#xff1f;希望大家看完这篇文章会有所帮助。也希望大家能够…

Depth maps转点云

前言 本文主要记录一下如何可视化相机位姿&#xff0c;如何用Blender得到的深度图反投影到3D空间&#xff0c;得到相应的点云。 Refernce https://github.com/colmap/colmap/issues/1106 https://github.com/IntelRealSense/librealsense/issues/12090 https://medium.com/yod…

【详细讲解下Photoshop】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

鸿蒙 Failed :entry:default@CompileResource...

Failed :entry:defaultCompileResource... media 文件夹下有文件夹或者图片名称包含中文字符 rawfile 文件夹下文件名称、图片名称不能包含中文字符

地理空间分析中的深度学习应用

深度学习与地理信息系统 (GIS) 的结合彻底改变了地理空间分析和遥感的格局。这种结合将遥感和地理空间分析领域带到了全球研究人员和科学家的前沿。 深度学习是机器学习的一个复杂子集&#xff08;更多关于机器学习的内容&#xff0c;请参阅我的其他文章&#xff09;&#xff0…

绿色地狱steam叫什么 绿色地狱steam怎么搜

绿色地狱steam叫什么 绿色地狱steam怎么搜 《绿色地狱》是一款以亚马逊雨林为背景的开放世界生存模拟游戏。玩家们扮演一名被困在丛林中的冒险者&#xff0c;玩家在游戏内需要学习采集资源、建造庇护所、狩猎和烹饪食物&#xff0c;同时要面对丛林中的危险和挑战&#xff0c;…

太好玩了,我用 Python 做了一个 ChatGPT 机器人

毫无疑问&#xff0c;ChatGPT 已经是当下编程圈最火的话题之一&#xff0c;它不仅能够回答各类问题&#xff0c;甚至还能执行代码&#xff01; 或者是变成一只猫 因为它实在是太好玩&#xff0c;我使用Python将ChatGPT改造&#xff0c;可以实现在命令行或者Python代码中调用。…

langchain 链式写法-使用本地 embedding 模型,Faiss 检索

目录 示例代码1 示例代码2 示例代码1 使用本地下载的 embedding 模型去做 embedding&#xff0c;然后从中查相似的 import os from dotenv import load_dotenv from langchain_community.llms import Tongyi load_dotenv(key.env) # 指定加载 env 文件 key os.getenv(DAS…

ansible创建用户账户和更新ansible库的密钥

1.创建⽤户帐户 从 http://materials/user_list.yml 下载要创建的⽤户的列表&#xff0c;并将它保存到 /home/greg/ansible 在本次考试中使⽤在其他位置创建的密码库 /home/greg/ansible/locker.yml 。创建名为 /home/greg/ansible/users.yml 的 playbook &#xff0c;从⽽…

探索顶级短视频素材库:多样化选择助力创作

在数字创作的浪潮中&#xff0c;寻找优质的短视频素材库是每位视频制作者的必经之路。多种短视频素材库有哪些&#xff1f;这里为您介绍一系列精选的素材库&#xff0c;它们不仅丰富多样&#xff0c;而且高质量&#xff0c;能极大地提升您的视频创作效率和质量。 1.蛙学网 蛙学…

springboot+Vue项目部署到云服务器上

一、下载配置ngnix 1.压缩包下载并上传 链接: https://pan.baidu.com/s/1m2LKV8ci4WXkAWdJXIeUFQ 提取码: 0415 2.解压 tar -xzvf 压缩包名 3.编译nginx 在解压好的文件夹下,依次执行&#xff1a; ./configure 来到nginx默认安装路径/usr/local/nginx 依次执行命令 mak…

【鸿蒙开发】饿了么页面练习

0. 整体结构 整体划分3部分。店铺部分&#xff0c;购物车部分&#xff0c;金额统计部分。使用 Stack 把3部分堆叠 0.1 整体页面 Index.ets 修改 Index.ets &#xff0c;使用堆叠布局&#xff0c;并居底部对齐 import { ElShop } from ../components/ElShop import { ElShopp…

C#版Facefusion ,换脸器和增强器

C#版Facefusion &#xff0c;换脸器和增强器 目录 说明 效果 项目 调用代码 说明 Facefusion是一款最新的开源AI视频/图片换脸项目。是原来ROOP的项目的延续。项目官方介绍只有一句话&#xff0c;下一代换脸器和增强器。 代码实现参考 https://github.com/facefusion/f…

软件工程及开发模型

根据希赛相关视频课程汇总整理而成&#xff0c;个人笔记&#xff0c;仅供参考。 软件工程的基本要素包括方法、工具和&#xff08;过程&#xff09; 方法&#xff1a;完成软件开发的各项任务的技术方法&#xff1b; 工具&#xff1a;运用方法而提供的软件工程支撑环境&#xff…

基于STM32的RFID智能门锁系统

本文针对RFID技术&#xff0c;着重研究了基于单片机的智能门锁系统设计。首先&#xff0c;通过链接4*4按键模块与主控STM32&#xff0c;实现了多种模式&#xff0c;包括刷卡开锁、卡号权限管理、密码开锁、修改密码、显示实时时间等功能。其次&#xff0c;采用RC522模块与主控S…

【学习笔记十四】EWM发货流程概述及相关配置

一、EWM发货流程与ERP集成配置 1.将凭证类型从 ERP 系统映射至 EWM ERP交货单凭证类型LF映射到EWM凭证类型OUTB 2.从 ERP 系统映射项目类型至 EWM ERP交货单凭证类型+ERP交货单项目类型TAN映射到EWM项目类型是ODLV 3.定义出库交货的参数文件 ①定义外向交货处理的凭证类型OUT…

Mamba论文笔记

Mamba论文 结合序列建模任务通俗地解释什么是状态空间模型&#xff1f;创新点和贡献 为什么Mamba模型擅长捕获long range dependencies&#xff1f; 结合序列建模任务通俗地解释什么是状态空间模型&#xff1f; 状态空间模型&#xff08;State Space Model, SSM&#xff09;是…

举个栗子!Tableau 技巧(270):用 Lookup 函数创建多 KPI 文本表

在 Tableau 中&#xff0c;文本表常用于呈现明细数据。但其实&#xff0c;数据粉如果想在同一视图中查看多个数据指标&#xff0c;也可以用到文本表。 如下示例&#xff0c;是不是很直观的就可以查看&#xff1a;不同区域随时间推移的数据指标情况呢&#xff1f; 如何在 Tablea…

app证书在设置在哪

根据近日工业和信息化部发布的《工业和信息化部关于开展移动互联网应用程序备案工作的通知》&#xff0c;相信不少要进行IOS平台App备案的朋友遇到了一个问题&#xff0c;就是apple不提供云管理式证书的下载&#xff0c;也就无法获取公钥及证书SHA-1指纹。 已经上架的应用不想重…

Langchain入门到实战-第二弹

Langchain入门到实战 Langchain快速入门官网地址Langchain概述Langchain调用大模型更新计划 Langchain快速入门 官网地址 声明: 由于操作系统, 版本更新等原因, 文章所列内容不一定100%复现, 还要以官方信息为准 https://python.langchain.com/Langchain概述 LangChain是一个…