You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将Java中TextureView+MediaPlayer代码转为Jetpack Compose实现

将Java中TextureView+MediaPlayer的视频逻辑迁移到Jetpack Compose

问题描述

我原本在Java的VideoCropActivity中通过实现TextureView.SurfaceTextureListener接口,结合TextureView和MediaPlayer处理视频相关操作。现在想把这段逻辑迁移到Jetpack Compose中,尝试用AndroidView加载布局的方式但没能正常运行,求正确的转换方案。

原Java代码

class VideoCropActivity extends Activity implements TextureView.SurfaceTextureListener {

    private MediaPlayer mPlayer;
    private Surface mSurface;
    private TextureView textureview;
    
    public void initSurface() {
        textureview = (TextureView) findViewById(R.id.aliyun_video_textureview);
        textureview.setSurfaceTextureListener(this);
    }

    @Override
    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
        if (mPlayer == null) {
            try {
                mSurface = new Surface(surface);
                mPlayer = new MediaPlayer();
                mPlayer.setSurface(mSurface);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {}

    @Override
    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
        return false;
    }

    @Override
    public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {}
}

当前尝试的Compose代码(无法正常工作)

AndroidView(factory = {
        val view = LayoutInflater.from(it).inflate(R.layout.aliyun_video_player, null)
        val textureView = view.findViewById<TextureView>(R.id.videoTextureView)
        textureView.apply {
            // 仅创建了监听器对象,但未设置给TextureView
            object : TextureView.SurfaceTextureListener {
                override fun onSurfaceTextureAvailable(p0: SurfaceTexture, p1: Int, p2: Int) {
                    println("=================")
                }

                override fun onSurfaceTextureSizeChanged(p0: SurfaceTexture, p1: Int, p2: Int) {
                    TODO("Not yet implemented")
                }

                override fun onSurfaceTextureDestroyed(p0: SurfaceTexture): Boolean {
                    TODO("Not yet implemented")
                }

                override fun onSurfaceTextureUpdated(p0: SurfaceTexture) {
                    TODO("Not yet implemented")
                }
            }
        }
    }, update = {
        // 每次重组都会创建新的MediaPlayer,逻辑混乱
        val s = it.surfaceTexture
        if (s != null) {
            val surface = android.view.Surface(it.surfaceTexture)
            val mediaPlayer = MediaPlayer()
            mediaPlayer.setSurface(surface)
            println(it)
        }
    })

正确实现方案

你的代码核心问题是未给TextureView绑定SurfaceTextureListener,且update块中重复创建MediaPlayer导致生命周期混乱。以下是修复后的完整实现:

方案1:直接创建TextureView(无需布局文件)

@Composable
fun VideoPlayer(videoUri: Uri) {
    // 用remember持有对象,避免Compose重组时重复创建
    val mediaPlayer = remember { MediaPlayer() }
    var playerSurface by remember { mutableStateOf<Surface?>(null) }

    AndroidView(
        factory = { context ->
            TextureView(context).apply {
                // 关键:给TextureView设置SurfaceTextureListener
                surfaceTextureListener = object : TextureView.SurfaceTextureListener {
                    override fun onSurfaceTextureAvailable(
                        surfaceTexture: SurfaceTexture,
                        width: Int,
                        height: Int
                    ) {
                        val newSurface = Surface(surfaceTexture)
                        playerSurface = newSurface
                        // 初始化MediaPlayer并绑定Surface
                        try {
                            mediaPlayer.reset()
                            mediaPlayer.setDataSource(context, videoUri)
                            mediaPlayer.setSurface(newSurface)
                            mediaPlayer.prepareAsync()
                            mediaPlayer.setOnPreparedListener { it.start() }
                        } catch (e: IOException) {
                            e.printStackTrace()
                        }
                    }

                    override fun onSurfaceTextureSizeChanged(
                        surfaceTexture: SurfaceTexture,
                        width: Int,
                        height: Int
                    ) {
                        // 可选:处理视频尺寸变化逻辑
                    }

                    override fun onSurfaceTextureDestroyed(surfaceTexture: SurfaceTexture): Boolean {
                        // 释放资源
                        mediaPlayer.stop()
                        mediaPlayer.release()
                        playerSurface?.release()
                        return true
                    }

                    override fun onSurfaceTextureUpdated(surfaceTexture: SurfaceTexture) {
                        // 可选:每一帧更新时的操作
                    }
                }
            }
        },
        update = { textureView ->
            // 可选:当videoUri变化时,重新加载视频
            if (mediaPlayer.dataSource != videoUri.toString()) {
                playerSurface?.let { surface ->
                    try {
                        mediaPlayer.reset()
                        mediaPlayer.setDataSource(textureView.context, videoUri)
                        mediaPlayer.setSurface(surface)
                        mediaPlayer.prepareAsync()
                        mediaPlayer.setOnPreparedListener { it.start() }
                    } catch (e: IOException) {
                        e.printStackTrace()
                    }
                }
            }
        }
    )

    // 确保Compose组件销毁时释放资源
    DisposableEffect(Unit) {
        onDispose {
            mediaPlayer.stop()
            mediaPlayer.release()
            playerSurface?.release()
        }
    }
}

方案2:加载现有布局文件

如果你想继续使用R.layout.aliyun_video_player,只需修改factory块的逻辑:

factory = { context ->
    val view = LayoutInflater.from(context).inflate(R.layout.aliyun_video_player, null)
    val textureView = view.findViewById<TextureView>(R.id.videoTextureView)
    // 给TextureView设置监听器(核心修复点)
    textureView.surfaceTextureListener = object : TextureView.SurfaceTextureListener {
        // 同上的监听器实现逻辑
    }
    view
}

关键修复点

  1. 绑定SurfaceTextureListener:必须调用textureView.setSurfaceTextureListener(),否则不会触发回调
  2. 生命周期管理:用remember保存MediaPlayer和Surface,避免重组时重复创建;用DisposableEffect确保组件销毁时释放资源
  3. 避免重复初始化:不在update块中创建新的MediaPlayer,而是复用已有的对象

内容的提问来源于stack exchange,提问作者gaohomway

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 19:33:26