佩戴操作系统瓷砖和媒体服务



Wear OS tiles的例子很好,不是什么问题,而是如何启动播放主应用程序中选择的歌曲的后台媒体服务,当我每次尝试启动该服务时,都会收到以下错误。没有可引用的UI线程,文档中只有onclick、LoadAction和LaunchAction的方法。

override fun onTileRequest(request: TileRequest) = serviceScope.future {
when(request.state!!.lastClickableId){
"play"-> playClicked()
}....
suspend fun playClicked(){
try {
// Convert the asynchronous callback to a suspending coroutine
suspendCancellableCoroutine<Unit> { cont ->
mMediaBrowserCompat = MediaBrowserCompat(
applicationContext, ComponentName(applicationContext, MusicService::class.java),
mMediaBrowserCompatConnectionCallback, null
)
mMediaBrowserCompat!!.connect()
}
}catch (e:Exception){
e.printStackTrace()
} finally {
mMediaBrowserCompat!!.disconnect()
}
}

错误

java.lang.RuntimeException: Can't create handler inside thread Thread[DefaultDispatcher-worker-1,5,main] that has not called Looper.prepare()

serviceScope正在Dispatchers.IO上运行。在对MediaBrowserCompat进行任何调用时,应使用withContext(Dispatchers.Main(。

响应上面的答案,serviceScope.efuture创建一个CoroutineScope,它将导致返回到服务的future等待所有子作业完成。

如果你想让它从onTileRequest调用中分离出来运行,你可以运行以下操作,它将在应用程序GlobalScope中启动一个新作业,并让onTileRRequest立即返回。

"play" -> GlobalScope.launch { 
}

这样做的好处是,您不会将第三个并发模型、ListenableFutures、Coroutines和现在的Handler加入到混合中。LF和Coroutines旨在避免您不得不求助于第三个并发选项。

感谢Yuri的工作,但它最终阻塞了UI线程,工作的解决方案低于

fun playClicked(){
mainHandler.post(playSong)
}
private val playSong: Runnable = object : Runnable {
@RequiresApi(Build.VERSION_CODES.N)
override fun run() {
mMediaBrowserCompat = MediaBrowserCompat(
applicationContext, ComponentName(applicationContext, MusicaWearService::class.java),
mMediaBrowserCompatConnectionCallback, null
)
mMediaBrowserCompat!!.connect()
}
}

酷尤里,下面的工作和我认为是更有效的

fun playClicked() = GlobalScope.launch(Dispatchers.Main) {
mMediaBrowserCompat = MediaBrowserCompat(
applicationContext, ComponentName(applicationContext, MusicaWearService::class.java),
mMediaBrowserCompatConnectionCallback, null
)
mMediaBrowserCompat!!.connect()
}

最新更新