But there are several problems with this project:

  1. After swiping up and down the list quickly, the video can no longer be played, and sometimes it will crash directly
  2. Asynchronous caching of web video is not supported

Therefore, it is optimized on the basis of this project, and supports asynchronous caching of network video

Asynchronous caching of web video

Video caching is actually roughly the same as image caching. There are many image caching frameworks now, but the basic principle is composed of three blocks: network download + memory cache + local cache. The video cache only needs to skip the memory cache. When the video file is not downloaded, it will be downloaded and cached locally. Next time, the video file information will be directly read from the local cache, so it is not difficult to realize the video based on the image cache framework. File caching feature.

Here I use Glide to implement video caching. Glide supports not only image caching but also normal file caching, so using Glide can easily implement video file caching

Video playback control based on TextureView

Android natively provides a video playback control -  VideoView , but VideoView is implemented based on SurfaceView , SurfaceView will use a separate window for drawing, it is not in the View hierachy, and the display is not controlled by the properties of the View, and cannot be transformed such as panning, zooming, etc. , it is also difficult to put it in ListView or ScrollView, and some features in View cannot be used.

为了弥补SurfaceView的不足,Android在4.0中加入了TextureView,它并没有创建一个单独的窗口用来绘制,这使得它可以像一般的View一样执行一些变换操作,设置透明度等,也很方便的放在其它ViewGroup中

所以要在ListView或者RecyclerView中播放视频,我们就需要实现基于TextureView的VideoView,实现代码参考ViewVideo就可以了

视频在滑动列表中的自动播放和停止

要实现视频的自动播放和停止,我们需要计算每个item中列表中的可见比。比如当某item可见比大于70%时,则该item视为可见的,激活视频播放。反之视为不可见,停止视频播放

这里简单说下实现原理,主要分为下面三步

  1. 在列表滑动时,判断滑动方向

  2. 根据滑动方向判断相邻的item是否视为可见,比如在下滑列表时,当前可见item的可见比在逐渐减小,而下一项的可见比在逐渐加大,当前item可见比低于70%时停止播放,下一项可见比大于70%时就开始播放

  3. 在快速滑动列表时,不检测item的变化(避免卡顿);在滑动停止时,查找当前可见item中可见比最大的item,如果该item和之前可见的item不一样时,则激活该item

列表中视频播放的性能问题

视频的播放主要使用了MediaPlayer,MediaPlayer的状态图如下所示:

state diagramstate diagram

从图中可以看出,视频在开始播放前需要首先通过setDataSource()进行初始化,然后通过prepare()或者prepareAsync()进行播放前准备工作,最后准备完成后通过start()操作才开始播放视频

其中prepare()操作是相当耗时的,这一步操作绝不应该在UI线程中调用,而prepareAsync()则是使用异步的方式调用,所以在list列表中播放视频应该使用prepareAsync()来准备视频

光靠prepareAsync()这一步,可不足以保证list滑动时每帧耗时不超过16ms,像setDataSource(),reset(),release()这些操作都是比较耗时的,虽然达不到引起ANR的程度,但是对于list滑动的流畅性却影响很大

解决方案

这里我采用了将MediaPlayer的全部操作都放在一个单独的线程中去处理,事件回调则通过ui Handler post回ui线程,这样就可以保证list滑动的流畅性

效果预览

代码具体的使用和详细实现方法都已放到Github上

项目地址:VideoListPlayer