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

Kotlin调用setText时App崩溃,如何跨类更新TextView?

问题分析与解决方案

崩溃原因

你犯了两个关键错误:

  1. 手动实例化Activity:val setMainActivity = MainActivity() 是完全错误的操作。Android系统才负责Activity的生命周期管理,手动new的Activity实例不会执行onCreate等生命周期方法,textViewDetection作为lateinit变量根本没有初始化,调用setText会直接抛出UninitializedPropertyAccessException导致崩溃。
  2. 直接耦合UI层与业务层:跨类直接引用Activity的UI组件,不仅违反单一职责原则,还容易引发内存泄漏。

正确解决方案

你已经定义了DetectorListener接口,完全可以利用这个回调机制实现解耦,让MainActivity自己处理UI更新逻辑,具体步骤如下:

1. 修改MainActivity.kt,实现DetectorListener接口

class MainActivity : AppCompatActivity(), ObjectDetectorHelper.DetectorListener {
    private lateinit var activityMainBinding: ActivityMainBinding
    private lateinit var textViewDetection: EditText
    private lateinit var objectDetectorHelper: ObjectDetectorHelper

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        activityMainBinding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(activityMainBinding.root)
        textViewDetection = activityMainBinding.editText

        // 初始化ObjectDetectorHelper,传入当前Activity作为回调监听
        objectDetectorHelper = ObjectDetectorHelper(context = this, objectDetectorListener = this)
    }

    override fun onError(error: String) {
        // 在主线程处理错误提示
        runOnUiThread {
            Toast.makeText(this, error, Toast.LENGTH_SHORT).show()
        }
    }

    override fun onResults(
        results: MutableList<Detection>?,
        inferenceTime: Long,
        imageHeight: Int,
        imageWidth: Int
    ) {
        // 必须在主线程更新UI
        runOnUiThread {
            results?.let { detections ->
                // 拼接检测结果文本
                val resultText = detections.joinToString("\n") { detection ->
                    val category = detection.categories.first()
                    "${category.label}: %.2f".format(category.score)
                }
                textViewDetection.setText(resultText)
            } ?: run {
                textViewDetection.setText("未检测到物体")
            }
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        // 释放检测器资源
        objectDetectorHelper.clearObjectDetector()
    }
}

2. 修改ObjectDetectorHelper.kt,移除错误代码

删除手动实例化Activity的代码,以及直接调用UI组件的逻辑,只保留回调通知:

class ObjectDetectorHelper(
    var threshold: Float = 0.5f,
    var numThreads: Int = 2,
    var maxResults: Int = 3,
    var currentDelegate: Int = 0,
    var currentModel: Int = 0,
    val context: Context,
    val objectDetectorListener: DetectorListener?
) {
    // 移除错误代码:val setMainActivity = MainActivity()

    private var objectDetector: ObjectDetector? = null

    init {
        setupObjectDetector()
    }

    // ... 其他方法保持不变 ...

    @SuppressLint("SetTextI18n")
    fun detect(image: Bitmap, imageRotation: Int) {
        if (objectDetector == null) {
            setupObjectDetector()
        }

        var inferenceTime = SystemClock.uptimeMillis()

        val imageProcessor = ImageProcessor.Builder()
            .add(Rot90Op(-imageRotation / 90))
            .build()

        val tensorImage = imageProcessor.process(TensorImage.fromBitmap(image))

        val results = objectDetector?.detect(tensorImage)
        inferenceTime = SystemClock.uptimeMillis() - inferenceTime
        
        // 仅通过回调通知结果,UI更新交给MainActivity处理
        objectDetectorListener?.onResults(
            results,
            inferenceTime,
            tensorImage.height,
            tensorImage.width
        )

        // 移除错误代码:
        // if (results != null) {
        //     setMainActivity.textViewDetection.setText("pio")
        // }
    }

    // ... 接口和伴生对象保持不变 ...
}

核心思路

  • 利用回调接口实现业务逻辑(物体检测)与UI层的解耦,避免直接引用Activity实例
  • 所有UI操作必须在主线程执行,通过runOnUiThread确保线程安全
  • 遵循Android组件生命周期,在Activity销毁时释放检测器资源,避免内存泄漏

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 05:12:01