Эх сурвалжийг харах

perf(image): 优化图片加载性能

- 使用 BitmapDecodeHelper 替换 BitmapFactory 进行采样解码
- 添加单线程执行器进行异步图片解码处理
- 实现图片视图布局完成后再进行尺寸计算和加载
- 防止因 Activity 销毁导致的内存泄漏问题
- 在 SprayingFinishActivity、SprayingOCRActivity 和 SprayingResultActivity 中统一图片加载逻辑
徐勤民 2 өдөр өмнө
parent
commit
7310127d56

+ 19 - 2
app/src/main/java/com/nova/brain/glass/ui/SprayingFinishActivity.kt

@@ -2,10 +2,11 @@ package com.nova.brain.glass.ui
 
 import android.app.Activity
 import android.content.Intent
-import android.graphics.BitmapFactory
+import androidx.core.view.doOnLayout
 import androidx.recyclerview.widget.RecyclerView
 import com.nova.brain.glass.R
 import com.nova.brain.glass.databinding.ActivitySprayingFinishBinding
+import com.nova.brain.glass.helper.BitmapDecodeHelper
 import com.nova.brain.glass.helper.OfflineCmdListener
 import com.nova.brain.glass.helper.OfflineCmdServiceHelper
 import com.nova.brain.glass.helper.SprayingPhotoManager
@@ -15,6 +16,7 @@ import com.xuqm.base.adapter.BasePagedAdapter
 import com.xuqm.base.adapter.CommonPagedAdapter
 import com.xuqm.base.adapter.ViewHolder
 import com.xuqm.base.ui.BaseListFormLayoutNormalActivity
