Android Studio 自定义Dialog实时更新UI卡顿冻结问题
问题原因
Android UI操作遵循单线程模型,所有界面绘制、控件更新都依赖主线程消息队列调度。你的代码存在两个核心问题:
- 你通过
Handler(Looper.getMainLooper()).post投递的任务本身就运行在主线程,内部嵌套的runOnUiThread完全是冗余代码 - 你在主线程任务里循环调用
SystemClock.sleep()直接阻塞了主线程,这期间主线程无法执行任何UI绘制操作,你连续设置的文本更新只会被加入消息队列排队,等所有sleep执行完毕、最后一次把文本设为"3"后,主线程才会获得调度机会执行重绘,因此你看不到"1""2"的切换过程,同时Dialog会因为主线程被阻塞出现冻结无响应的表现。
另外你设置StrictMode.ThreadPolicy.Builder().permitAll().build()相当于关闭了主线程违规检测,会掩盖主线程阻塞导致的ANR风险,不建议使用。
修复方案
不要阻塞主线程,改用主线程Handler的延迟投递机制实现间隔更新,保证两次更新的间隔期主线程可以正常处理UI渲染:
package com.slimakoi.myfirstapp import android.annotation.SuppressLint import android.app.Dialog import android.graphics.Color import android.graphics.drawable.ColorDrawable import android.os.Handler import android.os.Looper import android.os.Bundle import android.widget.ImageView import android.widget.TextView import androidx.appcompat.app.AppCompatActivity import androidx.core.content.ContextCompat class TrackerActivity : AppCompatActivity() { private val mainHandler = Handler(Looper.getMainLooper()) private lateinit var dialog: Dialog private lateinit var nMsg: TextView private lateinit var nImg: ImageView // 间隔更新任务 private val updateTextRunnable = object : Runnable { private val textSequence = listOf("1", "2", "3") private var currentTextPos = 0 private var loopCount = 0 private val maxLoop = 5 override fun run() { if (loopCount >= maxLoop) { return } nMsg.text = textSequence[currentTextPos] currentTextPos++ if (currentTextPos >= textSequence.size) { currentTextPos = 0 loopCount++ } // 延迟500ms执行下一次更新,期间主线程可自由处理UI渲染 mainHandler.postDelayed(this, 500) } } @SuppressLint("SetTextI18n") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_tracker) dialog = Dialog(this) dialog.setContentView(R.layout.notification) dialog.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) nMsg = dialog.findViewById(R.id.notificationMessage) nImg = dialog.findViewById(R.id.notificationImage) nMsg.text = "Loading..." nImg.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.error)) dialog.show() // 等Dialog首次渲染完成后启动文本更新 nMsg.post { mainHandler.post(updateTextRunnable) } } override fun onDestroy() { super.onDestroy() // 页面销毁时移除所有待执行任务,避免内存泄漏 mainHandler.removeCallbacksAndMessages(null) dialog.dismiss() } }
注意事项
- 任何时候都不要在主线程执行
sleep、耗时IO、网络请求等阻塞操作,否则会导致界面冻结、触发ANR - 使用Handler投递延迟任务时,必须在页面销毁阶段移除回调,避免持有页面引用导致内存泄漏
- 不建议通过StrictMode放开所有线程策略检测,默认的检测规则可以帮你及时发现主线程违规操作。
内容的提问来源于stack exchange,提问作者Alphexo
相关产品推荐
相关产品推荐

