[Android]浮层视频效果,在另外一个Window使用SurfaceV
发表时间:2020-10-19
发布人:葵宇科技
浏览次数:40
比来在劳碌视频的工作,而视频的绘制须要应用到SurfaceView。为了完成浮层效不雅,我们很天然的想到应用多Window的方法。然则问题就来了,当你将你的SurfaceView放置在别的一个window中的时刻,一切都变得不正常,为了验证这个器械,我写了一个小的demo:
[img]http://img.blog.csdn.net/20150104122520448?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvSGVsbG9fX1plcm8=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center
代码异常简单,按下中心那个按钮,弹出一个Window,这个Window琅绫擎存放一个简单的SurfaceView,而这个Window的顶层View是一个FrameLayout。Window参数为:
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; }
好了,我们跑一下,就会发明界面没有任何变更,然则界面上的按钮都弗成点击。这说清楚明了什么呢?说清楚明了你的Window已经被体系窗口治理办事所接收了,然则,界面显示出问题。我们给SurfaceView 增长一个SurfaceView回调,并在surfaceCreated处打印Log。你会发明,这个回调根本没有走。
@Override public void surfaceCreated(SurfaceHolder holder) { // TODO Auto-generated method stub Log.v("surface", "surfaceCreated"); }
这里我插入一灸┞封个回调的感化,这个回调的感化在于告诉你,SurfaceFlinger给你分派的Surface已经可用了,然则这个回调不走,意味着当缁ご态下你的Surface处于弗采取的状况,也就是SurfaceFlinger给你分派的Surface是弗采取的。或许你到这里已经一头雾水了,不过没紧要,@非子墨兄刚开端也有点好奇,不过你静下来再想想,一个过程向SurfaceFlinger申请Surfacce并不是直接申请SurfaceFlinger办事申请的,而是向WindowManager办事申请的,也有可能是因为它引起的。我们在整顿一下我们碰到的问题。我们增长了一个Window到窗口治理,然则我们看到了一个透明且没有surfacceCreate回调的SurfaceView。实际上这是两个问题:一个问题是透明,一个是没有回调。
我们先来解决第一个透明的问题,我们在顶层FrameLayout设置了背景后,发明照样透明的,这是为什么呢?是因为SurfaceView这个对象申请显示区域的时刻异常特别,并不是跟你的UI线程一个缓冲上叠加绘制,我们可以简单懂得为它在UI线程所绘制的缓冲上开了个口儿,然后在本身的Buffer膳绫擎绘制。那么怎么解决透明的问题呢?其实异常异常的简单,只须要给SurfaceView设置一个背景,告诉绘制办事你的┞封个SurfaceView长短透明的就可以了。我给SurfaceView设置了一个蓝色的背景,跑一下不雅然看到了效不雅:
[img]http://img.blog.csdn.net/20150104124346084?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvSGVsbG9fX1plcm8=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center
好了,如许我们解决了第一个问题:透明问题。再次我们来看下第二个问题,SurfaceView不回调的问题。我们刚才对Surface对象无效的问题都纯属于猜测,为了验证我们的问题我们将SurfaceView中的Surface对象参数打印一下:
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); /*查看mSurface是否可用*/ booelan result = mSurface.isValid(); ... }不雅然不出我们所料,result的值为false。我们来看一下mSurface.isValid()的实现:
public boolean isValid() { synchronized (mLock) { if (mNativeObject == 0) return false; return nativeIsValid(mNativeObject); } }
可见,mNativeObject对象句柄为null,也就是体系并没有分派给你绘制内存句柄。这个时刻,不知道你会不会放弃,告诉本身这是体系的问题,实际上你离本相已经很近了,只要再保持一会儿久煨。我们来看一下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 window W/WindowManager( 1154): java.lang.IllegalArgumentException: Requested window android.os.BinderProxy@43296170 does not exist W/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 window W/WindowManager( 1154): java.lang.IllegalArgumentException: Requested window android.os.BinderProxy@43296170 does not exist W/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)
我们粗浅的认为第二个客栈引起的原因是因为第一个客栈,而第一个客栈引起的原因有可能是因为这句话:
W/WindowManager( 1154): Attempted to add window with token that is a sub-window: android.os.BinderProxy@432d6290. Aborting.这个其实不算是一个异常,可以当成体系提示,也就是说它将我们的Window当成一个简单的sub-window。我们看一下WMS这段代码的实现:
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); } ... }
我们发明,当你以一个子Window的方法参加一个Window的时刻,体系办事直接返回,如许就不克不及往mTokenMap中存放你的Token记录,而这个token不存在,导致了膳绫擎两个线程的异常客栈。如许,我们离成功就只有一步之遥,我们已经定位我们的问题出在这句话:
mWindowLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL;而我们只须要改成FIRST_SUB_WINDOW和LAST_SUB_WINDOW之外的值就可以解决问题了。这里我选用了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; }
如许,SurfaceView的回调就正常了,此刻一切问题都水到渠成。欲望这篇文┞仿能赞助到正在做这项功能的筒子们。
thx