Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

460
Views
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 answers
Answer question

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 Report

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 Report

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 Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!