优化异常

This commit is contained in:
2026-01-13 19:08:56 +08:00
parent de0b6e994f
commit 8de09cf8c1
5 changed files with 155 additions and 105 deletions
@@ -371,13 +371,9 @@ class FoodCollectionActivity : ComponentActivity() {
// } // }
val goods = searchGoodsList[clickIndex] val goods = searchGoodsList[clickIndex]
val saveName = goods.goodsName + goods.goodsCode val saveName = goods.goodsName + goods.goodsCode
box?.put( ObjectBox.boxStore.runInTx {
Food( box?.put(Food(name = saveName, foodIdx = 0, foodVector = imageVector))
name = saveName, }
foodIdx = 0,
foodVector = imageVector
)
)
collectList[position].let { collectList[position].let {
// it.imageVector = imageVector // it.imageVector = imageVector
it.isFinish = true it.isFinish = true
@@ -158,7 +158,9 @@ class CollectSearchDialog(
} }
val name = list[position].goodsName val name = list[position].goodsName
val filterIdList = box?.all?.filter { it.name == name }?.map { it.id } val filterIdList = box?.all?.filter { it.name == name }?.map { it.id }
box?.removeByIds(filterIdList) ObjectBox.boxStore.runInTx {
box?.removeByIds(filterIdList)
}
activity?.runOnUiThread { activity?.runOnUiThread {
Loading.dismiss() Loading.dismiss()
//list.remove(list[position]) //list.remove(list[position])
@@ -209,24 +211,24 @@ class CollectSearchDialog(
binding.root.hideKeyboard() binding.root.hideKeyboard()
} }
fun test() { // fun test() {
val goodsName = "多宝鱼,黑鱼(整条鱼),鸡腿肉" // val goodsName = "多宝鱼,黑鱼(整条鱼),鸡腿肉"
activity?.searchGoodsInfoList(goodsName = goodsName) { items-> // activity?.searchGoodsInfoList(goodsName = goodsName) { items->
items.forEach { goods -> // items.forEach { goods ->
box?.all?.filter { // box?.all?.filter {
it.name?.contains(goods.goodsName?:"-") == true // it.name?.contains(goods.goodsName?:"-") == true
}?.forEach { // }?.forEach {
activity?.runOnUiThread { // activity?.runOnUiThread {
activity?.toast(goods.goodsName) // activity?.toast(goods.goodsName)
} // }
it.name = goods.goodsName + goods.goodsCode // it.name = goods.goodsName + goods.goodsCode
box?.put(it) // box?.put(it)
} // }
}
// activity?.runOnUiThread {
// activity?.toast("已保存")
// } // }
} //// activity?.runOnUiThread {
} //// activity?.toast("已保存")
//// }
// }
// }
} }
@@ -454,11 +454,11 @@ class GoodsStoreDialog(
Thread { Thread {
try { try {
val imageVector = FoodModule.bitmap2FloatArray(bitmap) val imageVector = FoodModule.bitmap2FloatArray(bitmap)
box?.put( if (imageVector != null) {
Food( ObjectBox.boxStore.runInTx {
name = goods.goodsName + goods.goodsCode, foodIdx = 0, foodVector = imageVector box?.put(Food(name = goods.goodsName + goods.goodsCode, foodIdx = 0, foodVector = imageVector))
) }
) }
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
} }
@@ -3,7 +3,8 @@ package com.sw.inbound.objbox
import android.content.Context import android.content.Context
import android.graphics.Bitmap import android.graphics.Bitmap
import android.net.Uri import android.net.Uri
import android.util.SparseLongArray import android.renderscript.Element.DataType
import androidx.core.graphics.scale
import com.google.gson.Gson import com.google.gson.Gson
import com.google.gson.reflect.TypeToken import com.google.gson.reflect.TypeToken
import com.sw.inbound.MyApp import com.sw.inbound.MyApp
@@ -22,6 +23,7 @@ import java.io.FileOutputStream
import java.io.IOException import java.io.IOException
import java.io.InputStream import java.io.InputStream
object FoodModule { object FoodModule {
private lateinit var module_mobile: Module private lateinit var module_mobile: Module
@@ -31,6 +33,9 @@ object FoodModule {
private lateinit var classInfo: FoodClassInfo private lateinit var classInfo: FoodClassInfo
private val NO_MEAN_RGB = floatArrayOf(0.0f, 0.0f, 0.0f) private val NO_MEAN_RGB = floatArrayOf(0.0f, 0.0f, 0.0f)
private val NO_STD_RGB = floatArrayOf(1.0f, 1.0f, 1.0f) private val NO_STD_RGB = floatArrayOf(1.0f, 1.0f, 1.0f)
// 1. 定义你的模型固定输入尺寸 (根据你的tflite模型修改,比如224x224)
private const val MODEL_INPUT_WIDTH = 300
private const val MODEL_INPUT_HEIGHT = 300
const val DEFAULT_FOOD_INDEX = -1 const val DEFAULT_FOOD_INDEX = -1
const val BAG_RATE = 0.05 const val BAG_RATE = 0.05
@@ -48,49 +53,64 @@ object FoodModule {
}.start() }.start()
} }
fun uri2FloatArray(uri: Uri): FloatArray? { // fun uri2FloatArray(uri: Uri): FloatArray? {
return MyApp.instance?.let { context -> // return MyApp.instance?.let { context ->
ImageUtil.uriToBitmap(context, uri)?.let { // ImageUtil.uriToBitmap(context, uri)?.let {
bitmap2FloatArray(it) // bitmap2FloatArray(it)
// }
// }
// }
fun bitmap2FloatArray(originBitmap: Bitmap): FloatArray? {
var rgb565Bitmap: Bitmap?=null
try {
val scaledBitmap = originBitmap.scale(MODEL_INPUT_WIDTH, MODEL_INPUT_HEIGHT)
originBitmap.recycle()
rgb565Bitmap = scaledBitmap.copy(Bitmap.Config.RGB_565, false)
scaledBitmap.recycle()
val inputTensor = TensorImageUtils.bitmapToFloat32Tensor(
rgb565Bitmap,
NO_MEAN_RGB, // [0.485, 0.456, 0.406] TORCHVISION_NORM_MEAN_RGB
NO_STD_RGB // [0.229, 0.224, 0.225] TORCHVISION_NORM_STD_RGB
)
val outputTensor = module_mobile.forward(IValue.from(inputTensor)).toTensor()
return outputTensor.dataAsFloatArray
} catch (e: OutOfMemoryError) {
e.printStackTrace()
} finally {
if (rgb565Bitmap != null && rgb565Bitmap.isRecycled.not()) {
rgb565Bitmap.recycle()
} }
//System.gc()
//System.runFinalization()
} }
return null
} }
fun bitmap2FloatArray(bitmap: Bitmap): FloatArray { // fun queryFood(uri: Uri, queryCount: Int = 15): List<String>? {
val inputTensor = TensorImageUtils.bitmapToFloat32Tensor( // return uri2FloatArray(uri)?.let {
bitmap, // queryFood(it, queryCount)
NO_MEAN_RGB, // [0.485, 0.456, 0.406] TORCHVISION_NORM_MEAN_RGB // }
NO_STD_RGB // [0.229, 0.224, 0.225] TORCHVISION_NORM_STD_RGB // }
)
val outputTensor = module_mobile.forward(IValue.from(inputTensor)).toTensor()
// return floatArrayOf()
val floatArray = outputTensor.dataAsFloatArray
return floatArray
}
fun queryFood(uri: Uri, queryCount: Int = 15): List<String>? { // /**
return uri2FloatArray(uri)?.let { // * 返回识别物品名称列表
queryFood(it, queryCount) // */
} // fun queryFood(bitmap: Bitmap, queryCount: Int = 15): List<String> {
} // val floatArray = bitmap2FloatArray(bitmap)
// return queryFood(floatArray, queryCount)
// }
/** // /**
* 返回识别物品名称列表 // * 返回识别物品IdNameScore对象列表
*/ // */
fun queryFood(bitmap: Bitmap, queryCount: Int = 15): List<String> { // fun queryFoodNameScore(bitmap: Bitmap, queryCount: Int = 15): List<IdNameScore> {
val floatArray = bitmap2FloatArray(bitmap) // val floatArray = bitmap2FloatArray(bitmap)
return queryFood(floatArray, queryCount) // return queryFoodNameScore(floatArray, queryCount)
} // }
/** fun queryFoodNameScore(floatArray: FloatArray?, queryCount: Int = 15): List<IdNameScore> {
* 返回识别物品IdNameScore对象列表 if (floatArray == null) return emptyList()
*/
fun queryFoodNameScore(bitmap: Bitmap, queryCount: Int = 15): List<IdNameScore> {
val floatArray = bitmap2FloatArray(bitmap)
return queryFoodNameScore(floatArray, queryCount)
}
fun queryFoodNameScore(floatArray: FloatArray, queryCount: Int = 15): List<IdNameScore> {
val query: Query<Food> = val query: Query<Food> =
box.query(Food_.foodVector.nearestNeighbors(floatArray, queryCount)).build() box.query(Food_.foodVector.nearestNeighbors(floatArray, queryCount)).build()
//查询比较分数 //查询比较分数
@@ -106,7 +126,7 @@ object FoodModule {
} }
fun getFoodScoreList(bitmap: Bitmap, queryCount: Int = 15): List<IdNameScore> { fun getFoodScoreList(bitmap: Bitmap, queryCount: Int = 15): List<IdNameScore> {
val floatArray = bitmap2FloatArray(bitmap) val floatArray = bitmap2FloatArray(bitmap)
// return emptyList() if (floatArray == null) return emptyList()
val nameScoreList = queryFoodNameScore(floatArray, queryCount) val nameScoreList = queryFoodNameScore(floatArray, queryCount)
if (nameScoreList.isEmpty()) { if (nameScoreList.isEmpty()) {
return emptyList() return emptyList()
@@ -170,7 +190,9 @@ object FoodModule {
val classIdx = labelsList[index] val classIdx = labelsList[index]
val foodName = foodMap["$classIdx"] val foodName = foodMap["$classIdx"]
val array = floatList.toFloatArray() val array = floatList.toFloatArray()
box.put(Food(name = foodName, foodVector = array, foodIdx = DEFAULT_FOOD_INDEX)) ObjectBox.boxStore.runInTx {
box.put(Food(name = foodName, foodVector = array, foodIdx = DEFAULT_FOOD_INDEX))
}
} }
action() action()
} }
@@ -184,47 +206,75 @@ object FoodModule {
* *
* 不可能反正SD * 不可能反正SD
*/ */
fun copyAssetToCache(context: Context, fileName: String): String? { // fun copyAssetToCache(context: Context, fileName: String): String? {
// 此app的缓存目录 --> 会默认在 cache目录...,可以自己去看看哦 // // 此app的缓存目录 --> 会默认在 cache目录...,可以自己去看看哦
val cacheDir = context.getCacheDir() // val cacheDir = context.getCacheDir()
if (!cacheDir.exists()) { // if (!cacheDir.exists()) {
cacheDir.mkdirs() // TODO 如果没有缓存目录,就创建 // cacheDir.mkdirs() // TODO 如果没有缓存目录,就创建
} // }
val outPath = File(cacheDir, fileName) // TODO 创建输出的文件位置 // val outPath = File(cacheDir, fileName) // TODO 创建输出的文件位置
if (outPath.exists()) { // if (outPath.exists()) {
outPath.delete() // TODO 如果该文件已经存在,就删掉 // outPath.delete() // TODO 如果该文件已经存在,就删掉
} // }
var `is`: InputStream? = null // 读取 // var `is`: InputStream? = null // 读取
var fos: FileOutputStream? = null // 写入 // var fos: FileOutputStream? = null // 写入
try { // try {
// 创建文件,如果创建成功,就返回true // // 创建文件,如果创建成功,就返回true
val res = outPath.createNewFile() // val res = outPath.createNewFile()
if (res) { // if (res) {
`is` = context.getAssets().open(fileName) // 拿到main/assets目录的输入流,用于读取字节 // `is` = context.getAssets().open(fileName) // 拿到main/assets目录的输入流,用于读取字节
fos = FileOutputStream(outPath) // 读取出来的字节最终写到outPath // fos = FileOutputStream(outPath) // 读取出来的字节最终写到outPath
val buf = ByteArray(`is`.available()) // 缓存区 // val buf = ByteArray(`is`.available()) // 缓存区
var byteCount: Int // var byteCount: Int
//
// // 开始循环读取
// while ((`is`.read(buf).also { byteCount = it }) != -1) {
// fos.write(buf, 0, byteCount)
// }
// return outPath.getAbsolutePath()
// }
// } catch (e: IOException) {
// e.printStackTrace()
// } finally {
// try {
// // TODO 一定要记得关闭资源,为了不去性能的磨损
// fos?.flush()
// `is`?.close()
// fos?.close()
// } catch (e: IOException) {
// e.printStackTrace()
// }
// }
// return null
// }
// 开始循环读取 fun copyAssetToCache(context: Context, fileName: String): String? {
while ((`is`.read(buf).also { byteCount = it }) != -1) { val cacheFile = File(context.cacheDir, fileName)
fos.write(buf, 0, byteCount) val buffer = ByteArray(8 * 1024)
} var inputStream: InputStream? = null
return outPath.getAbsolutePath() var outputStream: FileOutputStream? = null
try {
inputStream = context.assets.open(fileName)
outputStream = FileOutputStream(cacheFile)
var byteCount: Int
while (inputStream.read(buffer).also { byteCount = it } != -1) {
outputStream.write(buffer, 0, byteCount)
} }
outputStream.channel.force(true) // 强制物理落盘,比flush更彻底
return cacheFile.absolutePath
} catch (e: IOException) { } catch (e: IOException) {
e.printStackTrace() e.printStackTrace()
} finally { Timber.tag("FoodModule").d("文件拷贝失败 fileName=$fileName, error=${e.message}")
try { // 拷贝失败时删除残缺文件,避免下次读取到损坏文件
// TODO 一定要记得关闭资源,为了不去性能的磨损 if (cacheFile.exists()) {
fos?.flush() cacheFile.delete()
`is`?.close()
fos?.close()
} catch (e: IOException) {
e.printStackTrace()
} }
} finally {
outputStream?.close()
inputStream?.close()
} }
return null return null
} }
} }
@@ -55,13 +55,15 @@ object ObjectBox {
private set private set
fun init(context: Context) { fun init(context: Context) {
// 优化:调大事务回收超时时间
System.setProperty("objectbox.finalizerTimeout", "30000");
// On Android make sure to pass a Context when building the Store. // On Android make sure to pass a Context when building the Store.
boxStore = try { boxStore = try {
MyObjectBox.builder() MyObjectBox.builder()
.androidContext(context.applicationContext) .androidContext(context.applicationContext)
.build() .build()
} catch (e: DbException) { } catch (e: DbException) {
if (e.javaClass.equals(DbException::class.java) || e is FileCorruptException) { if (e.javaClass == DbException::class.java || e is FileCorruptException) {
// Failed to build BoxStore due to database file issue, store message; // Failed to build BoxStore due to database file issue, store message;
// checked in NoteListActivity to notify user. // checked in NoteListActivity to notify user.
dbExceptionMessage = e.toString() dbExceptionMessage = e.toString()