Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

461
Vistas
FusedLocationProviderClient con rutinas Kotlin

Estoy tratando de solicitar una nueva ubicación con FusedLocationProviderClient y Kotlin Coroutines. Esta es mi configuración actual:

 class LocationProviderImpl(context: Context) : LocationProvider, CoroutineScope { private val TAG = this::class.java.simpleName private val job = Job() override val coroutineContext: CoroutineContext get() = job + Dispatchers.IO private val fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(context) private val locationRequest = LocationRequest().apply { numUpdates = 1 priority = LocationRequest.PRIORITY_HIGH_ACCURACY } override suspend fun getLocation(): LatLng = suspendCoroutine { val locationCallback = object : LocationCallback() { override fun onLocationResult(result: LocationResult) { result.lastLocation.run { val latLng = latitude at longitude it.resume(latLng) } fusedLocationProviderClient.removeLocationUpdates(this) } } try { fusedLocationProviderClient.requestLocationUpdates(locationRequest, locationCallback, Looper.myLooper()) } catch (e: SecurityException) { throw NoLocationPermissionException() } } }

Pero cuando intento solicitar una nueva ubicación, obtengo la siguiente excepción:

 java.lang.IllegalStateException: Can't create handler inside thread that has not called Looper.prepare()

Sin embargo, si llamara a Looper.prepare() (y Looper.quit() eventualmente) ¿no significaría que puedo llamar a la función solo una vez?

Cualquier ayuda es apreciada.

over 4 years ago · Santiago Trujillo
3 Respuestas
Responde la pregunta

0

private val locationRequestGPS by lazy { LocationRequest.create() .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY) .setNumUpdates(1) .setExpirationDuration(1000) } private val locationRequestNETWORK by lazy { LocationRequest.create() .setPriority(LocationRequest.PRIORITY_LOW_POWER) .setNumUpdates(1) .setExpirationDuration(1000) } suspend fun getLocation(context: Context, offsetMinutes: Int = 15): Location? = suspendCoroutine { task -> val ctx = context.applicationContext if (!ctx.isPermissionValid(Manifest.permission.ACCESS_COARSE_LOCATION) && !ctx.isPermissionValid(Manifest.permission.ACCESS_FINE_LOCATION)) { task.resume(null) } else { val manager = ctx.getSystemService(Context.LOCATION_SERVICE) as LocationManager if (!LocationManagerCompat.isLocationEnabled(manager)) { task.resume(null) } else { val service = LocationServices.getFusedLocationProviderClient(ctx) service.lastLocation .addOnCompleteListener { locTask -> if (locTask.result == null || System.currentTimeMillis() - locTask.result!!.time > offsetMinutes.minute) { GlobalScope.launch(Dispatchers.Main) { task.resume(locationRequest(manager, service)) } } else { task.resume(locTask.result) } } } } } suspend fun getLocationLast(context: Context): Location? = suspendCoroutine { task -> val ctx = context.applicationContext if (!ctx.isPermissionValid(Manifest.permission.ACCESS_COARSE_LOCATION) && !ctx.isPermissionValid(Manifest.permission.ACCESS_FINE_LOCATION)) { task.resume(null) } else { if (!LocationManagerCompat.isLocationEnabled(ctx.getSystemService(Context.LOCATION_SERVICE) as LocationManager)) { task.resume(null) } else { LocationServices.getFusedLocationProviderClient(ctx) .lastLocation .addOnCompleteListener { locTask -> task.resume(locTask.result) } } } } suspend fun locationRequest(locationManager: LocationManager, service: FusedLocationProviderClient): Location? = suspendCoroutine { task -> val callback = object : LocationCallback() { override fun onLocationResult(p0: LocationResult?) { service.removeLocationUpdates(this) task.resume(p0?.lastLocation) } } when { locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) -> { service.requestLocationUpdates(locationRequestGPS, callback, Looper.getMainLooper()) } locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER) -> { service.requestLocationUpdates(locationRequestNETWORK, callback, Looper.getMainLooper()) } else -> { task.resume(null) } } }
over 4 years ago · Santiago Trujillo Denunciar

0

Has configurado mal tu coroutineContext . En su lugar, deberías tener

 override val coroutineContext = Dispatchers.MAIN + job

Si alguna vez necesita el despachador de IO , pídale explícitamente:

 withContext(Dispatchers.IO) { ... blocking IO code ... }

Para suspender la rutina, llama a suspendCancellableCoroutine , de lo contrario no obtendrás ningún beneficio de la concurrencia estructurada.

Otro detalle, no escriba ningún código después de él. it.resume en el bloque suspendCancellableCoroutine . Si el despachador elige reanudar la rutina inmediatamente, dentro de la llamada de resume , ese código no se ejecutará hasta que se haya ejecutado todo el código de la rutina (o al menos hasta el siguiente punto de suspensión).

 override fun onLocationResult(result: LocationResult) { fusedLocationProviderClient.removeLocationUpdates(this) it.resume(result.lastLocation.run { latitude at longitude }) }
over 4 years ago · Santiago Trujillo Denunciar

0

Al usar suspendCoroutine , invoca el código proporcionado en el despachador que llamó a la función suspendida durante el tiempo de ejecución. Dado que la mayoría de los despachadores no se ejecutan en subprocesos de Looper (prácticamente solo lo hace Dispatchers.MAIN ), la llamada a Looper.myLooper() falla.

La documentación dice que puede reemplazar Looper.myLooper() con null para invocar la devolución de llamada en un hilo no especificado. El despachador de rutina incorporado se asegurará de que se enruta al subproceso correcto para reanudar la ejecución.

EDITAR: es posible que deba llamarlo it.intercepted().resume(latLng) para asegurarse de que el resultado se envíe al hilo correcto. No estoy del todo seguro de si la continuación de suspendCoroutine se intercepta de forma predeterminada.

Además, no necesita llamar a fusedLocationProviderClient.removeLocationUpdates(this) porque ya configuró la cantidad de actualizaciones en LocationRequest en 1 .

over 4 years ago · Santiago Trujillo Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda