Android视频播放方案

文章目录


前言

本文用来记录Android中进行视频播放时的多种方案,会持续进行更新,将遇到的不同方案全部记录下来。


一、使用手机自带播放器

将视频资源通过手机自带的播放器进行操作,好处是操作简单,缺点是需要确定手机有相应的播放器,无法进行播放器的定制化,常用来播放本地资源。

kotlin 复制代码
播放网络资源时:
   val uri = data.mvdata.sq.backupdownurl.firstOrNull()?.toUri()
        val intent = Intent(Intent.ACTION_VIEW).apply {
            setDataAndType(uri,"video/mp4")
        }
        startActivity(intent)
        //可显示选择播放器
       // startActivity(Intent.createChooser(intent, "选择播放器"))

播放本地资源时:
val uri = (Environment.getExternalStorageDirectory().getPath()+"video/mp4").toUri()
        val intent = Intent(Intent.ACTION_VIEW).apply {
            setDataAndType(uri,"video/mp4")
        }
        startActivity(intent)
        //可显示选择播放器
        //startActivity(Intent.createChooser(intent, "选择播放器"))

二、VideoView(需要自定义UI控制)

系统自带的控件,可以快速简单继承,缺点是不能进行透明设置与动画设置,本身是继承自SurfaceView,内部包装了MediaPlayer的相关操作去实现视频的具体播放,因此不支持硬解码与部分高清格式的媒体。

kotlin 复制代码
xml页面:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ui.activity.VedioActivity">
    <VideoView
        android:id="@+id/video_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />
</LinearLayout>


使用的主页面:
1.控件实例化
 val  videoView = findViewById(R.id.video_view)
2.进行控件的操作设置
 //通过网络路径进入加载状态,异步
        videoView.setVideoPath(data.mvdata.sq.backupdownurl.firstOrNull())
        // videoView.setVideoURI() 也可以使用本地资源等
        //创建媒体控制器
        val mediaController= MediaController(this)
        mediaController.setAnchorView(videoView) //将控制条与videoView绑定
        //关联媒体控制器
        videoView.setMediaController(mediaController)
        videoView.setOnPreparedListener { mediaPlayer ->  //使用异步监听防止加载过长导致异常
           mediaPlayer .isLooping = true //设置循环播放
            mediaPlayer .setVolume(1.0f,1.0f) //可设置音量
            videoView.start()
            val duration = mediaPlayer .duration //获取视频总时长,ms
        }
3.根据当前页面的生命周期进行videoView生命周期的管理
   override fun onPause() {
        super.onPause()
        videoView.pause()
    }

    override fun onResume() {
        super.onResume()
        videoView.start()
    }

    override fun onDestroy() {
        super.onDestroy()
        videoView.stopPlayback() //停止后重新开启需要再次将资源加载进内存
    }     

三、TextureView(需要自定义UI)

API 级别 14(ICS)及以上版本引入的一个视图组件,继承自View,优点时时可以对其进行透明度与动画操作,拥有更好的UI效果与灵活性,更可以添加到列表中显示,缺点是绘制效率不如SurfaceView,且必须依赖于硬件加速,即设备必须有GPU且清单文件中设置硬件加速,使用时需注意监听的设置时机并及时销毁。

由于该演示使用了MVP架构,且播放的视频需要通过网络获取播放链接,所以只有在接口回调的success方法

里面才能获取到链接,又因为textureView在创建SurfaceTexture之后就回调视图完成的方法,因此监听的设置

需要放置早一点避免该方法已经执行完才去监听,最迟也必须在OnResume里面,之前就因此放在网络接口回调中导致textureView的回调没能被及时监听,同时必须通过url跟监听中SurfaceTexture两个实例都有值时才能时期正常播放内容。

kotlin 复制代码
设置硬件加速:(清单文件中可以放在Application或当前activity中)
  android:hardwareAccelerated="true"


activity的xml页面:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ui.activity.VedioActivity">
    <TextureView
        android:id="@+id/texture_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />
</LinearLayout>




activity页面:
class VedioActivity : BaseActivity(), BaseListViewInterface<MvPlayBeanGson>,
    TextureView.SurfaceTextureListener {
    override fun getViewId(): Int = R.layout.activity_vedio
    private lateinit var textureView: TextureView
    private var mediaPlayer: MediaPlayer? = null
    
    private var url: String? = null //保存mv的网络链接
    private var surfaceTexture: SurfaceTexture? = null //保存监听中获取的surfaceTexture对象

    //mv播放界面的P层实现对象
    private var mvPlayPresenterImpl: MvPlayPresenterImpl? = null
    override fun initView() {

        textureView = findViewById(R.id.texture_view)
    }
    override fun initData() {
        //获取传递过来的hash
        val hash = intent.getStringExtra("key").toString()
        //发起网络请求,获取当前条目的MV资源
        val mv_hash = AppConstants.URL_MV_PLAY.replace("{hash}", hash)
        //实例化P层对象
        mvPlayPresenterImpl = MvPlayPresenterImpl(mv_hash, this)
        mvPlayPresenterImpl?.loadDatas(AppConstants.TYPE_INIT_REFRESH)
        /**
         * TextureView 接口注册的时机需要早一点,因为onResume()方法执行后textureView会创建SurfaceTexture再触发回调,
         * 所以此监听如果放置在网络请求结果后,会导致监听注册太迟,SurfaceTexture已经创建完毕了
         */
        textureView.surfaceTextureListener = this //实现该接口

    }
    override fun error(msg: String?) {
        myToast("mv数据获取失败")
    }

    override fun success(type: Int, data: MvPlayBeanGson) {
        myToast("mv数据获取成功")
        url = data.mvdata.sq.backupdownurl.firstOrNull() //获取到url

        if (surfaceTexture != null && url != null) {
            initMediaPlayer(surfaceTexture!!)
        }
    }
    override fun onDestroy() {
        super.onDestroy()
        //确保页面销毁时进行资源回收
        releaseMediaPlayer()
    }
    /**
     * 视图可用时,该回调会在
     */
    override fun onSurfaceTextureAvailable(
        surface: SurfaceTexture,
        width: Int,
        height: Int
    ) {
        surfaceTexture = surface
        if (url != null) {
            initMediaPlayer(surface)
        }
    }

    /**
     * 视图销毁时
     * 返回true则表示应用已完成资源释放,允许系统自动销毁SurfaceTexture
     * 返回false则需要手动调用release()来释放资源
     */
    override fun onSurfaceTextureDestroyed(surface: SurfaceTexture): Boolean {
        Log.i("VedioActivity", "onSurfaceTextureDestroyed: ")
        releaseMediaPlayer()
        return true //常用
    }
    /**
     * 视图尺寸改变时
     */
    override fun onSurfaceTextureSizeChanged(
        surface: SurfaceTexture,
        width: Int,
        height: Int
    ) {
        Log.i("VedioActivity", "onSurfaceTextureSizeChanged: ")
    }
    /**
     * 视图更新时
     */
    override fun onSurfaceTextureUpdated(surface: SurfaceTexture) {
        Log.i("VedioActivity", "onSurfaceTextureUpdated: ")
    }
    /**
     * 自定义释放资源方法
     */
    private fun releaseMediaPlayer() {
        mediaPlayer?.let {
            if (it.isPlaying) it.stop()
            it.release() // 释放解码器、缓冲区等核心资源
            mediaPlayer = null
        }
    }
    /**这里使用了网络请求来获取当前mv资源,所以之前有个bug,将TextureView 监听设置在了网络数据返回的方法中,
     * 导致监听注册太晚,创建的SurfaceTexture早以设置完毕,所以一直没有触发onSurfaceTextureAvailable回调,
     * 之后改为下面方法才可以,同时需要注意使用完之后资源的释放
     */
    private fun initMediaPlayer(surface: SurfaceTexture) { //设置MediaPlayer的初始化加载操作
        mediaPlayer = MediaPlayer().apply {
            setDataSource(url) //设置数据源
            setSurface(Surface(surface)) //设置输出到 TextureView 的 SurfaceTexture
            prepareAsync() //进行异步加载
            start()
            setOnPreparedListener {  //监听
                it.start() //开启
               // textureView.rotation = 60f//将视图旋转
            }
            // it.isLooping = true //循环
        }
    }
}

四、ijkPlayer(需要自定义UI)

ijkPlayer作为B站开源的视频框架,适合多种播放模式,但本身已有多年不维护,且没有编译好的so库,所以使用需要自己编译对应设备的so库或者使用其他人编译好的。

这里我会放上其他编译好的库文件,但是该资源没有跟进Android15的16kb页面的格式,所以15及以上的设备会有不兼容的风险,因此,自己编译时可参考该文章Ubuntu20.04编译android平台ijkplayer源码
引入依赖步骤:

首先从 github中下载项目(https://github.com/bilibili/ijkplayer):

将ijkPlayer解压后取出里面ijkplayer-java文件作为依赖引入当前项目(如D:\download\ijkplayer-master\ijkplayer-master\android\ijkplayer\ijkplayer-java)

这里因为我已经导入过了所以爆红,正常情况下可直接点finish

然后修改模块ijkplayer-java中build.gradle中相关版本号等配置与当前项目一致,可参考如下:

kotlin 复制代码
apply plugin: 'com.android.library'

android {
    namespace 'tv.danmaku.ijk.media.player'//空间命名别忘记加上
    compileSdk 36

    defaultConfig {
        minSdk 24
        targetSdk 36
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

    }


    lintOptions {
        abortOnError false
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation  fileTree(dir: 'libs', include: ['*.jar'])
}

apply from: new File(rootProject.projectDir, "tools/gradle-on-demand.gradle");

进行同步后在主模块中添加ijkplayer-java模块为当前的依赖(软件左上角中File->Project Structure开启该界面):

也可以直接手动在主模块app的build.gradle中添加ijkplayer-java依赖:

最后需要将ijkPlayer中的tools文件复制过来,否则会提示这里找不到tools文件

kotlin 复制代码
apply from: new File(rootProject.projectDir, "tools/gradle-on-demand.gradle");

注意需要复制的是ijkPlayer项目中该路径下的tools文件(D:\download\ijkplayer-master\ijkplayer-master\android\ijkplayer\tools),存放在最外层工程目录下

最后将so文件复制到项目中,可以只针对目标机型的cpu架构,没必要全部复制进去

最后进行同步就可以使用了。

kotlin 复制代码
xml页面:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".ui.activity.VedioActivity">

    <SurfaceView
        android:id="@+id/video_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="center" />

</LinearLayout>



activity页面:(这里包含了当前项目的部分逻辑,所以可根据序号标点来捋顺重点使用逻辑)
class VedioActivity : BaseActivity(), BaseListViewInterface<MvPlayBeanGson> {
    override fun getViewId(): Int {
        return R.layout.activity_vedio
    }
    //mv播放界面的P层实现对象
    private var mvPlayPresenterImpl: MvPlayPresenterImpl? = null
    
    // 状态标记(避免空指针/重复操作)
    private var isPlayerInited = false // 播放器是否初始化完成
    private var isSurfaceReady = false // Surface是否就绪
    //播放视频的容器
    private lateinit var videoView: SurfaceView
    //ijkPlayer控制器
    private var ijkMediaPlayer: IjkMediaPlayer? = null

    override fun initView() {
        videoView = findViewById(R.id.video_view)
    }

    override fun initData() {

        //获取传递过来的hash
        val hash = intent.getStringExtra("key").toString()
        //发起网络请求,获取当前条目的MV资源
        val mv_hash = AppConstants.URL_MV_PLAY.replace("{hash}", hash)
        //实例化P层对象
        mvPlayPresenterImpl = MvPlayPresenterImpl(mv_hash, this)
        mvPlayPresenterImpl?.loadDatas(AppConstants.TYPE_INIT_REFRESH)

        //1.监听布局完绘制的完成后给控制器绑定容器
        initSurfaceView()
        //2.进行ijkPlayer初始化,异步加载运行库
        initIjkPlayer()
    }

    private fun initIjkPlayer() {
        //开启子线程进行异步加载
        Thread {
            try {
                IjkMediaPlayer.loadLibrariesOnce(null) //加载核心库
                IjkMediaPlayer.native_profileBegin("libijkplayer.so")
                IjkMediaPlayer.native_profileBegin("libijkffmpeg.so")
                IjkMediaPlayer.native_profileBegin("libijksdl.so")
                ijkMediaPlayer = IjkMediaPlayer().apply {
                    ThreadUtils.mainThread { //切换到主线程
                        setOnPreparedListener {
                            it.start() //异步监听加载数据完成后开始播放
                        }
                    }

                }
                isPlayerInited = true //标记播放器初始化完成
                tryAutoPlay(null)
            } catch (e: Exception) {
                Log.e("VedioActivity", "IjkPlayer初始化失败", e)
                finish()
            }
        }.start()

    }

    /**
     * 播放
     */
    private fun tryAutoPlay(url: String?) {
        //3.进行播放操作
        if (isPlayerInited && isSurfaceReady && url != null) {
            Thread {
                ijkMediaPlayer?.apply {
                    setDataSource(url)
                    prepareAsync() //异步加载资源
                }
            }.start()
        }
    }

    override fun error(msg: String?) {
        myToast("mv数据获取失败")
    }

    override fun success(type: Int, data: MvPlayBeanGson) {
        myToast("mv数据获取成功")
        val url = data.mvdata.sq.backupdownurl.firstOrNull() //获取到url

        url?.let {
            tryAutoPlay(url)
        }

    }

    private fun initSurfaceView() {
        videoView.holder.addCallback(object : SurfaceHolder.Callback {
            override fun surfaceChanged(
                holder: SurfaceHolder,
                format: Int,
                width: Int,
                height: Int
            ) {
            }

            override fun surfaceCreated(holder: SurfaceHolder) {
                isSurfaceReady = true //视图初始化完成
                ThreadUtils.mainThread {
                    //让ijkPlayer绑定完成创建后的容器
                    ijkMediaPlayer?.setDisplay(holder)
                }
                tryAutoPlay(null)
            }

            override fun surfaceDestroyed(holder: SurfaceHolder) {
                if (ijkMediaPlayer?.isPlaying() == true) {
                    ijkMediaPlayer?.pause()
                }
            }
        })
    }

    override fun onDestroy() {
        super.onDestroy()
        ijkMediaPlayer?.apply {
            stop()
            release()
        }
        ijkMediaPlayer = null
    }
}

五、jiaoziVideoPlayer(有完善UI控制)

jiaoziVideoPlayer是一个支持高度定制的框架,里面有各种现成的ui操作,可以用最简单的方式实现视频播放。

官方的地址:https://github.com/Jzvd/JZVideo

kotlin 复制代码
依赖:
implementation 'cn.jzvd:jiaozivideoplayer:7.7.0'


xml页面:
<cn.jzvd.JzvdStd
       android:id="@+id/video_view"
       android:layout_width="match_parent"
       android:layout_height="200dp"
       />



activity页面:(可选择设置清单中横竖屏切换保持生命周期)
class VedioActivity : BaseActivity(), BaseListViewInterface<MvPlayBeanGson> {
    override fun getViewId(): Int {
        return R.layout.activity_vedio
    }

    //mv播放界面的P层实现对象
    private var mvPlayPresenterImpl: MvPlayPresenterImpl? = null

    private lateinit var videoView: JzvdStd
    override fun initView() {
        //1.实例化,并设置返回键逻辑
        videoView = findViewById(R.id.video_view)

        onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
            override fun handleOnBackPressed() {
            // 处理返回手势的逻辑
                if (Jzvd.backPress()) return
                finish()
            }
        })
    }

    override fun initData() {
        //获取传递过来的hash
        val hash = intent.getStringExtra("key").toString()
        //拼接当前条目的MV资源
        val mv_hash = AppConstants.URL_MV_PLAY.replace("{hash}", hash)
        //实例化P层对象后发起网络请求
        mvPlayPresenterImpl = MvPlayPresenterImpl(mv_hash, this@VedioActivity)
        mvPlayPresenterImpl?.loadDatas(AppConstants.TYPE_INIT_REFRESH)

    }

    override fun error(msg: String?) {
        myToast("mv数据获取失败")
    }

    override fun success(type: Int, data: MvPlayBeanGson) {
        myToast("mv数据获取成功")
        val url = data.mvdata.sq.backupdownurl.firstOrNull() //获取到url
        val title = data.songname //获取到标题
        if (url == null) return
        title?.let {
            //2.设置数据源与标题进行播放
            videoView.setUp(url,it)
        }

    }

    override fun onPause() {
        super.onPause()
        //3.设置暂停与销毁的生命周期管理
        Jzvd.releaseAllVideos()
    }
    override fun onDestroy() {
        super.onDestroy()
        mvPlayPresenterImpl?.destory()
        mvPlayPresenterImpl = null
        // 释放播放器资源
        Jzvd.releaseAllVideos()
        // 清空静态缓存(关键:饺子播放器需手动清空)
        Jzvd.CURRENT_JZVD = null
        // 释放播放器 View 引用
        videoView.clearAnimation()
    }
}

六、GSYVideoPlayer(有完善UI控制)

GSYVideoPlayer 是一款目前持续更新的开源库,里面集成了ExoPlayer、IjkPlayer、MediaPalyer和AliPlayer等主流媒体框架,提供了一套完整且易用的视频播放方案,同时还具有丰富的交互功能,如音量、进度条、亮度、全屏、清晰度等设置,且官网中有着比较详细的介绍可以作为参考。

网址:https://github.com/CarGuo/GSYVideoPlayer

一些定制化设置可参考该文章:GSYVideoPlayer

全面的详细demo可参考官方文档:GSYVideoPlayer 官方文档说明

kotlin 复制代码
依赖:
implementation 'io.github.carguo:gsyvideoplayer:11.2.0'


xml页面:
 <com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer
        android:id="@+id/video_view"
        android:layout_width="match_parent"
        android:layout_height="200dp" />


activity页面:(可根据标号快速定位)
class VedioActivity : BaseActivity(), BaseListViewInterface<MvPlayBeanGson> {
    override fun getViewId(): Int {
        return R.layout.activity_vedio
    }

    //mv播放界面的P层实现对象
    private var mvPlayPresenterImpl: MvPlayPresenterImpl? = null

    private lateinit var videoView: StandardGSYVideoPlayer
    override fun initView() {
        //1.实例化,并设置返回键逻辑
        videoView = findViewById(R.id.video_view)
        onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
            override fun handleOnBackPressed() {
            // 处理返回手势的逻辑
              videoView.setVideoAllCallBack(null) //释放所有资源
                finish()
            }
        })
    }

    override fun initData() {
        //获取传递过来的hash
        val hash = intent.getStringExtra("key").toString()
        //拼接当前条目的MV资源
        val mv_hash = AppConstants.URL_MV_PLAY.replace("{hash}", hash)
        //实例化P层对象后发起网络请求
        mvPlayPresenterImpl = MvPlayPresenterImpl(mv_hash, this@VedioActivity)
        mvPlayPresenterImpl?.loadDatas(AppConstants.TYPE_INIT_REFRESH)
    }

    override fun error(msg: String?) {
        myToast("mv数据获取失败")
    }
    override fun success(type: Int, data: MvPlayBeanGson) {
        myToast("mv数据获取成功")
        val url = data.mvdata.sq.backupdownurl.firstOrNull() //获取到url
        val title = data.songname //获取到标题
        if (url == null) return
        title?.let {
            /**
             * 2.设置播放器的参数并进行播放
             */
            //路径,是否边播边缓存,标题
            videoView.setUp(url,true,title)
            videoView.setIsTouchWiget(false) //是否可以在视频任意地方滑动调整进度,默认true
           //设置全屏按键
            videoView.fullscreenButton.setOnClickListener { //上下文,是否有actionBar,是否有状态栏(此处设置为true可以让播放器自带的标题完整显示)
                videoView.startWindowFullscreen(this@VedioActivity,false,true)
            }
            //设置返回按键功能
            videoView.backButton.setOnClickListener {
                onBackPressedDispatcher.onBackPressed()
            }
            videoView.startPlayLogic() //播放
        }
    }
    //3.播放器的生命周期管理
    override fun onPause() {
        super.onPause()
        videoView.onVideoPause()
    }
    override fun onResume() {
        super.onResume()
        videoView.onVideoResume()
    }
    override fun onDestroy() {
        super.onDestroy()
        //释放P层引用并解绑
        mvPlayPresenterImpl?.destory()
        mvPlayPresenterImpl = null
        //释放播放器资源
        GSYVideoManager.releaseAllVideos()
    }
}

