本文示例代码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:
composeExoplayeruml
大致compose ui 调用流程,时序图时候类间调用,compose画时序图有些别扭。
composeExoplayerSequence
代码内涉及的一些compose api:

  1. rememberSaveable:获得一个可恢复(大部分是旋转屏幕)的remember,saver定义备份内容,init初始化方式。
  2. SideEffect,类似React的useState,副作用处理 每一次的compose commit生命周期都会执行
  3. BoxWithConstraints 官方说明 一个带LayoutDirection的Box,可以在里面拿到Layout的一些属性。
  4. PointerInputScope.detectHorizontalDragGestures,水平拖动手势检测,有4个方法。
  5. PointerInputScope.detectTapGestures,点击手势检测,可以拿到双击,长按等回调。

啥时候有空用spring boot写个视频服务端!!!

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