feat:完善Exoplayer
feat:新增资源下载(Glide版本)未测试验证是否有bug
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
package com.chuhai.core.download
|
||||
|
||||
import com.chuhai.core.exceptions.SuperException
|
||||
|
||||
/**
|
||||
* Created by Max on 2022/5/12 21:05
|
||||
* Desc:下载异常
|
||||
**/
|
||||
class DownloadException : SuperException {
|
||||
|
||||
constructor(message: String) : super(message)
|
||||
|
||||
constructor(throwable: Throwable) : super(
|
||||
throwable
|
||||
) {
|
||||
}
|
||||
}
|
@@ -0,0 +1,21 @@
|
||||
package com.chuhai.core.download
|
||||
|
||||
import java.io.File
|
||||
|
||||
|
||||
/**
|
||||
* Created by Max on 3/6/21 10:29 AM
|
||||
* Desc:下载监听器
|
||||
*/
|
||||
interface DownloadListener {
|
||||
|
||||
/**
|
||||
* 下载完成
|
||||
*/
|
||||
fun onDownloadCompleted(result: File) {}
|
||||
|
||||
/**
|
||||
* 下载异常
|
||||
*/
|
||||
fun onDownloadError(exception: DownloadException) {}
|
||||
}
|
@@ -13,7 +13,7 @@ interface IPlayer : ICleared {
|
||||
/**
|
||||
* 绑定页面生命周期
|
||||
*/
|
||||
fun bindingLifeCycle(lifecycleOwner: LifecycleOwner)
|
||||
fun bindLifeCycle(lifecycleOwner: LifecycleOwner)
|
||||
|
||||
/**
|
||||
* 加载视频源(加载后根据playWhenReady觉得是否播放)
|
||||
|
@@ -0,0 +1,60 @@
|
||||
package com.chuhai.core.player.download
|
||||
|
||||
import android.graphics.drawable.Drawable
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.request.target.CustomTarget
|
||||
import com.bumptech.glide.request.transition.Transition
|
||||
import com.chuhai.core.download.DownloadException
|
||||
import com.chuhai.core.download.DownloadListener
|
||||
import com.chuhai.utils.AppUtils
|
||||
import com.chuhai.utils.log.ILog
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* Created by Max on 2023/11/3 12:11
|
||||
* Desc:Glide 实现下载器功能
|
||||
* PS:没有在项目中找到下载器组件,之前的VAP视频是通过Glide下载的,这里暂且也采用这种方式,后续考虑搞个独立的下载组件!!
|
||||
* TODO ⚠️写好后还未测试验证是否有bug
|
||||
**/
|
||||
class GlideDownload(private val timeout: Int = 15000) : MediaDownloader, ILog {
|
||||
|
||||
private val targetList: ArrayList<CustomTarget<File>> by lazy {
|
||||
ArrayList()
|
||||
}
|
||||
|
||||
override fun download(url: String, listener: DownloadListener) {
|
||||
val target = object : CustomTarget<File>() {
|
||||
override fun onResourceReady(resource: File, transition: Transition<in File>?) {
|
||||
targetList.remove(this)
|
||||
listener.onDownloadCompleted(resource)
|
||||
}
|
||||
|
||||
override fun onLoadFailed(errorDrawable: Drawable?) {
|
||||
super.onLoadFailed(errorDrawable)
|
||||
targetList.remove(this)
|
||||
listener.onDownloadError(DownloadException("下载失败"))
|
||||
}
|
||||
|
||||
override fun onLoadCleared(placeholder: Drawable?) {
|
||||
}
|
||||
}
|
||||
Glide.with(AppUtils.getApp())
|
||||
.asFile()
|
||||
.dontTransform()
|
||||
.timeout(timeout)
|
||||
.load(url).into(target)
|
||||
targetList.add(target)
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
try {
|
||||
targetList.forEach {
|
||||
Glide.with(AppUtils.getApp()).clear(it)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
targetList.clear()
|
||||
}
|
||||
}
|
@@ -0,0 +1,23 @@
|
||||
package com.chuhai.core.player.download
|
||||
|
||||
import com.chuhai.core.download.DownloadListener
|
||||
import com.chuhai.utils.ICleared
|
||||
|
||||
/**
|
||||
* Created by Max on 2023/11/3 12:45
|
||||
* Desc:媒体资源下载器
|
||||
*/
|
||||
interface MediaDownloader : ICleared {
|
||||
|
||||
/**
|
||||
* 下载
|
||||
*/
|
||||
fun download(url: String, listener: DownloadListener)
|
||||
|
||||
/**
|
||||
* 释放
|
||||
*/
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
}
|
||||
}
|
@@ -3,12 +3,15 @@ package com.chuhai.core.player.exo
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleEventObserver
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import com.chuhai.core.player.IPlayer
|
||||
import com.chuhai.core.player.PlayerListener
|
||||
import com.chuhai.core.player.PlayerMediaItem
|
||||
import com.chuhai.core.player.PlaybackState
|
||||
import com.chuhai.core.player.PlayerException
|
||||
import com.chuhai.core.player.PlayerListener
|
||||
import com.chuhai.core.player.PlayerMediaItem
|
||||
import com.google.android.exoplayer2.DefaultLoadControl
|
||||
import com.google.android.exoplayer2.MediaItem
|
||||
import com.google.android.exoplayer2.PlaybackException
|
||||
import com.google.android.exoplayer2.Player
|
||||
@@ -20,7 +23,7 @@ import com.google.android.exoplayer2.ExoPlayer as ExoPlayerImpl
|
||||
* Created by Max on 2023/11/2 14:50
|
||||
* Desc:基于Exo
|
||||
**/
|
||||
class ExoPlayer : StyledPlayerView, IPlayer {
|
||||
class ExoPlayer : StyledPlayerView, IPlayer, LifecycleEventObserver {
|
||||
|
||||
private var currentItem: ExoMediaItem? = null
|
||||
|
||||
@@ -30,6 +33,9 @@ class ExoPlayer : StyledPlayerView, IPlayer {
|
||||
|
||||
private var listenerAdapter: Player.Listener? = null
|
||||
|
||||
// 操作的记录(用于感知生命周期恢复播放或其他场景的判断)
|
||||
private var playWhenReadyBackup = getPlayWhenReady()
|
||||
|
||||
constructor(context: Context) : this(context, null)
|
||||
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
|
||||
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
|
||||
@@ -37,11 +43,23 @@ class ExoPlayer : StyledPlayerView, IPlayer {
|
||||
attrs,
|
||||
defStyleAttr
|
||||
) {
|
||||
player = ExoPlayerImpl.Builder(context).build()
|
||||
/**
|
||||
* 缓冲策略:目前先用默认的,后续结合需求场景再调整
|
||||
*/
|
||||
val loadControl = DefaultLoadControl.Builder()
|
||||
.setBufferDurationsMs(
|
||||
DefaultLoadControl.DEFAULT_MIN_BUFFER_MS,
|
||||
DefaultLoadControl.DEFAULT_MAX_BUFFER_MS,
|
||||
DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_MS,
|
||||
DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS
|
||||
).build()
|
||||
player = ExoPlayerImpl.Builder(context).setLoadControl(loadControl).build()
|
||||
useController = false
|
||||
setPlayer(player)
|
||||
}
|
||||
|
||||
override fun bindingLifeCycle(lifecycleOwner: LifecycleOwner) {
|
||||
override fun bindLifeCycle(lifecycleOwner: LifecycleOwner) {
|
||||
lifecycleOwner.lifecycle.addObserver(this)
|
||||
}
|
||||
|
||||
override fun prepare(item: PlayerMediaItem) {
|
||||
@@ -60,6 +78,7 @@ class ExoPlayer : StyledPlayerView, IPlayer {
|
||||
|
||||
override fun setPlayWhenReady(playWhenReady: Boolean) {
|
||||
this.player.playWhenReady = playWhenReady
|
||||
this.playWhenReadyBackup = playWhenReady
|
||||
}
|
||||
|
||||
override fun getPlayWhenReady(): Boolean {
|
||||
@@ -133,4 +152,21 @@ class ExoPlayer : StyledPlayerView, IPlayer {
|
||||
this.currentItem?.onCleared()
|
||||
this.currentItem = null
|
||||
}
|
||||
|
||||
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
|
||||
// TODO 这个方法的代码 还未进行测试,目前需求貌似用不上,后续需要再验证!
|
||||
when (event) {
|
||||
Lifecycle.Event.ON_RESUME -> {
|
||||
setPlayWhenReady(playWhenReadyBackup)
|
||||
}
|
||||
|
||||
Lifecycle.Event.ON_PAUSE -> {
|
||||
player.playWhenReady = false
|
||||
}
|
||||
|
||||
Lifecycle.Event.ON_DESTROY -> {
|
||||
onCleared()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user