项目中有用到SurfaceView 作视频播放,可是一直有一个问题,测试哥们今天又从新提了,貌似上任开发者并无解决,看看吧。。。
java
本身试了试,确实是有这样的bug,可是是报ANR,奇了怪了,全屏放大不至于会报ANR啊,看了看代码,发现全屏播放实际是建立了一个包含surfaceView新的activity,也就是又建立一个SurfaceView,搞不懂为何这么设计,orz….android
首先,看一下日志 找半天才找到web
Surface has already been released.sql
接着 抓一下traces日志看看(adb pull data/anr/traces.txt)canvas
"main" prio=5 tid=1 Waiting
app
| group="main" sCount=1 dsCount=0 obj=0x75372268 self=0xaabd3ce0
| sysTid=3980 nice=-1 cgrp=top_visible sched=0/0 handle=0xf7762b50
| state=S schedstat=( 22407689743 17485653812 44070 ) utm=1882 stm=358 core=5 HZ=100
| stack=0xff307000-0xff309000 stackSize=8MB
| held mutexes=
at java.lang.Object.wait!(Native method)
- waiting on <0x0488bcf7> (a java.lang.Object)
at java.lang.Thread.parkFor$(Thread.java:1235)
- locked <0x0488bcf7> (a java.lang.Object)
at sun.misc.Unsafe.park(Unsafe.java:299)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:158)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:810)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:843)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1172)
at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:181)
at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:257)
at android.view.SurfaceView.updateWindow(SurfaceView.java:517)
at android.view.SurfaceView.onWindowVisibilityChanged(SurfaceView.java:246)
at android.view.View.dispatchWindowVisibilityChanged(View.java:9737)
at android.view.ViewGroup.dispatchWindowVisibilityChanged(ViewGroup.java:1309)
at android.view.ViewGroup.dispatchWindowVisibilityChanged(ViewGroup.java:1309)
at android.view.ViewGroup.dispatchWindowVisibilityChanged(ViewGroup.java:1309)
... repeated 10 times
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1415)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1139)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6238)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:884)
at android.view.Choreographer.doCallbacks(Choreographer.java:696)
at android.view.Choreographer.doFrame(Choreographer.java:631)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:870)
at android.os.Handler.handleCallback(Handler.java:743)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:150)
at android.app.ActivityThread.main(ActivityThread.java:5621)
at java.lang.reflect.Method.invoke!(Native method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:794)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:684)
日志最直接应该就是SurfaceView致使的主线程等待问题
at java.lang.Object.wait!(Native method)
at android.view.SurfaceView.updateWindow(SurfaceView.java:517)ide
直接粗暴看代码,先看SurfaceView.updateWindow源码部分:
最关键的一部分,mSurfaceLock获取lock对象svg
mSurfaceLock.lock();
相应的,有获取就会有释放lock,绘制视频时会有释放lock的操做(m_surfaceHolder.unlockCanvasAndPost(m_canvas);)oop
try {
if(m_surfaceHolder.getSurface().isValid()){
m_canvas = m_surfaceHolder.lockCanvas(null);
if(m_canvas!=null){
m_canvas.drawColor(Color.WHITE);
m_bitmap.copyPixelsFromBuffer(m_buffer);
m_buffer.clear();
Log.i("------", "---------m_winWidth---------" + m_winWidth + "---------------m_winHeight-----------------" + m_winHeight);
m_viewRect.set(0, 0, m_winWidth, m_winHeight);
m_canvas.drawBitmap(m_bitmap, null, m_viewRect, m_paint);
}
}
} catch (Exception e) {
return;
} finally {
if(m_surfaceHolder.getSurface().isValid()){
m_surfaceHolder.unlockCanvasAndPost(m_canvas);
}
// Log.i("------","---------unlockCanvasAndPost---------");
}
缘由:SurfaceView在建立时会获取lock,可是没有unlock,SurfaceView被销毁 再次建立时会出现等待lock的状况,就会出现ANR。。。。测试
总结本次ANR过程:
第一步:执行了mHolder.lockCanvas(),lock成功得到锁
第二步:此时恰巧遇到SurfaceView销毁,surfaceDestroyed执行,而且将mNativeObject置为0
第三步:调用unlockCanvasAndPost,可是因为mNativeObject为0,因此抛出异常,并无成功unlock
第四步:SurfaceView从新建立,尝试lock,由于上次的锁没有释放,因此进入了无限等待。
解决办法:
在绘制视频和销毁的过程当中都加上同步锁
synchronized (this.holder) {
Canvas c = null;
try {
c = holder.lockCanvas(null);
if (c != null) {
c.drawColor(Color.BLACK);
c.drawARGB(0, 0, 0, 0);
if (!msg.isEmpty()) {
Paint paint = new Paint();
paint.setColor(Color.WHITE);
paint.setTextSize((float) 25.0);
paint.setTextAlign(Paint.Align.CENTER);
c.drawText(msg, width / 2, height / 2, paint);
}
}
} catch (Exception e) {
Log.i("------", e.getMessage());
} finally {
holder.unlockCanvasAndPost(c);
}
}
surfaceDestroyed方法中(SurfaceView不可见时):
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
synchronized (this.holder) {
Logger.t(TAG).d("surfaceDestroyed--------" + Config.isShow);
}
}