安卓给app提供了:
MediaPlayer:播放视频或音频功能,详见谷歌MediaPlayer文档
借助MediaPlayer,我们可以轻松的实现一个简单的播放器app。一般来说app显示内容放在Activity中。但是试想一般的播放器要求app进入后台后可以继续播放声音,app回到前台后可以继续播放视频。因此其实MediaPlayer更适合放在Service中来播放视频,而我们的Activity仅显示视频即可。接下来我们依次实现MediaService和MediaActivity。
首先实现我们的核心类MediaService:
class MediaService : Service() {
val mMediaPlayer = MediaPlayer() // 播放媒体文件的对象
val mMediaBinder = MediaBinder() // 传给Activity,使得Activity可以与我们这个Service通信
var mIsForeground = false // 为true时变成前台服务,为false时停止前台服务成为一般的后台服务
var mMediaListener: MediaListener? = null // mMediaPlayer的相关回调
var mTimer: Timer? = null // 用来定时调用mMediaListener.onProgress回调方法的定时器
var mTimerTask = object : TimerTask() {
// 定时调用mMediaListener.onProgress回调方法的任务
override fun run() {
mMediaListener?.onProgress(mMediaPlayer.currentPosition, mMediaPlayer.duration)
}
}
// 此类的方法都是从Activity调用的
inner class MediaBinder : Binder() {
// 设置显示视频的surfaceHolder
fun setDisplay(surfaceHolder: SurfaceHolder?) {
mMediaPlayer.setDisplay(surfaceHolder)
}
// 设置一个额外的媒体播放完毕时回调
fun setMediaListener(mediaListener: MediaListener?) {
mMediaListener = mediaListener
mMediaPlayer.setOnVideoSizeChangedListener(mMediaListener)
if (mMediaListener == null) {
mTimer?.cancel()
mTimer = null
} else if (mTimer == null) {
mTimer = Timer().apply {
schedule(mTimerTask, 0, 100) }
}
}
// 打开媒体文件
fun open(uri: Uri) {
mMediaPlayer.reset()
mMediaPlayer.setDataSource(this@MediaService, uri)
mMediaPlayer.prepareAsync()
}
// 继续或暂停播放媒体,返回true代表继续播放了,返回false代表暂停播放了
fun playOrPause(): Boolean {
if (mMediaPlayer.isPlaying) mMediaPlayer.pause()
else mMediaPlayer.start()
updateServiceState()
return mMediaPlayer.isPlaying
}
// 停止播放媒体
fun stop() {
mMediaPlayer.stop()
updateServiceState()
}
// 调整播放进度
fun seekTo(millisecond: Int) {
mMediaPlayer.seekTo(millisecond)
}
}
override fun onBind(intent: Intent): IBinder {
return mMediaBinder
}
override fun onCreate() {
super.onCreate()
mMediaPlayer.setOnPreparedListener {
mMediaPlayer.start()
updateServiceState()
}
mMediaPlayer.setOnCompletionListener {
mMediaListener?.onCompletion(it)
updateServiceState()
}
mMediaPlayer.setOnErrorListener {
mp, what, extra ->
Log.e(TAG, "MediaPlayer error! mediaPlayer=$mp, what=$what, extra=$extra")
updateServiceState()
true
}
}
// 如果正在播放媒体文件,则变成前台服务,否则变成一般的后台服务
private fun updateServiceState() {
if (mMediaPlayer.isPlaying != mIsForeground) {
mIsForeground = !mIsForeground