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

Android Kotlin(Jetpack Compose)中如何日志输出PreviewView.bitmap?

解决PreviewView获取Bitmap返回Null的问题

要解决previewView.bitmap返回null的问题,核心是监听PreviewView的流状态,等待其进入STREAMING状态后再获取Bitmap。以下是针对你的Compose代码的修改方案:

实现步骤

  • 给PreviewView添加流状态变化监听器,监听StreamState的切换
  • 当状态变为PreviewView.StreamState.STREAMING时,安全获取Bitmap
  • 若需要实时获取Bitmap,可配合协程定时执行获取逻辑

修改后的完整代码

package com.example.livestreamdemo.screens

import android.Manifest
import android.content.pm.PackageManager
import android.util.Log
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.camera.core.CameraSelector
import androidx.camera.core.Preview
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.camera.view.PreviewView
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.content.ContextCompat
import com.example.livestreamdemo.util.Common.REQUIRED_PERMISSIONS
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch

@Composable
fun CameraPreviewScreen() {
    val lifecycleOwner = LocalLifecycleOwner.current
    val context = LocalContext.current
    val cameraProviderNext = remember { ProcessCameraProvider.getInstance(context) }
    val coroutineScope = rememberCoroutineScope()

    var hasCamPermission by remember {
        mutableStateOf(
            REQUIRED_PERMISSIONS.all {
                ContextCompat.checkSelfPermission(context, it) ==
                        PackageManager.PERMISSION_GRANTED
            })
    }

    val launcher = rememberLauncherForActivityResult(
        contract = ActivityResultContracts.RequestMultiplePermissions(),
        onResult = { granted ->
            hasCamPermission = granted.values.all { it }
        }
    )

    LaunchedEffect(key1 = true) {
        launcher.launch(
            arrayOf(
                Manifest.permission.CAMERA,
                Manifest.permission.WRITE_EXTERNAL_STORAGE
            )
        )
    }

    if(hasCamPermission) {
        AndroidView(
            factory = { ctx ->
                val previewView = PreviewView(ctx)
                val exec = ContextCompat.getMainExecutor(ctx)
                cameraProviderNext.addListener({
                    val cameraProvider = cameraProviderNext.get()
                    val preview = Preview.Builder().build()
                        .also {
                            it.setSurfaceProvider(previewView.surfaceProvider)
                        }
                    val cameraSelector = CameraSelector.Builder()
                        .requireLensFacing(CameraSelector.LENS_FACING_BACK)
                        .build()

                    cameraProvider.unbindAll()
                    cameraProvider.bindToLifecycle(
                        lifecycleOwner,
                        cameraSelector,
                        preview
                    )

                }, exec)

                // 监听预览流状态变化
                val streamStateListener = PreviewView.OnPreviewStreamStateChangedListener { state ->
                    if (state == PreviewView.StreamState.STREAMING) {
                        Log.d("CameraPreview", "预览已启动,开始获取Bitmap")
                        // 单次获取Bitmap
                        val bitmap = previewView.bitmap
                        bitmap?.let {
                            Log.d("CameraPreview", "获取到Bitmap: 宽=${it.width}, 高=${it.height}")
                        } ?: Log.w("CameraPreview", "Bitmap仍为Null")

                        // 若需要实时获取,启动协程定时执行
                        coroutineScope.launch {
                            while (true) {
                                delay(1000) // 每秒获取一次,可调整间隔
                                val realtimeBitmap = previewView.bitmap
                                realtimeBitmap?.let {
                                    Log.d("CameraPreview", "实时Bitmap: 宽=${it.width}, 高=${it.height}")
                                }
                            }
                        }

                        // 如果只需要监听一次,可移除监听器
                        // previewView.removeOnPreviewStreamStateChangedListener(this)
                    }
                }

                previewView.addOnPreviewStreamStateChangedListener(streamStateListener)

                // View销毁时移除监听器,避免内存泄漏
                previewView.addOnAttachStateChangeListener(object : android.view.View.OnAttachStateChangeListener {
                    override fun onViewAttachedToWindow(v: android.view.View) {}
                    override fun onViewDetachedFromWindow(v: android.view.View) {
                        previewView.removeOnPreviewStreamStateChangedListener(streamStateListener)
                    }
                })

                previewView
            },
            modifier = Modifier.fillMaxSize(),
        )
    }
}

关键说明

  • 流状态判断:只有当StreamState为STREAMING时,预览才真正处于活跃状态,此时调用previewView.bitmap才能大概率获取到有效数据。
  • 内存泄漏防护:通过OnAttachStateChangeListener在View从窗口分离时移除监听器,避免潜在的内存泄漏问题。
  • 权限判断优化:将原代码中granted.size == 2改为granted.values.all { it },确保所有请求的权限都被用户授予。
  • 实时获取逻辑:使用协程和delay实现定时获取,可根据业务需求调整获取间隔。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 06:25:17