+import java.util.concurrent.Executors
 
 class SprayingFinishActivity :
     BaseListFormLayoutNormalActivity<ItemItem, SprayingFinishVM, ActivitySprayingFinishBinding>() {
@@ -22,6 +24,7 @@ class SprayingFinishActivity :
     override fun fullscreen(): Boolean = true
 
     override fun getRecyclerOrientation(): Int = RecyclerView.VERTICAL
+    private val imageDecodeExecutor = Executors.newSingleThreadExecutor()
 
     private val listener = object : OfflineCmdListener {
         override fun onOfflineCmd(cmd: String) {
@@ -52,7 +55,21 @@ class SprayingFinishActivity :
         val imageViews = listOf(binding.photo1, binding.photo2, binding.photo3)
         imageViews.forEach { it.setImageDrawable(null) }
         photos.forEachIndexed { index, path ->
-            imageViews.getOrNull(index)?.setImageBitmap(BitmapFactory.decodeFile(path))
+            imageViews.getOrNull(index)?.let { imageView ->
+                imageView.doOnLayout {
+                    val targetWidth = imageView.width.coerceAtLeast(1)
+                    val targetHeight = imageView.height.coerceAtLeast(1)
+                imageDecodeExecutor.execute {
+                    val bitmap = BitmapDecodeHelper.decodeSampledBitmap(path, targetWidth, targetHeight)
+                    runOnUiThread {
+                        if (isFinishing || isDestroyed) {
+                            return@runOnUiThread
+                        }
+                        imageView.setImageBitmap(bitmap)
+                    }
+                }
+                }
+            }
         }
     }
 

+ 18 - 3
app/src/main/java/com/nova/brain/glass/ui/SprayingOCRActivity.kt

@@ -1,13 +1,14 @@
 package com.nova.brain.glass.ui
 
 import android.content.Intent
-import android.graphics.BitmapFactory
 import android.os.CountDownTimer
 import android.os.Environment
 import android.view.WindowManager
+import androidx.core.view.doOnLayout
 import androidx.recyclerview.widget.RecyclerView
 import com.nova.brain.glass.R
 import com.nova.brain.glass.databinding.ActivitySprayingOcrBinding
+import com.nova.brain.glass.helper.BitmapDecodeHelper
 import com.nova.brain.glass.helper.OfflineCmdListener
 import com.nova.brain.glass.helper.OfflineCmdServiceHelper
 import com.nova.brain.glass.helper.SprayingPhotoManager
@@ -24,6 +25,7 @@ import com.xuqm.base.common.LogHelper
 import com.xuqm.base.extensions.showMessage
 import com.xuqm.base.ui.BaseListFormLayoutNormalActivity
 import java.io.File
+import java.util.concurrent.Executors
 import java.util.UUID
 
 class SprayingOCRActivity :
@@ -63,6 +65,7 @@ class SprayingOCRActivity :
     }
     private val photoCallbackId = UUID.randomUUID().toString()
     private var resultCountdown: CountDownTimer? = null
+    private val imageDecodeExecutor = Executors.newSingleThreadExecutor()
 
     private val mPhotoFileCallback = object : PhotoFileCallback.Stub() {
         override fun onTakePhoto(path: String) {
@@ -139,8 +142,20 @@ class SprayingOCRActivity :
     override fun adapter(): BasePagedAdapter<ItemItem> = adapter
 
     private fun showPhoto(path: String) {
-        binding.content.setImageBitmap(BitmapFactory.decodeFile(path))
-        restartResultCountdown(path)
+        binding.content.doOnLayout {
+            val targetWidth = it.width.coerceAtLeast(1)
+            val targetHeight = it.height.coerceAtLeast(1)
+            imageDecodeExecutor.execute {
+                val bitmap = BitmapDecodeHelper.decodeSampledBitmap(path, targetWidth, targetHeight)
+                runOnUiThread {
+                    if (isFinishing || isDestroyed) {
+                        return@runOnUiThread
+                    }
+                    binding.content.setImageBitmap(bitmap)
+                    restartResultCountdown(path)
+                }
+            }
+        }
     }
 
     private fun restartResultCountdown(path: String) {

+ 21 - 2
app/src/main/java/com/nova/brain/glass/ui/SprayingResultActivity.kt

@@ -2,14 +2,15 @@ package com.nova.brain.glass.ui
 
 import android.app.Activity
 import android.content.Intent
-import android.graphics.BitmapFactory
 import android.graphics.Paint
 import android.os.Environment
 import android.view.WindowManager
 import androidx.activity.result.contract.ActivityResultContracts
+import androidx.core.view.doOnLayout
 import androidx.recyclerview.widget.RecyclerView
 import com.nova.brain.glass.R
 import com.nova.brain.glass.databinding.ActivitySprayingResultBinding
+import com.nova.brain.glass.helper.BitmapDecodeHelper
 import com.nova.brain.glass.helper.OfflineCmdListener
 import com.nova.brain.glass.helper.OfflineCmdServiceHelper
 import com.nova.brain.glass.helper.SprayingPhotoManager
@@ -25,6 +26,7 @@ import com.xuqm.base.common.LogHelper
 import com.xuqm.base.extensions.showMessage
 import com.xuqm.base.ui.BaseListFormLayoutNormalActivity
 import java.io.File
+import java.util.concurrent.Executors
 import java.util.UUID
 
 class SprayingResultActivity :
@@ -33,6 +35,7 @@ class SprayingResultActivity :
     override fun fullscreen(): Boolean = true
 
     override fun getRecyclerOrientation(): Int = RecyclerView.HORIZONTAL
+    private val imageDecodeExecutor = Executors.newSingleThreadExecutor()
     private var status = true
     private val manualResultLauncher =
         registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
@@ -157,7 +160,7 @@ class SprayingResultActivity :
         super.initData()
         window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
         intent.getStringExtra("path")?.apply {
-            binding.iv.setImageBitmap(BitmapFactory.decodeFile(this))
+            showResultImage(this)
             setStatusImage()
         }
     }
@@ -231,4 +234,20 @@ class SprayingResultActivity :
     }
 
     override fun adapter(): BasePagedAdapter<ItemItem> = adapter
+
+    private fun showResultImage(path: String) {
+        binding.iv.doOnLayout {
+            val targetWidth = it.width.coerceAtLeast(1)
+            val targetHeight = it.height.coerceAtLeast(1)
+            imageDecodeExecutor.execute {
+                val bitmap = BitmapDecodeHelper.decodeSampledBitmap(path, targetWidth, targetHeight)
+                runOnUiThread {
+                    if (isFinishing || isDestroyed) {
+                        return@runOnUiThread
+                    }
+                    binding.iv.setImageBitmap(bitmap)
+                }
+            }
+        }
+    }
 }