diff --git a/app/src/main/java/com/sw/inbound/objbox/ObjectBox.kt b/app/src/main/java/com/sw/inbound/objbox/ObjectBox.kt index e25d17d..6f78f96 100644 --- a/app/src/main/java/com/sw/inbound/objbox/ObjectBox.kt +++ b/app/src/main/java/com/sw/inbound/objbox/ObjectBox.kt @@ -18,7 +18,6 @@ package com.sw.inbound.objbox import android.annotation.SuppressLint import android.content.Context -import android.os.Environment import android.util.Log import com.sw.inbound.MyApp import com.sw.inbound.utils.LogSaveUtil @@ -32,7 +31,6 @@ import io.objectbox.query.IdWithScore import io.objectbox.query.Query import io.objectbox.sync.Sync import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.withContext @@ -41,13 +39,6 @@ import java.io.FileInputStream import java.io.FileOutputStream import java.io.IOException import java.util.zip.GZIPOutputStream -import kotlin.collections.emptyList -import kotlin.io.copyTo -import kotlin.io.inputStream -import kotlin.io.outputStream -import kotlin.io.use -import kotlin.jvm.java -import kotlin.jvm.javaClass /** * Singleton to keep BoxStore reference and provide current list of Notes Objects. @@ -60,9 +51,10 @@ object ObjectBox { // 避免直接修改自动生成的MyObjectBox类 // 跨版本升级时使用boxStore.runInTx执行数据迁移 - private const val TAG = "ObjectBox" + // 添加 @Volatile 保证多线程下的可见性 + @Volatile var boxStore: BoxStore? = null /** @@ -111,6 +103,7 @@ object ObjectBox { /** * If the database file is not in use, compresses (GZIP) and copies it to the given [target]. + * 增加 IOException 捕获,避免文件异常导致崩溃。 */ fun copyAndGzipDatabaseFileTo(target: File, context: Context): Boolean { if (BoxStore.isDatabaseOpen(context, null)) { @@ -123,13 +116,18 @@ object ObjectBox { // If a name was given when building BoxStore use that instead of the default below. val dbName = BoxStoreBuilder.DEFAULT_NAME - File(context.filesDir, "objectbox/$dbName/data.mdb").inputStream().use { input -> - target.parentFile?.mkdirs() - GZIPOutputStream(target.outputStream()).use { output -> - input.copyTo(output, DEFAULT_BUFFER_SIZE) + return try { + File(context.filesDir, "objectbox/$dbName/data.mdb").inputStream().use { input -> + target.parentFile?.mkdirs() + GZIPOutputStream(target.outputStream()).use { output -> + input.copyTo(output, DEFAULT_BUFFER_SIZE) + } } + true + } catch (e: IOException) { + Log.e(TAG, "备份数据库文件失败", e) + false } - return true } @@ -145,9 +143,9 @@ object ObjectBox { // 操作中触发损坏,尝试重建数据库 e.printStackTrace() LogSaveUtil.saveLogFile("safeDbOp出现FileCorruptException异常:${e.message},${e.errorCode}") - //val context = boxStore.context - //deleteDbFiles(context) - init(MyApp.instance!!) + // 使用安全调用替代 !! 断言,避免 NPE + val app = MyApp.instance ?: return@withContext null + init(app) null } catch (e: Exception) { e.printStackTrace() @@ -183,36 +181,35 @@ object ObjectBox { box?.put(entities) } + // 使用 QueryBuilder 在数据库层计数,避免全量加载到内存 suspend fun count(name: String) = safeDbOp { - getBox()?.all?.count() { it.name == name } - } ?: 0 + getBox()?.query(Food_.name.equal(name))?.build()?.count() + } ?: 0L + // 使用 QueryBuilder 在数据库层过滤,避免全量加载到内存 suspend fun filter(name: String?) = safeDbOp { - val list = getBox()?.all?.distinctBy { it.name } - if (name.isNullOrBlank().not()) { - list?.filter { it.name?.contains(name) == true } + if (!name.isNullOrBlank()) { + getBox()?.query(Food_.name.contains(name))?.build()?.find() + ?.distinctBy { it.name } } else { - list + getBox()?.all?.distinctBy { it.name } } ?: emptyList() } + // 使用 QueryBuilder 在数据库层删除,避免全量加载到内存 suspend fun remove(name: String) = safeDbOp { - getBox()?.run { - val filterIdList = all.filter { it.name == name }.map { it.id } - removeByIds(filterIdList) - } + getBox()?.query(Food_.name.equal(name))?.build()?.remove() } private val dbMutex = Mutex() // 协程并发锁,保证写入操作原子性 private const val DB_DIR_NAME = "objectbox" // ObjectBox 默认数据库目录 private const val BACKUP_DIR_NAME = "objectbox_backup" // 备份目录 - // 检查存储是否可读写(操作数据库前调用) + // 检查内部存储是否可读写(操作数据库前调用) + // 修复:原逻辑错误地检查外部存储状态,数据库存于内部存储,与外部存储无关 fun isStorageAvailable(context: Context): Boolean { return try { - val state = Environment.getExternalStorageState() - val innerDir = context.filesDir - Environment.MEDIA_MOUNTED == state && innerDir.canRead() && innerDir.canWrite() + context.filesDir.canRead() && context.filesDir.canWrite() } catch (e: Exception) { false } @@ -290,5 +287,4 @@ object ObjectBox { boxStore = null } - -} \ No newline at end of file +} diff --git a/app/src/main/java/com/sw/inbound/utils/CameraUtils.kt b/app/src/main/java/com/sw/inbound/utils/CameraUtils.kt index d1b574c..7d004e7 100644 --- a/app/src/main/java/com/sw/inbound/utils/CameraUtils.kt +++ b/app/src/main/java/com/sw/inbound/utils/CameraUtils.kt @@ -37,7 +37,6 @@ class CameraUtils(private var activity: ComponentActivity) { photoCaptureHelper = PhotoCaptureHelper( context = activity, cameraController = cameraController!!, - onSuccess = {}, onError = { msg -> //toast(msg) failCallback?.invoke() diff --git a/app/src/main/java/com/sw/inbound/utils/PhotoCaptureHelper.kt b/app/src/main/java/com/sw/inbound/utils/PhotoCaptureHelper.kt index 09c9806..5956a8b 100644 --- a/app/src/main/java/com/sw/inbound/utils/PhotoCaptureHelper.kt +++ b/app/src/main/java/com/sw/inbound/utils/PhotoCaptureHelper.kt @@ -16,13 +16,11 @@ import java.io.File * 拍照工具类 * @param context Context 上下文 * @param cameraController CameraController 相机控制器 - * @param onSuccess (Uri) -> Unit 拍照成功回调 * @param onError (String) -> Unit 拍照失败回调 */ class PhotoCaptureHelper( private val context: Context, private val cameraController: CameraController, - private val onSuccess: (Uri) -> Unit = {}, private val onError: (String) -> Unit = {} ) { private val callbackList: MutableList<(Uri) -> Unit> = mutableListOf() @@ -66,7 +64,6 @@ class PhotoCaptureHelper( override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) { val photoUri = outputFileResults.savedUri ?: Uri.fromFile(photoFile) Timber.d("照片保存成功: $photoUri") - onSuccess(photoUri) callbackList.forEach { it(photoUri) }