七、Media3(有完善UI控制)

Media3 是 Google 将 ExoPlayer、MediaSession、PlayerView、Cast 等多个媒体相关组件统一整合后的新架构,集成了ExoPlayer作为核心播放引擎。

更多的定制化可参考该文章:Android Media3 ExoPlayer 开发全攻略:从基础集成到高级功能实战

kotlin 复制代码
依赖:
//media3流媒体框架
    // 核心库
    implementation "androidx.media3:media3-exoplayer:1.7.1"
    // 用于 UI 组件和 MediaSession 集成
    implementation "androidx.media3:media3-ui:1.7.1"
    // 可选,支持DASH流媒体传输协议
    implementation "androidx.media3:media3-exoplayer-dash:1.7.1"
    // 可选,支持HLS流媒体传输协议
    implementation "androidx.media3:media3-exoplayer-hls:1.7.1"
    // 可选,支持RTSP流媒体传输协议
    implementation 'androidx.media3:media3-exoplayer-rtsp:1.7.1'
   /* // 用于平滑流媒体(如果不需要可以省略)
    implementation "androidx.media3:media3-exoplayer-smoothstreaming:1.4.1"
    // 用于使用 Transformer 进行媒体转换
    implementation "androidx.media3:media3-transformer:1.4.1"
    // 用于使用 Effect 进行音频/视频处理
    implementation "androidx.media3:media3-effect:1.4.1"
    // 用于使用 Common 和 Datasource 模块
    implementation "androidx.media3:media3-common:1.4.1"
    implementation "androidx.media3:media3-datasource:1.4.1"*/


