我的 kotlin 应用程序因 ML 图像处理(文本识别)而崩溃

My kotlin app crashes because of ML image processing (text recognition)

我正在编写一个用于在 android 智能手机上搜索模因的应用程序。我已经有了从存储中获取所有图像并将它们显示在屏幕上的代码,它工作得很好。现在,我正在尝试使用 google ML 文本识别处理每张获取的图像,并将该文本添加到存储在数组列表中的图像对象(每次处理新图像时,我都会将该列表保存在 sharedpreferences 中)。通常,我的代码可以工作,但不知何故,当有大量图像要处理时,应用程序会崩溃。我从日志中几乎看不到任何东西,没有错误。我有一次并发修改错误,但是当我添加newList时,它不再显示了。

但是,我可以再次启动该应用程序,它会继续加载图像,直到下一次崩溃。好像处理了几百张图片就崩溃了

这是从设备存储中获取和处理图像的完整函数(抱歉代码乱七八糟,我还不是专业人士):

//Function for fetching all the data and creating Image objects, which are returned in a list.
//I will pass them to the main activity and show on the screen
@SuppressLint("SetTextI18n")
@RequiresApi(Build.VERSION_CODES.Q)
private fun queryImageStorage() {
        var imageNumber = 0
        var percentageloaded = 0
        val list = readListFromPref(this, R.string.preference_file_key.toString()).toList()
        Log.d(TAG, "queryImageStorage: ROZMIAR ${list.size}")

        val newList = readListFromPref(this, R.string.preference_file_key.toString())

        val imageProjection = arrayOf(
            MediaStore.Images.Media.DISPLAY_NAME,
            MediaStore.Images.Media.SIZE,
            MediaStore.Images.Media.DATE_TAKEN,
            MediaStore.Images.Media._ID,
        )
        val imageSortOrder = "${MediaStore.Images.Media.DATE_TAKEN} DESC"

        val cursor = contentResolver.query(
            MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
            imageProjection,
            null,
            null,
            imageSortOrder
        )
        cursor.use { it ->
            val imagesAmount = cursor?.count
            //Log.d(TAG, "queryImageStorage: $x")
            it?.let { it ->
                val idColumn = it.getColumnIndexOrThrow(MediaStore.Images.Media._ID)
                val nameColumn = it.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME)
                val sizeColumn = it.getColumnIndexOrThrow(MediaStore.Images.Media.SIZE)
                val dateColumn = it.getColumnIndexOrThrow(MediaStore.Images.Media.DATE_TAKEN)

                while (it.moveToNext()) {

                    val id = it.getLong(idColumn)
                    val name = it.getString(nameColumn)
                    val size = it.getString(sizeColumn)
                    val date = it.getString(dateColumn)

                    val contentUri = ContentUris.withAppendedId(
                        MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                        id
                    ).toString()

                    // make image object and add to list if it's not there already.
                    //TODO: Dodac skanowanie obrazów z unused stuff
                    if (!list.any { Image -> Image.id == id }) {

                        //process the image
                        val inputImage =
                            contentUri.let { InputImage.fromFilePath(this, it.toUri()) }
                        val recognizer = TextRecognition.getClient(TextRecognizerOptions.DEFAULT_OPTIONS)
                        val result = recognizer.process(inputImage)
                            result.addOnSuccessListener { visionText ->
                                // Task completed successfully
                                if (visionText.toString().isNotEmpty()){
                                    val image = Image(id, name, size, date, contentUri, visionText.text)
                                    newList.add(image)
                                    Log.d(TAG, "queryImageStorage: SUCCESS ${visionText.text}")
                                    Log.d(TAG, "queryImageStorage: NEW LIST $newList")
                                    writeListToPref(this, newList, R.string.preference_file_key.toString())
                                }
                            }
                            result.addOnFailureListener { e ->
                                Log.d(TAG, "queryImageStorage: FAILURE $e")
                                // Task failed with an exception
                            }
                    }

                    imageNumber += 1

                    //Updating textview with percentage
                    if (imagesAmount != null) {
                        percentageloaded = (imageNumber *100/ imagesAmount)
                        //Log.d(TAG, "queryImageStorage: PROCENTY $imageNumber $imagesAmount")
                    }
                    Handler(Looper.getMainLooper()).post(Runnable {
                        loadingTextView.text = "Loading images: $percentageloaded %"
                    })
                    try {
                        Thread.sleep(0,1)
                    } catch (e: InterruptedException) {
                        e.printStackTrace()
                    }
                    //Log.d(TAG, "queryImageStorage: $imageNumber")
                    //TODO: Działa sharedpreferences. Przy 1 uruchomienu laduje wszystko, przy kolejnych tylko nowe zdjecia. Do zrobienia Listener zeby działało płynnie.
                }

            }
        } ?: kotlin.run {
            Log.e("TAG", "Cursor is null!")
        }
        val intent = Intent(applicationContext, MainActivity::class.java)
        startActivity(intent)
        finish()
    }

当我删除负责处理图像的代码并创建没有文本的图像对象时,应用程序运行正常。

图像数据class:

data class Image(val id: Long, val name: String?, val size: String?, val date: String?, val uri: String?, val text: String? = null) {

}

感谢您提供的所有提示和帮助。毕竟,我可以在它崩溃时自动重启我的应用程序,但我认为这不是一个好的解决方案:)

编辑:实际上,应用程序并没有崩溃,它只是冻结了一秒钟并且正在最小化。当我再次打开它时,应用程序继续加载图片。可能是某种内存问题?

调用 val result = recognizer.process(inputImage) 是一个异步调用。如果将太多图片放入队列中,可能会导致内存问题。

您可以启动工作线程并在发送新图像之前等待工作线程中的结果。使用 Tasks.await(task)