【解疑答惑】—— SurfaceView播放视频 来回切换出现ANR

项目中有用到SurfaceView 做视频播放,但是一直有一个问题,测试哥们今天又重新提了,貌似上任开发者并没有解决,看看吧。。。
这里写图片描述

自己试了试,确实是有这样的bug,但是是报ANR,奇了怪了,全屏放大不至于会报ANR啊,看了看代码,发现全屏播放实际是创建了一个包含surfaceView新的activity,也就是又创建一个SurfaceView,搞不懂为什么这么设计,orz….

首先,看一下日志 找半天才找到

Surface has already been released.

接着 抓一下traces日志看看(adb pull data/anr/traces.txt)

"main" prio=5 tid=1 Waiting
| 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)

直接粗暴看代码,先看SurfaceView.updateWindow源码部分:
最关键的一部分,mSurfaceLock获取lock对象

mSurfaceLock.lock();

相应的,有获取就会有释放lock,绘制视频时会有释放lock的操作(m_surfaceHolder.unlockCanvasAndPost(m_canvas);)

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);
      }
  }

你可能感兴趣的:(解疑答惑,SurfaceView,SurfaceView视频播放,ANR,Surface,has,already,been,relea)