編輯:關於Android編程
最近在忙碌視頻的事情,而視頻的繪制需要使用到SurfaceView。為了完成浮層效果,我們很自然的想到使用多Window的方式。但是問題就來了,當你將你的SurfaceView放置在另外一個window中的時候,一切都變得不正常,為了驗證這個東西,我寫了一個小的demo:
代碼非常簡單,按下中間那個按鈕,彈出一個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設置了一個藍色的背景,跑一下果然看到了效果:
好了,這樣我們解決了第一個問題:透明問題。再次我們來看下第二個問題,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); } }
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); } ... }
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; }
聲音的類型有:定義在AudioSystem.java文件中 /* The default audio stream */public static final
為了在SurfaceView上畫圖,我們定義一個MySurfaceView類,該類繼承SurfaceView並且實現SurfaceHolder.Callback接口。在s
先占個位置,下次翻譯~ :p There are a few scenarios in which your activity is destroyed due t
1、Fragment知識概要Android3.0引入了Fragment,主要目的是用在大屏幕設備上,支持更加動態和靈活的UI設計。Fragment在你的應用中應當是一個模