From 7563209cf130a79077431ae4c5d2e852c50301d2 Mon Sep 17 00:00:00 2001 From: lvmeng <848755140@qq.com> Date: Wed, 11 Mar 2026 18:04:21 +0800 Subject: [PATCH] =?UTF-8?q?refactor(activity):=20=E4=BC=98=E5=8C=96=20Food?= =?UTF-8?q?CollectionActivity=20=E6=80=A7=E8=83=BD=E4=B8=8E=E7=A8=B3?= =?UTF-8?q?=E5=AE=9A=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修复 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 --- .../activity/FoodCollectionActivity.kt | 474 ++++++------------ 1 file changed, 163 insertions(+), 311 deletions(-) diff --git a/app/src/main/java/com/sw/inbound/activity/FoodCollectionActivity.kt b/app/src/main/java/com/sw/inbound/activity/FoodCollectionActivity.kt index 7c99b35..8657474 100644 --- a/app/src/main/java/com/sw/inbound/activity/FoodCollectionActivity.kt +++ b/app/src/main/java/com/sw/inbound/activity/FoodCollectionActivity.kt @@ -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? = null - private val collectList: MutableList = mutableListOf() + private val collectList: MutableList = 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 - ImageUtil.decodeFromFile(file)?.let { bitmap -> - val imageVector = FoodModule.bitmap2FloatArray(bitmap, false) - Timber.tag(TAG).d("向量:${imageVector.toJsonString()}") + // 磁盘读取和 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 { } } - binding.tvTitle.setOnClickListener { -// startActivity { } - -// 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() { @@ -240,11 +215,13 @@ class FoodCollectionActivity : BaseActivity() { confirmBlock = { upload() } - ) .show() + ).show() } private var pageNo = 1 private var clickIndex = -1 + // 保存当前搜索任务,用于在新搜索发起时取消上一次未完成的请求 + private var searchJob: Job? = null private val searchGoodsList: MutableList = 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,68 +303,84 @@ 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 { + } + val imageVector = try { + // TFLite 推理为 CPU 密集型,切换到 IO 线程执行 + withContext(Dispatchers.IO) { FoodModule.bitmap2FloatArray(cropBitmap, false) + } + } catch (e: Exception) { + e.printStackTrace() + hideWaitingDialog() + toast("操作失败") + return + } + Timber.tag(TAG).d("向量:${imageVector.toJsonString()}") + val foodList = try { + // 数据库查询切换到 IO 线程 + withContext(Dispatchers.IO) { + FoodModule.queryFoodNameScore(imageVector) + } + } catch (e: DbException) { + hideWaitingDialog() + try { + // ObjectBox 重新初始化为磁盘 I/O,切换到 IO 线程 + withContext(Dispatchers.IO) { + ObjectBox.init(this@FoodCollectionActivity) + } } catch (e: Exception) { e.printStackTrace() - hideWaitingDialog() - toast("操作失败") - return@let } - Timber.tag(TAG).d("向量:${imageVector.toJsonString()}") - val foodList = try { - FoodModule.queryFoodNameScore(imageVector) - } catch (e: DbException) { - try { - ObjectBox.init(this) - } catch (e: Exception) { - e.printStackTrace() - } - toast("数据库异常,请稍后重试") - return@let - } - val filterList = - foodList.filter { it.score < FoodModule.BAG_RATE && it.name.contains("黑袋子") } - if (filterList.isNotEmpty()) { - hideWaitingDialog() - toast("当前物品可能被袋子遮挡,请检查后重试") - return@let - } - val file = BitmapSaver.saveToAppFilesDir( - cropBitmap, this, "IMG_CROP_${System.currentTimeMillis()}.jpg" - ) - Timber.d("${this.javaClass.simpleName}-searchFood-裁剪bitmap保存文件路径:${file?.absolutePath}") - collectList[index].let { - it.imageVector = imageVector - it.bitmap = null - it.imageFile = file - it.isShowCamera = false - it.isFinish = false - 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() + toast("数据库异常,请稍后重试") + return } + val filterList = + foodList.filter { it.score < FoodModule.BAG_RATE && it.name.contains("黑袋子") } + if (filterList.isNotEmpty()) { + hideWaitingDialog() + toast("当前物品可能被袋子遮挡,请检查后重试") + return + } + // 图片保存为磁盘 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 + it.bitmap = null + it.imageFile = file + it.isShowCamera = false + it.isFinish = false + it.uploadSuccess = false + } + collectionAdapter.notifyItemChanged(index) + 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.put(Food(name = saveName, foodIdx = 0, foodVector = imageVector)) - collectList[position].let { -// it.imageVector = imageVector - it.isFinish = true + // ObjectBox 写入为磁盘 I/O,切换到 IO 线程避免主线程卡顿 + withContext(Dispatchers.IO) { + ObjectBox.put(Food(name = saveName, foodIdx = 0, foodVector = imageVector)) } - 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) -> Unit ) { - runBlocking { + // 取消上一次未完成的搜索,避免并发竞争导致结果乱序 + searchJob?.cancel() + searchJob = lifecycleScope.launch { val records = viewModel.searchGoodsInfoList2( goodsName = searchName, pageNo = pageNo, pageSize = pageSize ) - runOnUiThread { - callback(records) - } + 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() - 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 { - showWaitingDialog2("图片上传中$count/$totalFileCount") - batch.forEach { - it.uploadSuccess = true - } + // lifecycleScope 默认在主线程执行,无需 runOnUiThread + showWaitingDialog2("图片上传中$count/$totalFileCount") + batch.forEach { + it.uploadSuccess = true } }, onError = { - runOnUiThread { - hideWaitingDialog() - toast("上传失败,请稍后重试") - } + // 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) -> Unit) { - runBlocking { + // 使用协程异步执行,避免阻塞主线程 + lifecycleScope.launch { val list = searchGoodsInfoList2(goodsName) block(list) } @@ -610,25 +572,35 @@ class FoodCollectionActivity : BaseActivity() { pageNo: Int = 1, pageSize: Int = 10 ): List { - 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(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(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(R.id.tvTip)?.text = tip } - val contentView = mDialogWaiting?.findViewById(android.R.id.content) - val tvTip = contentView?.findViewById(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().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() -// 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() -//// 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() -//// val isSuccess = resp?.code == "00000" -// binding.ivFinish.run { -// if (isSuccess) visible() else invisible() -// } -// } -// } -// Loading.dismiss() -// } - } \ No newline at end of file