1 year ago

#355064

test-img

Waqas Younis

ConcurrentModification Exception thrown when inserting the file via content resolver

I created a suspended function that I am calling from ViewModel, the function takes a set of MyFile abstract class and then iterates through the set to get values and eventually insert them via content resolver. There is no way the set values are being changed. It is immutable set after all. But still somehow, as the execution reaches insert function it throws ConcurrentModificationException NOTE: The first iteration went smooth. It's the second one that causes the crash.

Please can anyone help me with this?

Thank you

suspend fun unhideSelectedFiles(files: Set<MyFile>, address: String? = null): Flow<UIEvent> = flow {

    val context = App.getContext()
    files.forEach { currentFile ->
        Log.i(TAG, "unhideSelectedFiles: currentFile: ${currentFile.name}")

        if (currentFile.currentPath.isBlank()) {
            Log.e(TAG, "unhideSelectedFiles: ${currentFile.name} does not contain a current path")
            return@forEach
        }
        val collection =
            if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.Q){
                MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL)
            }else{
                MediaStore.Images.Media.EXTERNAL_CONTENT_URI
            }

        val contentValues: ContentValues =
                ContentValues().apply {
                    put(MediaStore.Images.Media.DISPLAY_NAME, currentFile.name)
                    put(MediaStore.Images.Media.MIME_TYPE, currentFile.mimeType)
                    put(MediaStore.Images.Media.IS_PENDING, 1)
                    put(
                        MediaStore.Images.Media.RELATIVE_PATH,
                        address ?: currentFile.originalPathWithoutFileName
                    )
                }


        val uri = context.contentResolver.insert(collection, contentValues)
        uri?.let {
            try {
                context.contentResolver.openOutputStream(it)?.use { outputStream ->
                    val buf = ByteArray(COPY_BYTE_SIZE)
                    val inputStream = FileInputStream(File(currentFile.currentPath))

                    var len: Int
                    while (inputStream.read(buf).also { len = it } > 0) {
                        outputStream.write(buf, 0, len)
                    }
                    outputStream.close()
                    inputStream.close()
                    contentValues.clear()
                    contentValues.put(MediaStore.MediaColumns.IS_PENDING, 0)
                    val isUpdated =
                        context.contentResolver.update(it, contentValues, null, null)
                    if (isUpdated > 0) {
                        if (File(currentFile.currentPath).delete()) {
                            emit(UIEvent.FileDone(currentFile))
                            Log.i(
                                TAG,
                                "unhideSelectedFiles: ${currentFile.name} unhidden successfully"
                            )

                        } else {
                            Log.e(
                                TAG,
                                "unhideSelectedFiles: Could not delete the image file from internal storage",
                            )
                            emit(UIEvent.Error("Could not delete ${currentFile.name} from hidden directory"))

                        }

                    } else {
                        Log.e(
                            TAG,
                            "unhideSelectedFiles: something went wrong with the file: ${currentFile.name}",
                        )
                        emit(UIEvent.Error("Something went wrong with the file: ${currentFile.name}"))
                    }

                }
            } catch (e: Exception) {
                Log.e(
                    TAG,
                    "unhideSelectedFiles: file: ${currentFile.name}\n\nException: ${e.localizedMessage}",
                )
                emit(UIEvent.Error("Something went wrong: ${e.localizedMessage}"))
            }
        }
        Log.i(TAG, "unhideSelectedFiles: file ${currentFile.name} task completed")


    }
    emit(UIEvent.Finished())
}

Here is the stack trace:

2022-03-31 06:47:25.806 13886-6547/com.androidbull.incognito.vaultreborn E/AndroidRuntime: FATAL EXCEPTION: DefaultDispatcher-worker-2
    Process: com.androidbull.incognito.vaultreborn, PID: 13886
    java.util.ConcurrentModificationException
        at java.util.LinkedHashMap$LinkedHashIterator.nextNode(LinkedHashMap.java:757)
        at java.util.LinkedHashMap$LinkedKeyIterator.next(LinkedHashMap.java:780)
        at com.androidbull.incognito.vaultreborn.utils.UtilsKt$unhideSelectedFiles$1.invokeSuspend(Utils.kt:302)
        at com.androidbull.incognito.vaultreborn.utils.UtilsKt$unhideSelectedFiles$1.invoke(Unknown Source:8)
        at com.androidbull.incognito.vaultreborn.utils.UtilsKt$unhideSelectedFiles$1.invoke(Unknown Source:4)
        at kotlinx.coroutines.flow.SafeFlow.collectSafely(Builders.kt:61)
        at kotlinx.coroutines.flow.AbstractFlow.collect(Flow.kt:212)
        at com.androidbull.incognito.vaultreborn.viewModels.PhotosViewModel$unhideFiles$1.invokeSuspend(PhotosViewModel.kt:387)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
        at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
        at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)

The function unhideSelectedFiles(..) is being called from a ViewModel just like this:


    fun unhideFiles(selectedImages: Set<MyFile>, address: String? = null) {
        viewModelScope.launch(IO) {
            unhideSelectedFiles(selectedImages, address)
                .collect {
                if (it is UIEvent.FileDone) {
                    repository.deleteImage((it.file as ImageFile).toImageEntity())
                } else {
                    /**
                     * you can show the progress of file by emitting the received flow and catching
                     * it in Fragment and showing dialog/bottomsheet accordingly
                     */

                }
            }
        }
    }

android

kotlin

android-contentprovider

0 Answers

Your Answer

Accepted video resources