xml页面:
<!--app:resize_mode 控制视频适配容器的方式
      zoom等比例填充 超出部分裁剪
      fit等比例显示全部画面,不足的部分黑边填充
      fill强制拉伸为容器尺寸
      none按原始视频显示,超出部分裁剪,不足部分黑边
      fixed_width 宽度填充容器,超出部分裁剪,不足部分黑边
      fixed_height 高度优先填充,超出部分裁剪,不足部分黑边-->
    <androidx.media3.ui.PlayerView
        android:id="@+id/video_view"
        android:layout_width="match_parent"
        app:resize_mode="zoom"
        android:layout_height="500dp"/>



activity页面:(实现标号内容即可)
class VedioActivity : BaseActivity(), BaseListViewInterface<MvPlayBeanGson> {
    override fun getViewId(): Int {
        return R.layout.activity_vedio
    }

    //mv播放界面的P层实现对象
    private var mvPlayPresenterImpl: MvPlayPresenterImpl? = null
    private var player: ExoPlayer? = null
    private lateinit var videoView: PlayerView
    override fun initView() {
        //1.实例化,并设置返回键逻辑
        videoView = findViewById(R.id.video_view)
    }

    override fun initData() {
        //获取传递过来的hash
        val hash = intent.getStringExtra("key").toString()
        //拼接当前条目的MV资源
        val mv_hash = AppConstants.URL_MV_PLAY.replace("{hash}", hash)
        //实例化P层对象后发起网络请求
        mvPlayPresenterImpl = MvPlayPresenterImpl(mv_hash, this@VedioActivity)
        mvPlayPresenterImpl?.loadDatas(AppConstants.TYPE_INIT_REFRESH)

    }

