Kotlin调用setText时App崩溃,如何跨类更新TextView?
问题分析与解决方案
崩溃原因
你犯了两个关键错误:
- 手动实例化Activity:
val setMainActivity = MainActivity()是完全错误的操作。Android系统才负责Activity的生命周期管理,手动new的Activity实例不会执行onCreate等生命周期方法,textViewDetection作为lateinit变量根本没有初始化,调用setText会直接抛出UninitializedPropertyAccessException导致崩溃。 - 直接耦合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
相关产品推荐
相关产品推荐

