[Android] Medialibrary loading: offload paging threadpool
Geoffrey Métais
git at videolan.org
Wed Nov 20 13:58:59 CET 2019
vlc-android | branch: master | Geoffrey Métais <geoffrey.metais at gmail.com> | Wed Nov 20 12:01:36 2019 +0100| [d1991f50b01faf3731d2deae25e516a35a6e56f7] | committer: Nicolas Pomepuy
Medialibrary loading: offload paging threadpool
Thread medialibrary calls from paging library to prevent a mutex
blocking main thread
Fix #1089
> https://code.videolan.org/videolan/vlc-android/commit/d1991f50b01faf3731d2deae25e516a35a6e56f7
---
.../src/org/videolan/vlc/gui/PlaylistActivity.kt | 9 ++-------
.../vlc/gui/audio/AudioAlbumsSongsFragment.kt | 14 ++++----------
.../videolan/vlc/gui/audio/AudioBrowserFragment.kt | 19 +++++++++++--------
.../org/videolan/vlc/gui/video/VideoGridFragment.kt | 7 ++++---
.../providers/medialibrary/MedialibraryProvider.kt | 10 +++++++---
5 files changed, 28 insertions(+), 31 deletions(-)
diff --git a/vlc-android/src/org/videolan/vlc/gui/PlaylistActivity.kt b/vlc-android/src/org/videolan/vlc/gui/PlaylistActivity.kt
index b044ab9ef..87e70dd7e 100644
--- a/vlc-android/src/org/videolan/vlc/gui/PlaylistActivity.kt
+++ b/vlc-android/src/org/videolan/vlc/gui/PlaylistActivity.kt
@@ -111,13 +111,8 @@ open class PlaylistActivity : AudioPlayerContainerActivity(), IEventsHandler, IL
binding.playlist = playlist
viewModel = getViewModel(playlist)
viewModel.tracksProvider.pagedList.observe(this, Observer { tracks ->
- if (tracks != null) {
- @Suppress("UNCHECKED_CAST")
- if (tracks.isEmpty() && !viewModel.isFiltering())
- finish()
- else
- audioBrowserAdapter.submitList(tracks as PagedList<MediaLibraryItem>?)
- }
+ @Suppress("UNCHECKED_CAST")
+ (tracks as? PagedList<MediaLibraryItem>)?.let { audioBrowserAdapter.submitList(it) }
})
audioBrowserAdapter = AudioBrowserAdapter(MediaLibraryItem.TYPE_MEDIA, this, this, isPlaylist)
if (isPlaylist) {
diff --git a/vlc-android/src/org/videolan/vlc/gui/audio/AudioAlbumsSongsFragment.kt b/vlc-android/src/org/videolan/vlc/gui/audio/AudioAlbumsSongsFragment.kt
index d602f2b09..00862e801 100644
--- a/vlc-android/src/org/videolan/vlc/gui/audio/AudioAlbumsSongsFragment.kt
+++ b/vlc-android/src/org/videolan/vlc/gui/audio/AudioAlbumsSongsFragment.kt
@@ -138,16 +138,10 @@ class AudioAlbumsSongsFragment : BaseAudioBrowser<AlbumSongsViewModel>(), SwipeR
}
fabPlay?.setImageResource(R.drawable.ic_fab_play)
- viewModel.albumsProvider.pagedList.observe(this, Observer { albums -> if (albums != null) albumsAdapter.submitList(albums as PagedList<MediaLibraryItem>) })
- viewModel.tracksProvider.pagedList.observe(this, Observer { tracks ->
- if (tracks != null) {
- @Suppress("UNCHECKED_CAST")
- if (tracks.isEmpty() && !viewModel.isFiltering()) {
- val activity = activity
- activity?.finish()
- } else
- songsAdapter.submitList(tracks as PagedList<MediaLibraryItem>)
- }
+ viewModel.albumsProvider.pagedList.observe(requireActivity(), Observer { albums -> if (albums != null) albumsAdapter.submitList(albums as PagedList<MediaLibraryItem>) })
+ viewModel.tracksProvider.pagedList.observe(requireActivity(), Observer { tracks ->
+ @Suppress("UNCHECKED_CAST")
+ (tracks as? PagedList<MediaLibraryItem>)?.let { songsAdapter.submitList(it) }
})
for (i in 0..1) setupLayoutManager(i)
}
diff --git a/vlc-android/src/org/videolan/vlc/gui/audio/AudioBrowserFragment.kt b/vlc-android/src/org/videolan/vlc/gui/audio/AudioBrowserFragment.kt
index 8b8c5b511..82ba9d818 100644
--- a/vlc-android/src/org/videolan/vlc/gui/audio/AudioBrowserFragment.kt
+++ b/vlc-android/src/org/videolan/vlc/gui/audio/AudioBrowserFragment.kt
@@ -40,6 +40,7 @@ import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.google.android.material.tabs.TabLayout
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.ObsoleteCoroutinesApi
+import kotlinx.coroutines.launch
import org.videolan.medialibrary.interfaces.media.AbstractMediaWrapper
import org.videolan.medialibrary.media.MediaLibraryItem
import org.videolan.vlc.R
@@ -220,7 +221,7 @@ class AudioBrowserFragment : BaseAudioBrowser<AudioBrowserViewModel>(), SwipeRef
handler.sendEmptyMessageDelayed(SET_REFRESHING, 300)
else
handler.sendEmptyMessage(UNSET_REFRESHING)
-
+ updateEmptyView()
(activity as? MainActivity)?.refreshing = loading
})
}
@@ -298,7 +299,7 @@ class AudioBrowserFragment : BaseAudioBrowser<AudioBrowserViewModel>(), SwipeRef
}
private fun updateEmptyView() {
- emptyView.state = if (getCurrentAdapter().isEmpty) EmptyLoadingState.EMPTY else EmptyLoadingState.NONE
+ emptyView.state = if (viewModel.isEmpty()) EmptyLoadingState.EMPTY else EmptyLoadingState.NONE
setFabPlayShuffleAllVisibility()
}
@@ -361,12 +362,14 @@ class AudioBrowserFragment : BaseAudioBrowser<AudioBrowserViewModel>(), SwipeRef
override fun onUpdateFinished(adapter: RecyclerView.Adapter<*>) {
super.onUpdateFinished(adapter)
- if (adapter === getCurrentAdapter()) {
- swipeRefreshLayout.isEnabled = (getCurrentRV().layoutManager as LinearLayoutManager).findFirstVisibleItemPosition() <= 0
- updateEmptyView()
- fastScroller.setRecyclerView(getCurrentRV(), viewModel.providers[currentTab])
- } else
- setFabPlayShuffleAllVisibility()
+ launch { // force a dispatch
+ if (adapter === getCurrentAdapter()) {
+ swipeRefreshLayout.isEnabled = (getCurrentRV().layoutManager as LinearLayoutManager).findFirstVisibleItemPosition() <= 0
+ updateEmptyView()
+ fastScroller.setRecyclerView(getCurrentRV(), viewModel.providers[currentTab])
+ } else
+ setFabPlayShuffleAllVisibility()
+ }
}
override fun getCurrentRV() = lists[currentTab]
diff --git a/vlc-android/src/org/videolan/vlc/gui/video/VideoGridFragment.kt b/vlc-android/src/org/videolan/vlc/gui/video/VideoGridFragment.kt
index ea5c7b80d..134d26319 100644
--- a/vlc-android/src/org/videolan/vlc/gui/video/VideoGridFragment.kt
+++ b/vlc-android/src/org/videolan/vlc/gui/video/VideoGridFragment.kt
@@ -36,6 +36,7 @@ import androidx.paging.PagedList
import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import kotlinx.coroutines.*
+import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.actor
import org.videolan.medialibrary.interfaces.AbstractMedialibrary
import org.videolan.medialibrary.interfaces.media.AbstractFolder
@@ -140,7 +141,7 @@ class VideoGridFragment : MediaBrowserFragment<VideosViewModel>(), SwipeRefreshL
if (loading) handler.sendEmptyMessageDelayed(SET_REFRESHING, 300L)
else handler.sendEmptyMessage(UNSET_REFRESHING)
(activity as? MainActivity)?.refreshing = loading
-
+ updateEmptyView()
})
videoListAdapter.showFilename.set(viewModel.groupingType == VideoGroupingType.NONE && viewModel.provider.sort == AbstractMedialibrary.SORT_FILENAME)
}
@@ -450,7 +451,7 @@ class VideoGridFragment : MediaBrowserFragment<VideosViewModel>(), SwipeRefreshL
}
}
- private val actor = actor<VideoAction> {
+ private val actor = actor<VideoAction>(capacity = Channel.UNLIMITED) {
for (action in channel) when (action) {
is VideoClick -> {
when (action.item) {
@@ -495,8 +496,8 @@ class VideoGridFragment : MediaBrowserFragment<VideosViewModel>(), SwipeRefreshL
}
is VideoUpdateFinished -> {
if (isStarted()) {
+ yield() //Let the refresh actually happen
if (!mediaLibrary.isWorking) handler.sendEmptyMessage(UNSET_REFRESHING)
- updateEmptyView()
setFabPlayVisibility(true)
menu?.let { UiTools.updateSortTitles(it, viewModel.provider) }
}
diff --git a/vlc-android/src/org/videolan/vlc/providers/medialibrary/MedialibraryProvider.kt b/vlc-android/src/org/videolan/vlc/providers/medialibrary/MedialibraryProvider.kt
index 500f64c1b..3efaeceab 100644
--- a/vlc-android/src/org/videolan/vlc/providers/medialibrary/MedialibraryProvider.kt
+++ b/vlc-android/src/org/videolan/vlc/providers/medialibrary/MedialibraryProvider.kt
@@ -30,6 +30,7 @@ import androidx.paging.toLiveData
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
import org.videolan.medialibrary.interfaces.AbstractMedialibrary
import org.videolan.medialibrary.media.MediaLibraryItem
import org.videolan.vlc.providers.HeaderProvider
@@ -116,8 +117,8 @@ abstract class MedialibraryProvider<T : MediaLibraryItem>(val context: Context,
override fun loadInitial(params: LoadInitialParams, callback: LoadInitialCallback<T>) {
model.viewModelScope.launch(Dispatchers.Unconfined) {
retry(1) {
- val page = getPage(params.requestedLoadSize, params.requestedStartPosition)
- val count = if (page.size < params.requestedLoadSize) page.size else getTotalCount()
+ val page = withContext(Dispatchers.IO) { getPage(params.requestedLoadSize, params.requestedStartPosition) }
+ val count = if (page.size < params.requestedLoadSize) page.size else withContext(Dispatchers.IO) { getTotalCount() }
try {
callback.onResult(page.toList(), params.requestedStartPosition, count)
true
@@ -130,7 +131,10 @@ abstract class MedialibraryProvider<T : MediaLibraryItem>(val context: Context,
}
override fun loadRange(params: LoadRangeParams, callback: LoadRangeCallback<T>) {
- callback.onResult(getPage(params.loadSize, params.startPosition).toList())
+ model.viewModelScope.launch {
+ val result = withContext(Dispatchers.IO) { getPage(params.loadSize, params.startPosition).toList() }
+ callback.onResult(result)
+ }
}
}
More information about the Android
mailing list