El ejemplo de mosaicos de Wear OS es excelente, no es un problema, pero ¿cómo se iniciaría el servicio de medios en segundo plano que reproduce las canciones seleccionadas en la aplicación principal? Cada vez que intento iniciar el servicio, aparece el siguiente error. No hay un subproceso de interfaz de usuario al que hacer referencia y la documentación solo tiene métodos para onclick, LoadAction y 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() } }ERROR
java.lang.RuntimeException: Can't create handler inside thread Thread[DefaultDispatcher-worker-1,5,main] that has not called Looper.prepare()serviceScope se ejecuta en Dispatchers.IO, debe usar withContext(Dispatchers.Main) al realizar llamadas a MediaBrowserCompat.
Cool Yuri, lo siguiente funcionó y creo que es más eficiente
fun playClicked() = GlobalScope.launch(Dispatchers.Main) { mMediaBrowserCompat = MediaBrowserCompat( applicationContext, ComponentName(applicationContext, MusicaWearService::class.java), mMediaBrowserCompatConnectionCallback, null ) mMediaBrowserCompat!!.connect() }Gracias, Yuri, funcionó, pero terminó bloqueando el subproceso de la interfaz de usuario, la solución que funciona está a continuación.
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() } }```En respuesta a la respuesta anterior, serviceScope.future crea un CoroutineScope que hará que el futuro devuelto al servicio espere a que se completen todos los trabajos secundarios.
Si desea que se ejecute de forma separada de la llamada onTileRequest, puede ejecutar lo siguiente, que iniciará un nuevo trabajo dentro de la aplicación GlobalScope y permitirá que onTileRequest regrese de inmediato.
"play"-> GlobalScope.launch { }El beneficio de esto es que no incluye un tercer modelo de concurrencia en la mezcla, ListenableFutures, Coroutines y ahora Handler. LF y Coroutines están destinados a evitar que tenga que recurrir a una tercera opción de concurrencia.