    override fun error(msg: String?) {
        myToast("mv数据获取失败")
    }

    @OptIn(UnstableApi::class)
    override fun success(type: Int, data: MvPlayBeanGson) {
        myToast("mv数据获取成功")
        val url = data.mvdata.sq.backupdownurl.firstOrNull() //获取到url
        val title = data.songname //获取到标题
        if (url == null) return
        title?.let {

            //2.对播放器进行初始化并进行设置
            player = ExoPlayer.Builder(this@VedioActivity).build()
            //将播放器附加到视图
            videoView.player = player
            //构建播放列表
            val mediaItem = MediaItem.fromUri(url)
            //设置播放列表准备播放
            player?.setMediaItem(mediaItem)
            player?.playWhenReady = true //当Player处于STATE_READY状态时进行播放
            player?.prepare() //异步加载准备数据
            player?.play()
        }

    }

    //3.播放器的生命周期管理
    override fun onPause() {
        super.onPause()
        player?.pause()
    }

    override fun onResume() {
        super.onResume()
        player?.play()
    }

    override fun onDestroy() {
        super.onDestroy()
        //释放P层引用并解绑
        mvPlayPresenterImpl?.destory()
        mvPlayPresenterImpl = null
        //释放播放器资源
        player?.release()
        player = null

    }
}

八、AliPlayer

阿里提供的播放器,支持全屏播放、视频快进/快退、播放/暂停,具备自适应码率技术,根据用户的网络状况自动调整视频质量,支持多种视频格式和流媒体协议,官网有比较详细的使用说明,且目前只支持真机调试,具体使用可根据官方文档。

官网开发文档:阿里官方集成文档


相关推荐
U***l8321 小时前
MySql的慢查询(慢日志)
android·mysql·adb
2501_915918411 小时前
iOS 手机抓包软件怎么选?HTTPS 调试、TCP 数据流分析与多工具组合的完整实践
android·ios·智能手机·小程序·https·uni-app·iphone
某空m1 小时前
【Android】组件化搭建
android·java·前端
小小8程序员1 小时前
Android 性能调优与故障排查:ADB 诊断命令终极指南
android·adb
游戏开发爱好者81 小时前
iOS 应用上架的工程实践复盘,从构建交付到审核通过的全流程拆解
android·ios·小程序·https·uni-app·iphone·webview
00后程序员张1 小时前
iOS App 如何上架,从准备到发布的完整流程方法论
android·macos·ios·小程序·uni-app·cocoa·iphone
i***27951 小时前
MySQL-mysql zip安装包配置教程
android·mysql·adb
AI周红伟1 小时前
开源 | InfiniteTalk:无限长虚拟人视频生成的新范式
音视频
v***16021 小时前
mysql的主从配置
android·mysql·adb