[Android] Storages adapter: prevent desync

Geoffrey Métais git at videolan.org
Thu Oct 17 14:42:18 CEST 2019


vlc-android | branch: 3.2.x | Geoffrey Métais <geoffrey.metais at gmail.com> | Thu Oct 17 14:40:50 2019 +0200| [b262da32d9d1fd9f5ab007948a71d4bab6637744] | committer: Geoffrey Métais

Storages adapter: prevent desync

coroutines dispatching could cause race conditions

(cherry picked from commit 3acf91bed62e60c8dac0aba34d307baa7f52e361)

> https://code.videolan.org/videolan/vlc-android/commit/b262da32d9d1fd9f5ab007948a71d4bab6637744
---

 .../videolan/vlc/gui/browser/BaseBrowserAdapter.kt |  7 +++---
 .../vlc/gui/browser/StorageBrowserAdapter.kt       | 28 ++++++++++++----------
 2 files changed, 20 insertions(+), 15 deletions(-)

diff --git a/vlc-android/src/org/videolan/vlc/gui/browser/BaseBrowserAdapter.kt b/vlc-android/src/org/videolan/vlc/gui/browser/BaseBrowserAdapter.kt
index 324cee4ac..d36d5ce53 100644
--- a/vlc-android/src/org/videolan/vlc/gui/browser/BaseBrowserAdapter.kt
+++ b/vlc-android/src/org/videolan/vlc/gui/browser/BaseBrowserAdapter.kt
@@ -34,6 +34,7 @@ import android.widget.TextView
 import androidx.databinding.ViewDataBinding
 import androidx.recyclerview.widget.RecyclerView
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.Job
 import kotlinx.coroutines.ObsoleteCoroutinesApi
 import org.videolan.libvlc.util.AndroidUtil
 import org.videolan.medialibrary.interfaces.media.AbstractMediaWrapper
@@ -52,6 +53,7 @@ import org.videolan.vlc.gui.helpers.SelectorViewHolder
 import org.videolan.vlc.gui.helpers.enableMarqueeEffect
 import org.videolan.vlc.util.AndroidDevices
 import org.videolan.vlc.util.Settings
+import org.videolan.vlc.util.Settings.init
 import org.videolan.vlc.util.UPDATE_SELECTION
 import java.util.*
 
@@ -174,9 +176,7 @@ open class BaseBrowserAdapter() : DiffUtilAdapter<MediaLibraryItem, BaseBrowserA
 
         open fun onImageClick(v: View) {}
 
-        open fun onLongClick(v: View): Boolean {
-            return false
-        }
+        open fun onLongClick(v: View) = false
 
         open fun onCheckBoxClick(v: View) {}
 
@@ -187,6 +187,7 @@ open class BaseBrowserAdapter() : DiffUtilAdapter<MediaLibraryItem, BaseBrowserA
     @TargetApi(Build.VERSION_CODES.M)
     internal inner class MediaViewHolder(binding: BrowserItemBinding) : ViewHolder<BrowserItemBinding>(binding), View.OnFocusChangeListener, MarqueeViewHolder {
         override val titleView: TextView? = binding.title
+        var job : Job? = null
 
         init {
             binding.holder = this
diff --git a/vlc-android/src/org/videolan/vlc/gui/browser/StorageBrowserAdapter.kt b/vlc-android/src/org/videolan/vlc/gui/browser/StorageBrowserAdapter.kt
index f35db498b..01b728181 100644
--- a/vlc-android/src/org/videolan/vlc/gui/browser/StorageBrowserAdapter.kt
+++ b/vlc-android/src/org/videolan/vlc/gui/browser/StorageBrowserAdapter.kt
@@ -27,16 +27,12 @@ import android.content.Context
 import android.net.Uri
 import android.view.View
 import androidx.databinding.ViewDataBinding
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.ObsoleteCoroutinesApi
-import kotlinx.coroutines.launch
+import kotlinx.coroutines.*
 import org.videolan.medialibrary.interfaces.AbstractMedialibrary
 import org.videolan.medialibrary.interfaces.media.AbstractMediaWrapper
 import org.videolan.medialibrary.media.MediaLibraryItem
 import org.videolan.medialibrary.media.Storage
 import org.videolan.vlc.MediaParsingService
-import org.videolan.vlc.VLCApplication
 import org.videolan.vlc.gui.helpers.ThreeStatesCheckbox
 import org.videolan.vlc.repository.DirectoryRepository
 import org.videolan.vlc.util.containsPath
@@ -47,22 +43,21 @@ internal class StorageBrowserAdapter(fragment: StorageBrowserFragment) : BaseBro
 
     private var mediaDirsLocation: MutableList<String> = mutableListOf()
     private lateinit var customDirsLocation: List<String>
-    private var job : Job? = null
+    private var updateJob : Job? = null
 
     init {
         updateMediaDirs(fragment.requireContext())
     }
 
     override fun onBindViewHolder(holder: ViewHolder<ViewDataBinding>, position: Int) {
-        launch {
-            val vh = holder as BaseBrowserAdapter.MediaViewHolder
+        val vh = holder as MediaViewHolder
+        vh.job = launch(start = CoroutineStart.UNDISPATCHED) {
             var storage = getItem(position)
-
             if (storage.itemType == MediaLibraryItem.TYPE_MEDIA) storage = Storage((storage as AbstractMediaWrapper).uri)
             var storagePath = (storage as Storage).uri.path ?: ""
             if (!storagePath.endsWith("/")) storagePath += "/"
             vh.binding.item = storage
-            job?.join()
+            updateJob?.join()
             val hasContextMenu = customDirsLocation.contains(storagePath)
             val checked = (fragment as StorageBrowserFragment).scannedDirectory || mediaDirsLocation.containsPath(storagePath)
             vh.binding.hasContextMenu = hasContextMenu
@@ -72,7 +67,16 @@ internal class StorageBrowserAdapter(fragment: StorageBrowserFragment) : BaseBro
                 else -> vh.binding.browserCheckbox.state = ThreeStatesCheckbox.STATE_UNCHECKED
             }
             vh.binding.checkEnabled = !(fragment as StorageBrowserFragment).scannedDirectory
+            vh.job = null
+        }
+    }
+
+    override fun onViewRecycled(holder: ViewHolder<ViewDataBinding>) {
+        (holder as MediaViewHolder).apply {
+            job?.cancel()
+            job = null
         }
+        super.onViewRecycled(holder)
     }
 
     private fun hasDiscoveredChildren(path: String): Boolean {
@@ -82,7 +86,7 @@ internal class StorageBrowserAdapter(fragment: StorageBrowserFragment) : BaseBro
 
     suspend fun updateListState(context: Context) {
         updateMediaDirs(context)
-        job?.join()
+        updateJob?.join()
         notifyItemRangeChanged(0, itemCount)
     }
 
@@ -101,7 +105,7 @@ internal class StorageBrowserAdapter(fragment: StorageBrowserFragment) : BaseBro
             mediaDirsLocation.add(Uri.decode(if (it.startsWith("file://")) it.substring(7) else it))
         }
 
-        job = launch {
+        updateJob = launch {
             customDirsLocation = DirectoryRepository.getInstance(context).getCustomDirectories().map { it.path }
         }
     }



More information about the Android mailing list