一、几个问题
1、如何自定义一个视频播放器?
Android提供了很多方式,有videoview,有mediaplayer,有surfaceview等等,或者可以使用开源的等等。
2、什么是sdk,为什么要将其封装为一个sdk?
直接实现一个播放器不就可以了吗?
sdk全称是software development kit,也就是软件开发包,常见的就是Android sdk,如果没有Android sdk的话就没有Android api,也就无法开发我们的应用、游戏。还有一些常见的sdk,比如友盟sdk、sharesdk、讯飞语音sdk、高德地图sdk等等。这些sdk都有一个共同的特点,一是它们高度封装,对使用者来说,我们并不关心它内部的实现逻辑,只需要调用它们几个比较简单的接口或者方法就可以实现功能。所以sdk对开发者来说是一个比较常见的名词,但是如果没有做过sdk的话,对我们来说又是一个比较陌生的名词。为什么要封装一个sdk呢?将我们的程序代码高度地封装以后呢,就可以拿来去给别人使用。
3、如何统计我们视频播放次数?
视频播放过程中会发各种各样的监测,这种监测就是监测视频播放的状态,是播放中还是播放完成了,还是播放到第几秒了。
4、思维导图
二、播放器封装讲解
1、几个问题
①如何定义一个视频播放器?
②了解视频播放器生命周期吗?
首先,播放器处于iddle状态,也就是空闲状态。此时调用setDataSource()方法,如果成功的话会到Intialized状态,如果失败的话就会进入的Error状态。在进入到初始化状态Intialized时,如果调用prepareAsync()方法,则进入到准备中Preparing状态,当然也可以通过调用同步方法prepare()同步等待进入Prepared状态。但是,同步的方法prepare()一般情况下是不会用到的,因为Android一般是不允许主线程中有任何耗时操作的。在Prepared状态时可以通过调用start()进入Started状态,在Started状态视频播放就正常播放起来了。在started状态下调用stop()方法,就会进入到Stopped状态,或者调用pause()方法进入到Paused状态,或者等到播放完成进入到completed状态。这三种状态是相互可逆的。也就是completed、Stopped、Paused都可以进入到started状态。
③自定义视频播放器有哪些坑
2、核心知识串讲
①MediaPlayer视频播放核心类
控制视频播放器是否播放成功,是否准备成功,视频播放、暂停、停止、完成的一个流程控制
②TexttureView显示帧数据核心类
与Surfaceview的原理是一样的但是比SurfaceView多了些特性。SurfaceView有三个状态的监听,一个是create()、一个是change()、一个是destroy(),而TextTureView也一样,分别有avalible()、change()、destroy()三个接口的监听,来通知开发者当前texttureview当前处于什么状态,如果是avalible()就可以去加载视频,加载完以后就可以让texttureview去显示
③众多事件接口概述
主要用于监听MediaPlayer当前处于什么状态
常见的几个接口:
OnPreparedListener:异步地告诉程序员mediaplayer处于prepare状态,这时可以调用start()去播放视频了
onErrorListener:不仅要处理正常的逻辑,也要在出现异常的时候保证程序能够正常地运行
onCompletionListener:在视频播放完成后发一个消息通知程序员视频播放完成了,在这里做一些处理。比如当视频播放完成时发送一个监测,就是在这个方法中发送的
onBufferingUpdateListener:在视频不断地缓冲过程中调用,如果视频非常大的话会分段去缓存的。每缓存一段儿,会调用OnBufferingUpdateListener告诉我们现在缓存的状态
3、开发步骤
三、创建视频播放器核心类CustomVideoView
1、CustomViewdeoView,继承自RelativeLayout并且实现了MediaPlayer中的一些监听接口。主要作用是负责视频播放、暂停以及各类事件的触发等等。同时也实现了TextureView.SurfaceTextureListener,这个接口是textureview的回调,会告诉我们现在的textureview是否准备好了,是否改变以及是否有效果。一般情况下实现这些接口,足够做一个比较完善的播放器了
2、定义了一些常量
TIME_MSG:事件类型,后面会在handler中使用
TIME_INVAL:时间间隔,每隔1000毫秒会发一个TIME_MSG出来
STATE_ERROR、STATE_IDLE、STATE_PLAYING、STATE_PAUSING是当前状态的标志,mediaplayer是有生命周期状态的,通过这些状态声明它当前处于什么状态
LOAD_TOTAL_COUNT:视频可能会加载失败,这里定义了一个重试的机制
3、定义了一些UI
ViewGroup mParentContaine : VideoView所要添加到的父容器中
RelativeLayout mPlayerView : 当前父容器的RelativeLayout
TextureView mVideoView: 要显示帧数据上去的那个view
AudioManager audioManager:音频播放器,用来控制音量是否静音
Surface videoSurface :最终真正显示帧数据的类
其余几个Button就是播放按钮
4、数据相关的
mUrl :要加载的视频地址
isMute : 是否静音
mScreenWidth:屏幕宽度,默认是屏幕的宽度
mDestationHeight:屏幕默认的高度,默认是16:9算出来的高度
5、状态标志位
mIsRealPause: 状态是不是真正进入暂停了
mIsComplete : 播放器是否播放完成了
playerState : 播放器当前处于什么状态,默认处于Idle状态
6、相关类
MediaPlayer mediaPlayer : 播放的核心类
ADVideoPlayerListener listener : 事件的监听,通知外界发生的事件,由外界实现这个监听的逻辑处理
ScreenEventReceiver mScreenReceiver : 自定义的broadcastreceiver,用来监听屏幕是否锁屏。如果屏幕锁屏后,播放器是需要暂停的,如果用户解锁后进入到应用程序,是需要恢复播放的
Handler mHandler : 作用是每隔1s发送一个TIME_MSG事件并通过调用listener的onBufferUpdate来通知当前的播放进度。timer能不用尽量不用,因为它很容易造成内存泄漏。
7、构造方法中进行初始化操作
8、主要生命周期方法
onVisibilityChanged(View changedView,int visibility) : 所有view都有的生命周期方法,当状态发生变化的时候,会去回调这个函数。如果view由不可见变为可见,此时visibility为visible;如果view由可见变为不可见,此时visibility为gone
onTouchEvent为事件触摸处理,返回true,表明只要事件传递到视频播放器中,就会把事件消耗掉,主要是为了防止与父容器产生的一些事件冲突
onClick() :主要是处理一些点击事件,比如点击播放、点击到全屏等等
onCompletion() :是前面的接口onCompleteListener当中的一个方法,当视频播放器播放完成后会回调这个方法
onError() :是onErrorListener中的一个方法,当视频播放器播放出错时会回调这个方法
onPrepared():视频播放器已经准备好了,此时可以调用这个方法,可以进行播放,可以调用start()进入播放状态
onBufferingUpdate():是缓存的一个更新
上面四个方法是textureview的状态更新方法,最重要的是onSurfaceTextureAvailable()方法因为只有处于该状态时才可以为它加载帧数据,否则的话是一片黑屏
9、功能性方法
包括加载视频url
暂停视频
恢复视频(包括从就绪状态恢复到播放状态以及从pause状态恢复到播放状态,一旦到resume()状态就说明视频处于播放中)
playBack():是播放完成后回到初始状态,这里的处理逻辑是播放完成后不把视频销毁,而是把播放流跳转到0,处于暂停状态,这样在下次播放时就不需要耗费流量去加载视频了
stop():让视频处于停止状态,停止状态和暂停状态不一样。如果处于停止状态,那么只能是去重新prepare()
destroy():就是销毁videoview,不仅会销毁mediaplayer,也会销毁事件的监听。实际上相当于activity的onDestroy()方法
seekAndResume()和seekAndPause()其实并不是定义播放器必需的方法,但是由于功能的特殊性,有一个小屏跳转到大屏的播放功能,跳转之后是需要续播的。也就是说小屏播放到第5s的话,跳转到大屏也需要从第5s开始播放,所以需要这两个方法
setListener() :设置监听,当事件发生后,会调用listener中对应的方法来通知外界,播放器中产生了哪些方法
10、辅助方法
包括注册和取消注册监听、view的一些显示
获取播放器的时长以及当前播放的时间等等
定义播放器最难的有两点,一个是播放器状态的处理,另一个是功能性方法的实现