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:
@@ -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()
|
||||
// }
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user