Android Kotlin图片编辑器:计算前ProgressBar无法显示问题排查
问题根源分析
你的ProgressBar无法显示,核心原因是UI线程被完全阻塞,导致系统根本没时间绘制ProgressBar:
onStopTrackingTouch方法运行在UI线程中,你设置progressBar.visibility = View.VISIBLE后,立刻执行了耗时的图片处理逻辑——而effectConfigApplyListener.onEffectConfigApply(it)最终会调用modifyPhoto方法,其中的runBlocking(Dispatchers.Default)会阻塞当前调用线程(也就是UI线程),直到所有子协程执行完毕。- UI线程被阻塞期间,系统无法处理任何UI绘制请求,所以ProgressBar的VISIBLE状态还没来得及渲染,你又立刻设置了
View.GONE,自然看不到它的影子。
另外,modifyPhoto里的runBlocking用法完全错误:runBlocking会阻塞调用它的线程,哪怕指定了Dispatchers.Default,它也只是在后台线程启动子协程,但调用线程(UI线程)会一直等待所有协程完成,等于把耗时操作的阻塞转嫁到了UI线程上。
解决方案
要解决这个问题,核心是把耗时的图片处理逻辑移到后台线程执行,同时正确控制ProgressBar的可见性:
1. 改造Fragment中的触摸事件处理
使用lifecycleScope(Fragment的生命周期绑定协程)启动后台任务,在主线程更新ProgressBar的可见性:
binding.seekBarEffectVal.setOnSeekBarChangeListener(object: SeekBar.OnSeekBarChangeListener{ override fun onProgressChanged(p0: SeekBar?, p1: Int, p2: Boolean) {} override fun onStartTrackingTouch(p0: SeekBar?) {} override fun onStopTrackingTouch(seekBar: SeekBar?) { seekBar?.let { val value = it.progress.toDouble() / it.max val effect = IEffectFactory().createEffect(param1!!, value) ?: return // 启动生命周期绑定的协程 lifecycleScope.launch { // 主线程显示进度条 binding.progressBarProcessingImage.visibility = View.VISIBLE try { // 切换到后台线程执行耗时的图片处理 withContext(Dispatchers.Default) { effectConfigApplyListener.onEffectConfigApply(effect) } } finally { // 无论成功失败,都在主线程隐藏进度条 binding.progressBarProcessingImage.visibility = View.GONE } } } } })
2. 修复图片处理方法的协程使用
把modifyPhoto改成挂起函数,用coroutineScope替代runBlocking——coroutineScope会等待所有子协程完成,但不会阻塞调用线程,只会挂起当前协程:
// 改成suspend函数,允许在协程中调用 override suspend fun modifyPhoto(bitmap: Bitmap): Bitmap { val width = bitmap.width val height = bitmap.height val pixels = IntArray(width * height) val newPixels = IntArray(width * height) bitmap.getPixels(pixels,0, width, 0, 0, width, height) val coef = getCoefficient() // 用coroutineScope替代runBlocking,挂起协程而非阻塞线程 coroutineScope { for (y in 0 until height) { launch { for (x in 0 until width) { val colour = pixels[y * width + x] val red = truncate(Color.red(colour) + coef) val green = truncate(Color.green(colour) + coef) val blue = truncate(Color.blue(colour) + coef) newPixels[y * width + x] = Color.rgb(red, green, blue) } } } } val newBitmap = Bitmap.createBitmap(width, height, bitmap.config) newBitmap.setPixels(newPixels, 0, width, 0, 0, width, height) return newBitmap }
3. 调整监听接口的方法
需要把effectConfigApplyListener的onEffectConfigApply方法也改成挂起函数,这样才能在withContext(Dispatchers.Default)中调用:
interface EffectConfigApplyListener { suspend fun onEffectConfigApply(effect: IEffect) }
然后在MainActivity的实现中,直接调用挂起的modifyPhoto方法即可。
额外优化建议
- 你当前在
modifyPhoto中为每一行都启动一个协程,对于大尺寸图片来说会创建大量协程,反而可能降低性能。可以改用async配合awaitAll,或者直接在后台线程用普通循环处理(如果单线程处理速度足够的话)。 - 可以考虑用
ProgressBar.setProgress来实时更新进度,提升用户体验。
内容的提问来源于stack exchange,提问作者Bartosz Kowalski
相关产品推荐
相关产品推荐

