fix(androidauto): async handling of browseTree init instead of busy‑loop

Removed blocking `while (!browseTree.isInitialized){}` in
`onLoadChildren`. Added `waitForBrowseTree` and `onBrowseTreeInitialized`
helpers to queue pending results until browseTree is ready. All
browseTree assignments now call `onBrowseTreeInitialized()`. This avoids
ANRs and high CPU when Android Auto requests children before init.
This commit is contained in:
Lauris van Rijn 2025-08-29 00:19:24 +02:00 committed by Lauris van Rijn
parent 361c55c5ac
commit 719e517dda

View file

@ -1087,6 +1087,26 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
private val DOWNLOADS_ROOT = "__DOWNLOADS__" private val DOWNLOADS_ROOT = "__DOWNLOADS__"
private val CONTINUE_ROOT = "__CONTINUE__" private val CONTINUE_ROOT = "__CONTINUE__"
private lateinit var browseTree: BrowseTree private lateinit var browseTree: BrowseTree
private val browseTreeInitListeners = mutableListOf<() -> Unit>()
private fun waitForBrowseTree(cb: () -> Unit)
{
if (this::browseTree.isInitialized)
{
cb()
}
else
{
browseTreeInitListeners += cb
}
}
private fun onBrowseTreeInitialized()
{
// Called after browseTree is assigned for the first time
browseTreeInitListeners.forEach { it.invoke() }
browseTreeInitListeners.clear()
}
// Only allowing android auto or similar to access media browser service // Only allowing android auto or similar to access media browser service
// normal loading of audiobooks is handled in webview (not natively) // normal loading of audiobooks is handled in webview (not natively)
@ -1257,6 +1277,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
mediaManager.serverLibraries, mediaManager.serverLibraries,
mediaManager.allLibraryPersonalizationsDone mediaManager.allLibraryPersonalizationsDone
) )
onBrowseTreeInitialized()
val children = val children =
browseTree[parentMediaId]?.map { item -> browseTree[parentMediaId]?.map { item ->
Log.d(tag, "Found top menu item: ${item.description.title}") Log.d(tag, "Found top menu item: ${item.description.title}")
@ -1291,6 +1312,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
mediaManager.serverLibraries, mediaManager.serverLibraries,
mediaManager.allLibraryPersonalizationsDone mediaManager.allLibraryPersonalizationsDone
) )
onBrowseTreeInitialized()
val children = val children =
browseTree[parentMediaId]?.map { item -> browseTree[parentMediaId]?.map { item ->
Log.d(tag, "Found top menu item: ${item.description.title}") Log.d(tag, "Found top menu item: ${item.description.title}")
@ -1303,22 +1325,40 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
AbsLogger.info(tag, "onLoadChildren: Android auto data loaded") AbsLogger.info(tag, "onLoadChildren: Android auto data loaded")
result.sendResult(children as MutableList<MediaBrowserCompat.MediaItem>?) result.sendResult(children as MutableList<MediaBrowserCompat.MediaItem>?)
} }
} else if (parentMediaId == LIBRARIES_ROOT || parentMediaId == RECENTLY_ROOT) { } else if (parentMediaId == LIBRARIES_ROOT || parentMediaId == RECENTLY_ROOT)
{
Log.d(tag, "First load done: $firstLoadDone") Log.d(tag, "First load done: $firstLoadDone")
if (!firstLoadDone) { if (!firstLoadDone)
{
result.sendResult(null) result.sendResult(null)
return return
} }
// Wait until top-menu is initialized
while (!this::browseTree.isInitialized) {} if (!this::browseTree.isInitialized)
val children = {
browseTree[parentMediaId]?.map { item -> // ✅ good: detach and wait for init
Log.d(tag, "[MENU: $parentMediaId] Showing list item ${item.description.title}") result.detach()
MediaBrowserCompat.MediaItem( waitForBrowseTree {
item.description, val children = browseTree[parentMediaId]?.map { item ->
MediaBrowserCompat.MediaItem.FLAG_BROWSABLE Log.d(tag, "[MENU: $parentMediaId] Showing list item ${item.description.title}")
) MediaBrowserCompat.MediaItem(
} item.description,
MediaBrowserCompat.MediaItem.FLAG_BROWSABLE
)
}
result.sendResult(children as MutableList<MediaBrowserCompat.MediaItem>?)
}
return
}
// Already initialized: just return
val children = browseTree[parentMediaId]?.map { item ->
Log.d(tag, "[MENU: $parentMediaId] Showing list item ${item.description.title}")
MediaBrowserCompat.MediaItem(
item.description,
MediaBrowserCompat.MediaItem.FLAG_BROWSABLE
)
}
result.sendResult(children as MutableList<MediaBrowserCompat.MediaItem>?) result.sendResult(children as MutableList<MediaBrowserCompat.MediaItem>?)
} else if (mediaManager.getIsLibrary(parentMediaId)) { // Load library items for library } else if (mediaManager.getIsLibrary(parentMediaId)) { // Load library items for library
Log.d(tag, "Loading items for library $parentMediaId") Log.d(tag, "Loading items for library $parentMediaId")