1、SurfaceView/VideoView
我们经常用SurfaceView和VideoView来播放视频,但是这两个东东,经常都会出问题。
surfaceView的问题
①SurfaceView不会添加到View树上,并且显示在所有View之上
②在按Home键的时候,会让Surface销毁,并且在重新进入APP的时候,让Surface重建,在Surface重建的时候,SurfaceView那一块是透明的,显示的会是Activity的背景
③如果是列表中的子view中播放视频,在上下滑动的时候,会导致Surface绘制不及时,会有残留
④多个VideoView同时播放的时候,在SurfaceFlinger支持不好的手机上,会出现下一个SurfaceView的某一帧会显示在上一个SurfaceView上
videoView的问题
①VideoView是直接继承SurfaceView
②VideoView中的openVideo可能会ANR
③VideoView中的release,stopPlayBack都会导致ANR,因为这些方法都是同步执行的,并且通过IPC服务交给MediaServer去释放资源
解决方法:
使用TextureView替换SurfaceView实现VideoView,因为TextureView是直接继承View的,并且在ListView中滑动的时候,也不会在滑动的时候,有帧残留
ANR问题:
当我们有一个列表,每个子项就是一个视频,并且自动播放。surfaceView使用的就是Android自带的状态机来控制播放,所以就会一段一段的将视频先读到缓冲区,再播放。由于MediaPlayer中的release,reset,stopPlayBack都是同步的。而且当视频卡片在滑出屏幕之后,需要把视频暂停,在不可见的时候不进行播放。节省系统资源,并且节省用户流量。而如果同时出现多个视频的时候,会频繁调用到上述生命周期方法,导致很容易出现ANR。
解决方法:
问题1.在视频划出ListView的时候,停止播放视频。
解决方案:在ListView中调用setRecycleListener,设置View回收的监听,因为ListView的重用性,会在View回收到scrap区的时候,通过这个Listener进行一些处理,所以在这里根据View.getTag,找到视频View的引用,调用stopPlayBack停止
问题2.频繁调用release等方法导致ANR
解决方案:在视频调用的时候,建立一个释放视频资源的守护线程。在Android中,直接可以用HandlerThread,因为这样可以尽可能的让资源的消耗达到最少,HandlerThread在没有新事件到来的时候,都是处于wait状态,直到有新事件的到来,才会被notify,处理新事件。它里面也是通过一个Thread,在这个Thread中新建一个Looper,在Looper中没有事件的话,则wait,一旦通过Handler发送新事件的话,则会被notify。所以会在子线程中加入一个队列,当需要release的MediaPlayer,直接丢到子线程去进行资源释放。
但是这样会导致一个问题,就是Android维护的MediaPlayer的状态机中的状态可能会乱,这时候就会抛出IllegalStateException,目前对于这种异常,我们选择了捕获它。
另外,由于MediaPlayer.setSurface需要传递一个Surface,然后再在这个Surface上进行绘制,如果频繁new Surface传入的话,就会导致GrafficBuffer分配Surface失败,从而MediaPlayer会回调onError中,显示视频不能播放。所以尽可能一个视频View用一个Surface。