compose UI(七)引入视频播放器ExoPlayer
本文示例代码API基于compose UI 1.0.0 ExoPlayer2.14.2ExoPlayer简介ExoPlayer-githubExoPlayer官网-开发者文档ExoPlayer代码实验室androidView接入关于怎么接入androidView,可以参考前面的文章。@Composablefun VideScreen (){val context = LocalContext.cu
本文示例代码API基于compose UI 1.0.0 ExoPlayer 2.14.2
ExoPlayer简介
ExoPlayer-github
ExoPlayer官网-开发者文档
ExoPlayer代码实验室
androidView接入
关于怎么接入androidView,可以参考前面的文章。
@Composable
fun VideScreen (){
val context = LocalContext.current
val exoPlayer = SimpleExoPlayer.Builder(context)
.build()
.apply {
playWhenReady = false
}
//uri可以时网络url资源,这里我adb push了一个视频到使用sd卡根目录
val mediaItem = MediaItem.fromUri(Uri.fromFile(File(SdcardUtils.rootPath(),"EXPLAIN.MP4")))
exoPlayer.setMediaItem(mediaItem)
exoPlayer.prepare()
exoPlayer.play()
PlayerSurface(modifier = Modifier.width(400.dp).height(400.dp)){
it.player = exoPlayer
}
}
@Composable
fun PlayerSurface(
modifier: Modifier,
onPlayerViewAvailable: (PlayerView) -> Unit = {}
) {
AndroidView(
factory = { context ->
PlayerView(context).apply {
useController = true
onPlayerViewAvailable(this)
}
},
modifier = modifier
)
}
资源回收
视频资源比较大,资源回收时首要考虑的。在compose中,remember {} 缓存的内容当这个compose方法不再执行时就会释放(当前compose组件的生命周期)。也就是说,我在(一)里面写的viewPage,Crossfade方式进行页面切换时就会释放资源。如果做视频滑动效果,最好将资源cache在父组件层。当然也可以使用viewmodel进行缓冲。如果手动缓冲,就必须考虑资源释放的问题。
xml layout方式,通常PlayerView建在Activity,需要在生命周期中回收。回收demo:
public override fun onPause() {
super.onPause()
if (Util.SDK_INT < 24) {
releasePlayer()
}
}
public override fun onStop() {
super.onStop()
if (Util.SDK_INT >= 24) {
releasePlayer()
}
}
------------------------------------------------
private var playWhenReady = true
private var currentWindow = 0
private var playbackPosition = 0L
private fun releasePlayer() {
player?.run {
playbackPosition = this.currentPosition
currentWindow = this.currentWindowIndex
playWhenReady = this.playWhenReady
//如果注册了监听,等等
removeListener(playbackStateListener)
...
release()
}
//避免java野指针
player = null
}
具体参考官方说明,在compose中compose函数中我们可以选择 remember,非函数可以选择MVVM。当然我们也可以获取到当前screen的生命周期,做一些事情,比如onPause时暂停。
/**
* 当前Screen的生命周期
*/
val lifecycleOwner = LocalLifecycleOwner.current
//默认在每次compose oncommit生命周期时都会执行,我们带了观察lifecycleOwner
DisposableEffect( lifecycleOwner) {
val observer = object : DefaultLifecycleObserver {
override fun onPause(owner: LifecycleOwner) {
//todo 暂停视频
}
override fun onDestroy(owner: LifecycleOwner) {
//todo 也可以主动在这里主动回收viewmodel的资源
}
}
lifecycleOwner.lifecycle.addObserver(observer)
//DisposableEffect需要一个onDispose的block作为结尾
//可以简单理解为DisposableEffect必带一个try{}的finally
onDispose {
lifecycleOwner.lifecycle.removeObserver(observer)
}
}
DefaultLifecycleObserver 接口需要引入“androidx.lifecycle:lifecycle-common-java8”
这里的Screen指的我前面(一)文章中以“androidx.navigation:navigation-compose”导航库方式导航到的Screen。Crossfade方式进行页面切换时LocalLifecycleOwner.current的值并不会发生改变,是同一个生命周期下。这里用Screen不用Activity,是因为compose并没有创建新的Activity,不然Activity需要在Manifest注册。
上面的生命周期观察方法可以用来单Avitity应用时,对全局Viewmodel内存进行管理。
compose扩展
这种方式的ExoPlayer跟androidView方式引入原生VideoView没什么区别。甚至还不如直接使用原生播放器来的方便。当然在网络播放上ExoPlayer可以引入自适应轨道选择和自适应流媒体( DASH ),这些比起原生VideoView强大很多。我们讲ui,当然需要提升一下颜值,自带的控制器太丑了。
PlayerSurface关闭控制器
useController = false
没有控制器后,我们就需要手动去调用exoPlayer的方法去控制视频。
xml layout方式下你可以这样(一个自定义custom_player_control_view.xml):
<com.google.android.exoplayer2.ui.PlayerView
android:id="@+id/video_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:controller_layout_id="@layout/custom_player_control_view"
/>
custom_player_control_view.xml
<ImageButton android:id="@id/exo_rew"
android:tint="#FF00A6FF"
style="@style/ExoMediaButton.Rewind"/>
<TextView android:id="@id/exo_position"
android:textColor="#FF00A6FF"/>
<TextView android:id="@id/exo_duration"
android:textColor="#FF00A6FF"/>
...等等...
或者查询开发文档采用覆盖style的方式。
compose 方式:
可以阅读ComposeVideoPlayer
代码内需要修改一些弃用的方法和升级依赖。我阅读后修改了一下封装成一个模块,当做sdk用。我画的UML:
大致compose ui 调用流程,时序图时候类间调用,compose画时序图有些别扭。
代码内涉及的一些compose api:
- rememberSaveable:获得一个可恢复(大部分是旋转屏幕)的remember,saver定义备份内容,init初始化方式。
- SideEffect,类似React的useState,副作用处理 每一次的compose commit生命周期都会执行
- BoxWithConstraints 官方说明 一个带LayoutDirection的Box,可以在里面拿到Layout的一些属性。
- PointerInputScope.detectHorizontalDragGestures,水平拖动手势检测,有4个方法。
- PointerInputScope.detectTapGestures,点击手势检测,可以拿到双击,长按等回调。
啥时候有空用spring boot写个视频服务端!!!
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)