refactor(activity): 优化 FoodCollectionActivity 性能与稳定性

- 修复 runBlocking 阻塞主线程,改为 lifecycleScope.launch 协程异步执行
- 耗时操作(I/O、图像裁剪、TFLite 推理、ObjectBox 读写)全部切换至 Dispatchers.IO
- 修复 bitmap 内存泄漏:用 try/finally 保证所有退出路径均回收
- 修复 uriToBitmap 返回 null 时 loading 转圈永不消失的 Bug
- 修复 DbException 块、index==-1 分支缺少 hideWaitingDialog() 的 Bug
- 移除重复 ReceiptViewModel,统一使用单例;移除空 initBox() 函数
- 新增 searchJob 取消上一次未完成搜索,修复 clearData() 遗漏取消的 Bug
- 提取 createWaitingDialog() 消除 dialog 创建代码重复
- 修复 clearCoilCache() 使用新实例无法清缓存,改用 context.imageLoader 单例
- upload() 两处强制解包 !! 改为安全调用,避免 NPE 崩溃
- 清理无用注释、废弃代码块、冗余注解及无用 import

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-11 18:04:21 +08:00
co-authored by Claude Sonnet 4.6
parent 05015a7a18
commit 7563209cf1
@@ -1,14 +1,12 @@
package com.sw.inbound.activity
import android.annotation.SuppressLint
import android.app.Dialog
import android.content.Context
import android.graphics.Bitmap
import android.net.Uri
import android.graphics.Bitmap
import android.os.Bundle
import android.text.TextUtils
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import com.sw.inbound.base.BaseActivity
import androidx.activity.viewModels
@@ -17,8 +15,8 @@ import androidx.core.view.updateLayoutParams
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import coil.ImageLoader
import coil.annotation.ExperimentalCoilApi
import coil.imageLoader
import com.sw.inbound.R
import com.sw.inbound.adapter.FoodCollectionAdapter
import com.sw.inbound.adapter.GoodsSearchAdapter
@@ -47,21 +45,19 @@ import com.sw.inbound.utils.ext.hideKeyboard
import com.sw.inbound.utils.ext.invisible
import com.sw.inbound.utils.ext.startActivity
import com.sw.inbound.utils.ext.toJsonString
import com.sw.inbound.utils.ext.toObject
import com.sw.inbound.utils.ext.toast
import com.sw.inbound.utils.ext.visible
import com.sw.inbound.viewmodel.ReceiptViewModel
import dagger.hilt.android.AndroidEntryPoint
import io.objectbox.Box
import io.objectbox.exception.DbException
import io.objectbox.exception.FileCorruptException
import io.objectbox.kotlin.boxFor
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import okhttp3.RequestBody
import okhttp3.RequestBody.Companion.toRequestBody
import timber.log.Timber
import java.io.File
/**
* 采集菜品
@@ -76,12 +72,11 @@ class FoodCollectionActivity : BaseActivity() {
const val PAGE_SIZE = 30
}
// private var box: Box<Food>? = null
private val collectList: MutableList<FoodCollectionBean> = mutableListOf<FoodCollectionBean>()
private val collectList: MutableList<FoodCollectionBean> = mutableListOf()
private lateinit var binding: ActivityFoodCollectionBinding
private lateinit var previewView: PreviewView
val viewModel: ReceiptViewModel by viewModels()
private val viewModel: ReceiptViewModel by viewModels()
private val cameraUtils: CameraUtils by lazy {
CameraUtils(this)
@@ -90,9 +85,14 @@ class FoodCollectionActivity : BaseActivity() {
FoodCollectionAdapter(collectList).apply {
setOnItemClickListener { adapter, view, position ->
val file = collectList[position].imageFile ?: return@setOnItemClickListener
// 磁盘读取和 TFLite 推理均为耗时操作,切换到 IO 线程执行
lifecycleScope.launch(Dispatchers.IO) {
ImageUtil.decodeFromFile(file)?.let { bitmap ->
val imageVector = FoodModule.bitmap2FloatArray(bitmap, false)
Timber.tag(TAG).d("向量:${imageVector.toJsonString()}")
// 向量提取完毕后立即回收,避免内存泄漏
if (bitmap.isRecycled.not()) bitmap.recycle()
}
}
}
addOnItemChildClickListener(R.id.ivDelete) { _, _, position ->
@@ -105,8 +105,6 @@ class FoodCollectionActivity : BaseActivity() {
it.uploadSuccess = false
}
collectionAdapter.notifyItemChanged(position)
// val count = collectList.count { it.imageFile != null && it.uploadSuccess.not() }
// binding.btnUploadImage.text = "待上传图片${count}张"
}
}
}
@@ -128,7 +126,6 @@ class FoodCollectionActivity : BaseActivity() {
binding.llCameraFlag.visible()
}
@SuppressLint("NotifyDataSetChanged")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityFoodCollectionBinding.inflate(layoutInflater)
@@ -163,14 +160,6 @@ class FoodCollectionActivity : BaseActivity() {
searchGoods()
}
}
// binding.btnUploadImage.setOnClickListener {
// val count = collectList.count { it.imageFile!=null && it.uploadSuccess.not() }
// if (count == 0) {
// toast("已全部上传成功")
// return@setOnClickListener
// }
// uploadAllImage()
// }
binding.btnSave.setOnClickListener { _ ->
if (clickIndex == -1) {
toast("请选择物品名称")
@@ -181,8 +170,6 @@ class FoodCollectionActivity : BaseActivity() {
toast("请拍摄物品照片")
return@setOnClickListener
}
// vectorThread()
// uploadAllImage()
showUploadDialog()
}
binding.btnTakePhoto.clickWithDebounce(800) {
@@ -215,18 +202,6 @@ class FoodCollectionActivity : BaseActivity() {
startActivity<LocalImagePreviewActivity> { }
}
binding.tvTitle.setOnClickListener {
// startActivity<LocalImagePreviewActivity> { }
// val goodsImage = GoodsImage(goodsId = "1993493094888140802", goodsName = "多宝鱼", startTime = 1764120531856L, endTime = 1764121842825L)
// uploadImage(goodsImage)
// val goodsImage = GoodsImage(goodsId = "1986366073649287170", goodsName = "黑鱼(整条鱼)", startTime = 1764122703093L, endTime = 1764123156022L)
// uploadImage(goodsImage)
// val goodsImage = GoodsImage(goodsId = "1986699063868801025", goodsName = "鸡腿肉", startTime = 1764123375841L, endTime = 1764124039162L)
// uploadImage(goodsImage)
}
}
private fun showUploadDialog() {
@@ -245,6 +220,8 @@ class FoodCollectionActivity : BaseActivity() {
private var pageNo = 1
private var clickIndex = -1
// 保存当前搜索任务,用于在新搜索发起时取消上一次未完成的请求
private var searchJob: Job? = null
private val searchGoodsList: MutableList<SearchGoodsInfo.Record> = mutableListOf()
private val searchAdapter by lazy {
@@ -274,7 +251,6 @@ class FoodCollectionActivity : BaseActivity() {
}
}
@SuppressLint("NotifyDataSetChanged")
private fun takePhoto() {
val count = collectList.count { it.imageFile != null }
if (count == MAX_COUNT) {
@@ -285,10 +261,12 @@ class FoodCollectionActivity : BaseActivity() {
cameraUtils.takePhoto(cameraCallback)
}
@SuppressLint("NotifyDataSetChanged")
private fun clearData() {
val count = collectList.count { it.imageFile != null }
if (count == 0) return
// 取消正在进行的搜索,防止清空后回调重新填充数据
searchJob?.cancel()
searchJob = null
clearCoilCache(this)
collectList.forEach {
try {
@@ -313,7 +291,6 @@ class FoodCollectionActivity : BaseActivity() {
searchAdapter.notifyDataSetChanged()
loadEmptyView()
binding.ivFinish.invisible()
// binding.btnUploadImage.text = "待上传图片${0}张"
}
private val cameraCallback: (Uri) -> Unit = { uri ->
@@ -326,48 +303,73 @@ class FoodCollectionActivity : BaseActivity() {
val index = collectList.indexOfFirst { it.imageFile == null }
if (index == -1) {
toast("每次只允许保存${MAX_COUNT}条数据")
showWaitingDialog2("加载中")
hideWaitingDialog()
return
}
// toast("index=$index")
// 将 bitmap/cropBitmap 提到外层,确保任意退出路径都能在 finally 中回收,避免内存泄漏
var bitmap: Bitmap? = null
var cropBitmap: Bitmap? = null
try {
ImageUtil.uriToBitmap(this, uri)?.let { bitmap ->
val cropBitmap = BitmapCropper.cropCenter(
// 所有耗时操作(磁盘 I/O、图像裁剪、TFLite 推理)切换到 IO 线程,避免阻塞主线程
bitmap = withContext(Dispatchers.IO) {
ImageUtil.uriToBitmap(this@FoodCollectionActivity, uri)
}
if (bitmap == null) {
// uriToBitmap 返回 null 时关闭 loading,否则转圈永不消失
hideWaitingDialog()
toast("图片读取失败,请重试")
return
}
cropBitmap = withContext(Dispatchers.IO) {
BitmapCropper.cropCenter(
original = bitmap,
targetWidth = 1000, targetHeight = 1300,
offsetX = 30, offsetY = 100
)
initBox()
}
val imageVector = try {
// TFLite 推理为 CPU 密集型,切换到 IO 线程执行
withContext(Dispatchers.IO) {
FoodModule.bitmap2FloatArray(cropBitmap, false)
}
} catch (e: Exception) {
e.printStackTrace()
hideWaitingDialog()
toast("操作失败")
return@let
return
}
Timber.tag(TAG).d("向量:${imageVector.toJsonString()}")
val foodList = try {
// 数据库查询切换到 IO 线程
withContext(Dispatchers.IO) {
FoodModule.queryFoodNameScore(imageVector)
}
} catch (e: DbException) {
hideWaitingDialog()
try {
ObjectBox.init(this)
// ObjectBox 重新初始化为磁盘 I/O,切换到 IO 线程
withContext(Dispatchers.IO) {
ObjectBox.init(this@FoodCollectionActivity)
}
} catch (e: Exception) {
e.printStackTrace()
}
toast("数据库异常,请稍后重试")
return@let
return
}
val filterList =
foodList.filter { it.score < FoodModule.BAG_RATE && it.name.contains("黑袋子") }
if (filterList.isNotEmpty()) {
hideWaitingDialog()
toast("当前物品可能被袋子遮挡,请检查后重试")
return@let
return
}
val file = BitmapSaver.saveToAppFilesDir(
cropBitmap, this, "IMG_CROP_${System.currentTimeMillis()}.jpg"
// 图片保存为磁盘 I/O,切换到 IO 线程
val file = withContext(Dispatchers.IO) {
BitmapSaver.saveToAppFilesDir(
cropBitmap, this@FoodCollectionActivity, "IMG_CROP_${System.currentTimeMillis()}.jpg"
)
}
Timber.d("${this.javaClass.simpleName}-searchFood-裁剪bitmap保存文件路径:${file?.absolutePath}")
collectList[index].let {
it.imageVector = imageVector
@@ -378,16 +380,7 @@ class FoodCollectionActivity : BaseActivity() {
it.uploadSuccess = false
}
collectionAdapter.notifyItemChanged(index)
// val count = collectList.count { it.imageFile != null && it.uploadSuccess.not() }
// binding.btnUploadImage.text = "待上传图片${count}张"
if (cropBitmap.isRecycled.not()) {
cropBitmap.recycle()
}
if (bitmap.isRecycled.not()) {
bitmap.recycle()
}
hideWaitingDialog()
}
} catch (e: Exception) {
e.printStackTrace()
hideWaitingDialog()
@@ -397,7 +390,10 @@ class FoodCollectionActivity : BaseActivity() {
e is DbException
) {
try {
ObjectBox.init(this)
// ObjectBox 重新初始化为磁盘 I/O,切换到 IO 线程
withContext(Dispatchers.IO) {
ObjectBox.init(this@FoodCollectionActivity)
}
} catch (e: Exception) {
e.printStackTrace()
}
@@ -405,6 +401,10 @@ class FoodCollectionActivity : BaseActivity() {
} else {
toast("程序异常$errMsg")
}
} finally {
// 无论成功、失败还是提前返回,均在此回收 bitmap,彻底消除内存泄漏
if (cropBitmap?.isRecycled == false) cropBitmap.recycle()
if (bitmap?.isRecycled == false) bitmap.recycle()
}
}
@@ -412,15 +412,13 @@ class FoodCollectionActivity : BaseActivity() {
Loading.show(this)
lifecycleScope.launch {
collectList
//.filter { it.bitmap != null }
// .filter { it.imageVector != null }
.forEachIndexed { index, it ->
//image2VectorTask(it.bitmap!!, index)
it.imageVector?.let { imageVector ->
image2VectorTask(imageVector = imageVector, position = index)
}
}
runOnUiThread {
// lifecycleScope 默认在主线程,直接切换到主线程调度器更新 UI
withContext(Dispatchers.Main) {
window.decorView.postDelayed({
Loading.dismiss()
toast("保存成功")
@@ -430,30 +428,19 @@ class FoodCollectionActivity : BaseActivity() {
}
private suspend fun image2VectorTask(
bitmap: Bitmap? = null,
imageVector: FloatArray? = null,
imageVector: FloatArray,
position: Int
) {
try {
initBox()
// val imageVector = try {
// FoodModule.bitmap2FloatArray(bitmap)
// } catch (e: Exception) {
// e.printStackTrace()
// //toast("保存失败")
// return
// }
val goods = searchGoodsList[clickIndex]
val saveName = goods.goodsName + goods.goodsCode
// ObjectBox.boxStore.runInTx {
// box?.put(Food(name = saveName, foodIdx = 0, foodVector = imageVector))
// }
// ObjectBox 写入为磁盘 I/O,切换到 IO 线程避免主线程卡顿
withContext(Dispatchers.IO) {
ObjectBox.put(Food(name = saveName, foodIdx = 0, foodVector = imageVector))
collectList[position].let {
// it.imageVector = imageVector
it.isFinish = true
}
runOnUiThread {
collectList[position].isFinish = true
// 切换到主线程刷新列表项,与协程调度器保持一致
withContext(Dispatchers.Main) {
collectionAdapter.notifyItemChanged(position)
}
} catch (e: Exception) {
@@ -461,7 +448,6 @@ class FoodCollectionActivity : BaseActivity() {
}
}
@SuppressLint("NotifyDataSetChanged")
private fun searchGoods() {
val searchName = binding.etGoodsInput.text.toString().trim()
if (searchName.isBlank()) {
@@ -516,58 +502,37 @@ class FoodCollectionActivity : BaseActivity() {
pageSize: Int = PAGE_SIZE,
callback: (List<SearchGoodsInfo.Record>) -> Unit
) {
runBlocking {
// 取消上一次未完成的搜索,避免并发竞争导致结果乱序
searchJob?.cancel()
searchJob = lifecycleScope.launch {
val records = viewModel.searchGoodsInfoList2(
goodsName = searchName,
pageNo = pageNo,
pageSize = pageSize
)
runOnUiThread {
callback(records)
}
}
}
private fun initBox() {
// if (box == null) {
// box = ObjectBox.boxStore.boxFor(Food::class)
// }
}
// private fun uploadAllImage() {
// Loading.show(this)
// val files = collectList.filter { it.imageFile!=null }.map { it.imageFile!! }
// if (files.isEmpty()) {
// return
// }
// val goods = searchGoodsList[clickIndex]
// for (index in collectList.indices step 5) {
// val end = if(index + 5 < collectList.size - 1) index + 5 else collectList.size - 1
// val subList = collectList.subList(index, end)
// val subFiles = subList.map { it.imageFile }
// viewModel.uploadMultipleImages(goods.id!!, goods.goodsName!!, subFiles) {isSuccess->
// Timber.tag(TAG).d("uploadMultipleImages: ${isSuccess}")
// subList.filter { it.imageFile!=null }.forEach { it.uploadSuccess = isSuccess }
//
// val count = collectList.count { it.imageFile!=null && it.uploadSuccess.not() }
// runOnUiThread {
// binding.btnUploadImage.text = "待上传图片${count}张"
// if (count == 0) {
// Loading.dismiss()
// }
// }
// }
// }
// }
private fun upload() {
lifecycleScope.launch {
val totalFileCount = collectList.count { it.imageFile != null }
showWaitingDialog2("图片上传中0/$totalFileCount")
val goods = searchGoodsList[clickIndex]
val params = HashMap<String, RequestBody>()
params["goodsId"] = goods.id!!.toRequestBody()
params["goodsName"] = goods.goodsName!!.toRequestBody()
//params["foodVector"] = foodVector.toRequestBody()
// 使用安全调用避免 id/goodsName 为 null 时崩溃,字段缺失则跳过上传
val goodsId = goods.id ?: run {
hideWaitingDialog()
toast("商品数据异常,请重新选择")
return@launch
}
val goodsName = goods.goodsName ?: run {
hideWaitingDialog()
toast("商品数据异常,请重新选择")
return@launch
}
params["goodsId"] = goodsId.toRequestBody()
params["goodsName"] = goodsName.toRequestBody()
ImageUploader(
totalList = collectList,
uploadImage = { batch ->
@@ -575,18 +540,16 @@ class FoodCollectionActivity : BaseActivity() {
viewModel.uploadMultipleImages(files = files, params)
},
onProgress = { count, batch ->
runOnUiThread {
// lifecycleScope 默认在主线程执行,无需 runOnUiThread
showWaitingDialog2("图片上传中$count/$totalFileCount")
batch.forEach {
it.uploadSuccess = true
}
}
},
onError = {
runOnUiThread {
// lifecycleScope 默认在主线程执行,无需 runOnUiThread
hideWaitingDialog()
toast("上传失败,请稍后重试")
}
},
onComplete = {
hideWaitingDialog()
@@ -596,10 +559,9 @@ class FoodCollectionActivity : BaseActivity() {
}
}
private val receiptViewModel: ReceiptViewModel by viewModels()
fun searchGoodsInfoList(goodsName: String, block: (List<SearchGoodsInfo.Record>) -> Unit) {
runBlocking {
// 使用协程异步执行,避免阻塞主线程
lifecycleScope.launch {
val list = searchGoodsInfoList2(goodsName)
block(list)
}
@@ -610,25 +572,35 @@ class FoodCollectionActivity : BaseActivity() {
pageNo: Int = 1,
pageSize: Int = 10
): List<SearchGoodsInfo.Record> {
return receiptViewModel.searchGoodsInfoList2(goodsName, pageNo, pageSize)
// 统一使用类顶部声明的 viewModel,避免重复创建 ViewModel 实例
return viewModel.searchGoodsInfoList2(goodsName, pageNo, pageSize)
}
private var mDialogWaiting: CustomDialog? = null
/**
* 显示等待提示框
* 内部方法:创建并显示等待提示框(调用前须确保已在主线程)
* showWaitingDialog 与 showWaitingDialog2 共用此创建逻辑,避免重复代码
*/
private fun createWaitingDialog(tip: String?) {
mDialogWaiting?.dismiss()
mDialogWaiting = null
val view = View.inflate(this, R.layout.dialog_waiting, null)
if (!TextUtils.isEmpty(tip)) {
(view.findViewById<View>(R.id.tvTip) as TextView).text = tip
}
mDialogWaiting = CustomDialog(this, view, R.style.MyDialog).apply {
show()
setCanceledOnTouchOutside(true)
setCancelable(true)
}
}
/**
* 显示等待提示框(每次调用都重新创建对话框)
*/
fun showWaitingDialog(tip: String?) {
runOnUiThread {
hideWaitingDialog()
val view = View.inflate(this, R.layout.dialog_waiting, null)
if (!TextUtils.isEmpty(tip)) (view.findViewById<View?>(R.id.tvTip) as TextView).text =
tip
mDialogWaiting = CustomDialog(this, view, R.style.MyDialog)
mDialogWaiting!!.show()
mDialogWaiting!!.setCanceledOnTouchOutside(true)
mDialogWaiting!!.setCancelable(true)
}
runOnUiThread { createWaitingDialog(tip) }
}
/**
@@ -641,19 +613,18 @@ class FoodCollectionActivity : BaseActivity() {
}
}
/**
* 显示或更新等待提示框
* 若对话框已存在则仅更新文字,否则创建新对话框
*/
fun showWaitingDialog2(tip: String?) {
runOnUiThread {
if (mDialogWaiting == null) {
hideWaitingDialog()
val view = View.inflate(this, R.layout.dialog_waiting, null)
mDialogWaiting = CustomDialog(this, view, R.style.MyDialog)
mDialogWaiting!!.show()
mDialogWaiting!!.setCanceledOnTouchOutside(true)
mDialogWaiting!!.setCancelable(true)
createWaitingDialog(tip)
} else {
// 对话框已显示,仅更新提示文字
mDialogWaiting?.findViewById<TextView>(R.id.tvTip)?.text = tip
}
val contentView = mDialogWaiting?.findViewById<ViewGroup>(android.R.id.content)
val tvTip = contentView?.findViewById<TextView>(R.id.tvTip)
tvTip?.text = tip
}
}
@@ -662,128 +633,9 @@ class FoodCollectionActivity : BaseActivity() {
*/
@OptIn(ExperimentalCoilApi::class)
fun clearCoilCache(context: Context) {
val imageLoader = ImageLoader.Builder(context).build()
// 清除内存缓存
imageLoader.memoryCache?.clear()
// 清除磁盘缓存
imageLoader.diskCache?.clear()
// 使用 Coil 全局单例,避免每次新建 ImageLoader 实例导致清除无效
context.imageLoader.memoryCache?.clear()
context.imageLoader.diskCache?.clear()
}
data class RespData(
var code: String? = null,
var data: String? = null,
var msg: String? = null,
)
data class GoodsImage(
var goodsId: String,
var goodsName: String,
var startTime: Long,
var endTime: Long
)
//
// val goodsImageList = mutableListOf<GoodsImage>().apply {
// add(
// GoodsImage(
// goodsId = "1986353555971297282",
// goodsName = "马铃薯[土豆、洋芋]",
// startTime = 1763692477786L,
// endTime = 1763693153825L
// )
// )
// add(GoodsImage(
// goodsId = "1986607476769869825",
// goodsName = "辣椒(青辣椒,螺丝椒)",
// startTime = 1763693182929L,
// endTime = 1763693621664L
// ))
// add(GoodsImage(
// goodsId = "1987880591453859842",
// goodsName = "辣椒(红,小)",
// startTime = 1763693683837L,
// endTime = 1763694154521L
// ))
// add(GoodsImage(
// goodsId = "1986699063868801025",
// goodsName = "鸡腿肉",
// startTime = 1763694221267L,
// endTime = 1763695052924L
// ))
//
// add(GoodsImage(
// goodsId = "1986689570300809217",
// goodsName = "猪腿肉",
// startTime = 1763695212428L,
// endTime = 1763695810578L
// ))
// add(GoodsImage(
// goodsId = "1986673470079029249",
// goodsName = "西兰花[绿菜花]",
// startTime = 1763695924575L,
// endTime = 1763696252669L
// ))
// add(GoodsImage(
// goodsId = "1991473021965070338",
// goodsName = "姜[黄姜]",
// startTime = 1763696258001L,
// endTime = 1763696294831L
// ))
// add(GoodsImage(
// goodsId = "00000",
// goodsName = "其它",
// startTime = 1763696442577L,
// endTime = 1763696990541L
// ))
// }
// private fun uploadImage(goodsImage: GoodsImage) {
// Loading.show(this)
// val cropFile = File(this.cacheDir, "crop")
// val list = mutableListOf<File>()
// val startTime = goodsImage.startTime
// val endTime = goodsImage.endTime
// cropFile.listFiles()?.forEach {
// if (it.isDirectory) {
// return@forEach
// }
// val start = "IMG_CROP_".length
// val end = it.name.indexOf(".jpg")
// val time = it.name.substring(start, end).toLong()
// if (time in startTime..endTime) {
// list.add(it)
// }
// if (list.size < 5) {
// return@forEach
// }
// viewModel.uploadMultipleImages(
// goodsId = goodsImage.goodsId,
// goodsName = goodsImage.goodsName,
// files = list
// ) { isSuccess ->
//// val resp = data?.toObject<RespData>()
//// val isSuccess = resp?.code == "00000"
// binding.ivFinish.run {
// if (isSuccess) visible() else invisible()
// }
// }
// list.clear()
// }
// if (list.isNotEmpty()) {
// viewModel.uploadMultipleImages(
// goodsId = goodsImage.goodsId,
// goodsName = goodsImage.goodsName,
// files = list
// ) { isSuccess ->
//// val resp = data?.toObject<RespData>()
//// val isSuccess = resp?.code == "00000"
// binding.ivFinish.run {
// if (isSuccess) visible() else invisible()
// }
// }
// }
// Loading.dismiss()
// }
}