[Android] How to troubleshoot the problem that SurfaceView cannot be normally displayed on another Window
The video is busy recently, and SurfaceView must be used for video rendering. In order to achieve the floating layer effect, we naturally think of using multiple windows. But the problem arises. When you place your SurfaceView in another window, everything becomes abnormal. To verify this, I wrote a small demo:
The code is very simple. Press the button in the middle to bring up a Window, which stores a simple SurfaceView, and the top-level View of the Window is a FrameLayout. Window parameter:
private WindowManager.LayoutParams getWindowLayoutParams() { mWindowLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL; mWindowLayoutParams.setTitle(This is a test); mWindowLayoutParams.width = WindowManager.LayoutParams.MATCH_PARENT; mWindowLayoutParams.height = WindowManager.LayoutParams.MATCH_PARENT; mWindowLayoutParams.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; mWindowLayoutParams.token = MainActivity.this.getWindow().getDecorView().getWindowToken(); return mWindowLayoutParams; }
Okay, let's run and we will findThe interface has not changed, but the buttons on the interface cannot be clicked.. What does this mean? DescriptionYour Window has been received by the system Window management service, but the interface shows a problem. We add a SurfaceView callback to SurfaceView and print the Log at surfaceCreated. You will find that this callback is not implemented at all.
@Overridepublic void surfaceCreated(SurfaceHolder holder) {// TODO Auto-generated method stubLog.v(surface, surfaceCreated);}
Here I insert a callback to tell you that the Surface allocated by SurfaceFlinger is available, but this callback does not take place, this means that your Surface is unavailable in the current state, that is, the Surface allocated by SurfaceFlinger is unavailable. Maybe you are confused here, but it doesn't matter,@ NonzimoBrother was a little curious at the beginning, but you should wait and think about it again. A process applying for Surfacce from SurfaceFlinger does not apply for SurfaceFlinger service directly, but does apply to the WindowManager service, it may also be caused by it. We are sorting out the problems we encountered. We added a Window to Window management, but we saw a transparent SurfaceView without the surfacceCreate callback. In fact, there are two problems: transparency and no callback.
Let's first solve the first problem of transparency. After we set the background in FrameLayout on the top layer, we found that it was still transparent. Why? This is because the SurfaceView object is very special when applying for a display area. It is not a buffer to overlay the painting with your UI thread, we can simply understand that it opens a mouth on the Buffer drawn by the UI thread and then draws it on its own Buffer. So how can we solve the problem of transparency? In fact, it is very simple. You only need to set a background for SurfaceView and tell the rendering service that your SurfaceView is non-transparent. I set a blue background for SurfaceView and ran it to see the effect:
Now we have solved the first problem: transparency. Next, let's look at the second problem: SurfaceView does not callback. We just guessed the problem that the Surface object is invalid. to verify our problem, we printed the Surface object parameter in SurfaceView:
SurfaceView. java: private void updateWindow (boolean force, boolean redrawNeeded) {... relayoutResult = mSession. relayout (mWindow, mWindow. mSeq, mLayout, mWidth, mHeight, visible? VISIBLE: GONE, WindowManagerGlobal. RELAYOUT_DEFER_SURFACE_DESTROY, mWinFrame, mOverscanInsets, mContentInsets, mVisibleInsets, mConfiguration, mNewSurface);/* check whether mSurface is available */booelan result = mSurface. isValid ();...}As expected, the value of result is false. Let's take a look at the implementation of mSurface. isValid:
public boolean isValid() { synchronized (mLock) { if (mNativeObject == 0) return false; return nativeIsValid(mNativeObject); } }
It can be seen that the handle of the mNativeObject object is null, that is, the system has not allocated you a memory handle. At this time, I don't know if you will give up and tell myself that this is a system problem. In fact, you are very close to the truth, just stick to it for a while. Let's take a look at the WMS log:
W/WindowManager( 1154): Attempted to add window with token that is a sub-window: android.os.BinderProxy@432d6290. Aborting.W/WindowManager( 1154): Failed looking up windowW/WindowManager( 1154): java.lang.IllegalArgumentException: Requested window android.os.BinderProxy@43296170 does not existW/WindowManager( 1154): at com.android.server.wm.WindowManagerService.windowForClientLocked(WindowManagerService.java:7981)W/WindowManager( 1154): at com.android.server.wm.WindowManagerService.windowForClientLocked(WindowManagerService.java:7972)W/WindowManager( 1154): at com.android.server.wm.WindowManagerService.relayoutWindow(WindowManagerService.java:2784)W/WindowManager( 1154): at com.android.server.wm.Session.relayout(Session.java:190)W/WindowManager( 1154): at android.view.IWindowSession$Stub.onTransact(IWindowSession.java:235)W/WindowManager( 1154): at com.android.server.wm.Session.onTransact(Session.java:125)W/WindowManager( 1154): at android.os.Binder.execTransact(Binder.java:404)W/WindowManager( 1154): at dalvik.system.NativeStart.run(Native Method)W/WindowManager( 1154): Failed looking up windowW/WindowManager( 1154): java.lang.IllegalArgumentException: Requested window android.os.BinderProxy@43296170 does not existW/WindowManager( 1154): at com.android.server.wm.WindowManagerService.windowForClientLocked(WindowManagerService.java:7981)W/WindowManager( 1154): at com.android.server.wm.WindowManagerService.windowForClientLocked(WindowManagerService.java:7972)W/WindowManager( 1154): at com.android.server.wm.WindowManagerService.finishDrawingWindow(WindowManagerService.java:3105)W/WindowManager( 1154): at com.android.server.wm.Session.finishDrawing(Session.java:224)W/WindowManager( 1154): at android.view.IWindowSession$Stub.onTransact(IWindowSession.java:372)W/WindowManager( 1154): at com.android.server.wm.Session.onTransact(Session.java:125)W/WindowManager( 1154): at android.os.Binder.execTransact(Binder.java:404)W/WindowManager( 1154): at dalvik.system.NativeStart.run(Native Method)
We think that the reason for the Second stack is the first stack, and the reason for the first stack is probably because of this sentence:
W/WindowManager( 1154): Attempted to add window with token that is a sub-window: android.os.BinderProxy@432d6290. Aborting.
This is actually not an exception. It can be regarded as a system prompt, that is, it treats our Window as a simple sub-window. Let's take a look at the implementation of the WMS code:
public int addWindow(Session session, IWindow client, int seq, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, Rect outContentInsets, InputChannel outInputChannel) {...if (attachedWindow.mAttrs.type >= FIRST_SUB_WINDOW && attachedWindow.mAttrs.type <= LAST_SUB_WINDOW) { Slog.w(TAG, Attempted to add window with token that is a sub-window: + attrs.token + . Aborting.); return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN; }...if (addToken) { mTokenMap.put(attrs.token, token); }...}
We found that when you add a Window as a sub-Window, the system service will return directly, so that you cannot store your Token record in mTokenMap, and this token does not exist, cause the exception stack of the above two threads. In this way, we are only one step away from success. We have located our problem in this sentence:
mWindowLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL;
Instead, we only need to change the values other than FIRST_SUB_WINDOW and LAST_SUB_WINDOW to solve the problem. Here I chose TYPE_TOAST
private WindowManager.LayoutParams getWindowLayoutParams() { mWindowLayoutParams.type = WindowManager.LayoutParams.TYPE_TOAST; mWindowLayoutParams.setTitle(This is a test); mWindowLayoutParams.width = WindowManager.LayoutParams.MATCH_PARENT; mWindowLayoutParams.height = WindowManager.LayoutParams.MATCH_PARENT; mWindowLayoutParams.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; mWindowLayoutParams.token = MainActivity.this.getWindow().getDecorView().getWindowToken(); return mWindowLayoutParams; }
In this way, the callback of SurfaceView is normal, and all problems are solved at the moment. I hope this article will help the users who are working on this function.