1 year ago
#355064
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