1. Antes de comenzar
Android 10 y 11 les brindan a los usuarios más control acceso a las ubicaciones de sus dispositivos.
Cuando una app que se ejecuta en Android 11 solicita acceso a la ubicación, los usuarios tienen cuatro opciones:
- Permitir todo el tiempo
- Permitir solo con la app en uso (en Android 10)
- Solo una vez (en Android 11)
- Rechazar
Android 10
Android 11
En este codelab, aprenderás a recibir actualizaciones de ubicación y a admitir la ubicación en cualquier versión de Android, especialmente Android 10 y 11. Al final del codelab, podrás contar con una app que siga las prácticas recomendadas actuales para recuperar actualizaciones de ubicación.
Requisitos previos
- Conocimientos del desarrollo de Android
- Estar familiarizados con actividades, servicios y permisos
Actividades
- Sigue las prácticas recomendadas para la ubicación en Android.
- Controla los permisos de ubicación en primer plano (cuando el usuario solicita que la app acceda a la ubicación del dispositivo mientras la app está en uso).
- Modifica una app existente para agregar compatibilidad con la solicitud de acceso a la ubicación. Para ello, agrega código para suscribirse a la ubicación y anular la suscripción.
- Agrega compatibilidad a la app para Android 10 y 11 agregando lógica para acceder a la ubicación en primer plano o mientras está en uso.
Requisitos
- Android Studio 3.4 o una versión posterior para ejecutar el código
- Un dispositivo o emulador que ejecute una vista previa para desarrolladores de Android 10 y 11
2. Comenzar
Clona el repositorio del proyecto inicial
Para comenzar lo antes posible, puedes compilar este proyecto inicial. Si tienes Git instalado, simplemente puedes ejecutar el siguiente comando:
git clone https://github.com/android/codelab-while-in-use-location
No dudes en visitar directamente la página de GitHub.
Si no tienes Git, puedes obtener el proyecto como un archivo ZIP:
Importa el proyecto
Abre Android Studio y selecciona "Open an existing Android Studio project". desde la pantalla de bienvenida y abre el directorio del proyecto.
Después de que se cargue el proyecto, es posible que aparezca una alerta que indique que Git no está realizando un seguimiento de todos los cambios locales. Puedes hacer clic en Ignorar. (No enviarás ningún cambio al repositorio de Git).
En la esquina superior izquierda de la ventana del proyecto, deberías ver una imagen similar a la que se muestra a continuación si estás en la vista Android. (Si estás en la vista Project, debes expandir el proyecto para ver lo mismo).
Hay dos carpetas (base
y complete
). Cada uno se conoce como un "módulo".
Ten en cuenta que Android Studio puede tardar varios segundos la primera vez que compile el proyecto en segundo plano. Durante este tiempo, verás el siguiente mensaje en la barra de estado de la parte inferior de Android Studio:
Espera hasta que Android Studio termine de indexar y compilar el proyecto antes de realizar cambios en el código. De esa manera, Android Studio podrá extraer todos los componentes necesarios.
Si aparece un mensaje que dice Volver a cargar para que se apliquen los cambios de idioma o algo similar, selecciona Sí.
Comprende el proyecto inicial
Ya está todo configurado y listo para solicitar la ubicación en la app. Usa el módulo base
como punto de partida. Durante cada paso, agrega código al módulo base
. Cuando hayas terminado este codelab, el código del módulo base
debería coincidir con el contenido del módulo complete
. El módulo complete
se puede usar para revisar tu trabajo o como referencia si tienes algún problema.
Los componentes clave incluyen los siguientes:
MainActivity
: Es la IU que permite al usuario permitir que la app acceda a la ubicación del dispositivo.LocationService
: Es un servicio que suscribe o anula la suscripción a los cambios de ubicación, y que asciende a un servicio en primer plano (con una notificación) si el usuario sale de la actividad de la app. Agrega el código de ubicación aquí.Util
: Agrega funciones de extensión para la claseLocation
y guarda la ubicación enSharedPreferences
(capa de datos simplificada).
Configuración del emulador
Si quieres obtener información para configurar un emulador de Android, consulta Cómo ejecutar un emulador.
Ejecuta el proyecto inicial
Ejecuta tu app.
- Conecta tu dispositivo Android a la computadora o inicia un emulador. (Asegúrate de que el dispositivo ejecute Android 10 o una versión posterior).
- En la barra de herramientas, selecciona la configuración
base
en el selector desplegable y haz clic en Run:
- Observa que aparece la siguiente app en tu dispositivo:
Es posible que notes que no aparece información de ubicación en la pantalla de resultados. Esto se debe a que aún no agregaste el código de ubicación.
3. Agregando ubicación
Conceptos
El objetivo de este codelab es mostrarte cómo recibir actualizaciones de ubicación y, eventualmente, admitir Android 10 y Android 11.
Sin embargo, antes de comenzar a programar, tiene sentido revisar los conceptos básicos.
Tipos de acceso a la ubicación
Quizás recuerdes las cuatro opciones diferentes para acceder a la ubicación desde el comienzo de este codelab. Veamos qué significan:
- Permitir solo con la app en uso
- Esta es la opción recomendada para la mayoría de las apps. También conocido como "durante el uso" o "solo en primer plano" , esta opción se agregó en Android 10 y permite que los desarrolladores recuperen la ubicación solo mientras la app se usa de forma activa. Se considera que una app está activa si se cumple alguna de las siguientes condiciones:
- Una actividad es visible.
- Se está ejecutando un servicio en primer plano con una notificación continua.
- Solo una vez
- que se agregó en Android 11 y es lo mismo que Permitir solo con la app en uso, pero por un tiempo limitado. Para obtener más información, consulta Permisos únicos.
- Denegar
- Esta opción impide el acceso a la información de ubicación.
- Permitir todo el tiempo
- Esta opción permite el acceso a la ubicación todo el tiempo, pero requiere un permiso adicional para Android 10 y versiones posteriores. También debes asegurarte de tener un caso de uso válido y de cumplir con las políticas de ubicación. No abordarás esta opción en este codelab, ya que es un caso de uso menos frecuente. Sin embargo, si tienes un caso de uso válido y quieres comprender cómo administrar correctamente la ubicación todo el tiempo, incluido el acceso a la ubicación en segundo plano, revisa la muestra LocationUpdatesBackgroundKotlin.
Servicios, servicios en primer plano y vinculación
Para admitir completamente las actualizaciones de ubicación de la opción Permitir solo con la app en uso, debes tener en cuenta el momento en que el usuario salga de la app. Si quieres seguir recibiendo actualizaciones en esa situación, debes crear un Service
en primer plano y asociarlo con un Notification
.
Además, si deseas usar el mismo Service
para solicitar actualizaciones de ubicación cuando tu app está visible y el usuario sale de ella, debes vincular o desvincular ese Service
al elemento de la IU.
Debido a que este codelab se enfoca solo en obtener actualizaciones de ubicación, puedes encontrar todo el código que necesitas en la clase ForegroundOnlyLocationService.kt
. Puedes explorar esa clase y la MainActivity.kt
para ver cómo funcionan en conjunto.
Para obtener más información, consulta Descripción general de los servicios y Descripción general de los servicios vinculados.
Permisos
Para recibir actualizaciones de ubicación de un objeto NETWORK_PROVIDER
o GPS_PROVIDER
, debes solicitar el permiso del usuario declarando el permiso ACCESS_COARSE_LOCATION
o ACCESS_FINE_LOCATION
, respectivamente, en el archivo de manifiesto de Android. Sin estos permisos, tu app no podrá solicitar acceso a la ubicación durante el tiempo de ejecución.
Esos permisos abarcan los casos de Solo una vez y Permitir solo con la app en uso cuando la app se usa en un dispositivo con Android 10 o versiones posteriores.
Ubicación
Tu app puede acceder al conjunto de servicios de ubicación compatibles a través de las clases del paquete com.google.android.gms.location
.
Observa las clases principales:
FusedLocationProviderClient
- Este es el componente central del marco de trabajo de ubicación. Una vez creada, la usarás para solicitar actualizaciones de ubicación y obtener la ubicación conocida más reciente.
LocationRequest
- Este es un objeto de datos que contiene parámetros de calidad de servicio para las solicitudes (intervalos de actualizaciones, prioridades y precisión). Se pasa a
FusedLocationProviderClient
cuando solicitas actualizaciones de ubicación. LocationCallback
- Se usa para recibir notificaciones cuando la ubicación del dispositivo cambia o ya no se puede determinar. Se pasa un
LocationResult
donde puedes obtener elLocation
para guardar en tu base de datos.
Ahora que tienes una idea básica de lo que estás haciendo, comienza a usar el código.
4. Cómo agregar funciones de ubicación
Este codelab se enfoca en la opción de ubicación más común: Permitir solo con la app en uso.
Para recibir actualizaciones de ubicación, la app debe tener una actividad visible o un servicio ejecutándose en primer plano (con una notificación).
Permisos
El objetivo de este codelab es mostrar cómo recibir actualizaciones de ubicación, no cómo solicitar permisos de ubicación, por lo que el código basado en permisos ya está escrito por ti. Puedes omitir este paso si ya lo entiendes.
A continuación, se muestran los permisos destacados (no se requiere ninguna acción para esta parte):
- Declara qué permiso usas en
AndroidManifest.xml
. - Antes de intentar acceder a la información de ubicación, verifica si el usuario le otorgó permiso a la app para hacerlo. Si tu app aún no recibió permiso, solicita acceso.
- Controla la elección de permisos del usuario. (Puedes ver este código en
MainActivity.kt
).
Si buscas TODO: Step 1.0, Review Permissions
en AndroidManifest.xml
o MainActivity.kt
, verás todo el código escrito para los permisos.
Para obtener más información, consulta Descripción general de los permisos.
Ahora, empieza a escribir algún código de ubicación.
Revisa las variables clave necesarias para las actualizaciones de ubicación
En el módulo base
, busca TODO: Step 1.1, Review variables
en
Archivo ForegroundOnlyLocationService.kt
.
No es necesario realizar ninguna acción en este paso. Solo debes revisar el siguiente bloque de código, junto con los comentarios, para comprender las clases y variables clave que usas para recibir actualizaciones de ubicación.
// TODO: Step 1.1, Review variables (no changes).
// FusedLocationProviderClient - Main class for receiving location updates.
private lateinit var fusedLocationProviderClient: FusedLocationProviderClient
// LocationRequest - Requirements for the location updates, i.e., how often you
// should receive updates, the priority, etc.
private lateinit var locationRequest: LocationRequest
// LocationCallback - Called when FusedLocationProviderClient has a new Location.
private lateinit var locationCallback: LocationCallback
// Used only for local storage of the last known location. Usually, this would be saved to your
// database, but because this is a simplified sample without a full database, we only need the
// last location to create a Notification if the user navigates away from the app.
private var currentLocation: Location? = null
Revisa la inicialización de FusedLocationProviderClient
En el módulo base
, busca TODO: Step 1.2, Review the FusedLocationProviderClient
en el archivo ForegroundOnlyLocationService.kt
. El código debería ser similar al siguiente:
// TODO: Step 1.2, Review the FusedLocationProviderClient.
fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this)
Como se mencionó en los comentarios anteriores, esta es la clase principal para obtener actualizaciones sobre la ubicación. La variable ya se inicializó, pero es importante que revises el código para comprender cómo se inicializó. Más adelante, agregarás código aquí para solicitar actualizaciones de ubicación.
Inicializa la LocationRequest
- En el módulo
base
, buscaTODO: Step 1.3, Create a LocationRequest
en el archivoForegroundOnlyLocationService.kt
. - Agrega el siguiente código después del comentario.
El código de inicialización LocationRequest
agrega la calidad adicional de los parámetros de servicio que necesitas para tu solicitud (intervalos, tiempo de espera máximo y prioridad).
// TODO: Step 1.3, Create a LocationRequest.
locationRequest = LocationRequest.create().apply {
// Sets the desired interval for active location updates. This interval is inexact. You
// may not receive updates at all if no location sources are available, or you may
// receive them less frequently than requested. You may also receive updates more
// frequently than requested if other applications are requesting location at a more
// frequent interval.
//
// IMPORTANT NOTE: Apps running on Android 8.0 and higher devices (regardless of
// targetSdkVersion) may receive updates less frequently than this interval when the app
// is no longer in the foreground.
interval = TimeUnit.SECONDS.toMillis(60)
// Sets the fastest rate for active location updates. This interval is exact, and your
// application will never receive updates more frequently than this value.
fastestInterval = TimeUnit.SECONDS.toMillis(30)
// Sets the maximum time when batched location updates are delivered. Updates may be
// delivered sooner than this interval.
maxWaitTime = TimeUnit.MINUTES.toMillis(2)
priority = LocationRequest.PRIORITY_HIGH_ACCURACY
}
- Lee los comentarios para entender cómo funciona cada uno.
Inicializa la LocationCallback
- En el módulo
base
, buscaTODO: Step 1.4, Initialize the LocationCallback
en el archivoForegroundOnlyLocationService.kt
. - Agrega el siguiente código después del comentario.
// TODO: Step 1.4, Initialize the LocationCallback.
locationCallback = object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult) {
super.onLocationResult(locationResult)
// Normally, you want to save a new location to a database. We are simplifying
// things a bit and just saving it as a local variable, as we only need it again
// if a Notification is created (when the user navigates away from app).
currentLocation = locationResult.lastLocation
// Notify our Activity that a new location was added. Again, if this was a
// production app, the Activity would be listening for changes to a database
// with new locations, but we are simplifying things a bit to focus on just
// learning the location side of things.
val intent = Intent(ACTION_FOREGROUND_ONLY_LOCATION_BROADCAST)
intent.putExtra(EXTRA_LOCATION, currentLocation)
LocalBroadcastManager.getInstance(applicationContext).sendBroadcast(intent)
// Updates notification content if this service is running as a foreground
// service.
if (serviceRunningInForeground) {
notificationManager.notify(
NOTIFICATION_ID,
generateNotification(currentLocation))
}
}
}
El objeto LocationCallback
que creas aquí es la devolución de llamada que llamará el objeto FusedLocationProviderClient
cuando haya una nueva actualización de ubicación disponible.
En tu devolución de llamada, primero obtienes la ubicación más reciente con un objeto LocationResult
. Luego, debes notificar a tu Activity
sobre la nueva ubicación mediante una transmisión local (si está activa) o actualizar la Notification
si este servicio se ejecuta como Service
en primer plano.
- Lee los comentarios para entender qué hace cada parte.
Suscribirse a los cambios de ubicación
Ahora que inicializaste todo, debes indicarle a FusedLocationProviderClient
que deseas recibir actualizaciones.
- En el módulo
base
, buscaStep 1.5, Subscribe to location changes
en el archivoForegroundOnlyLocationService.kt
. - Agrega el siguiente código después del comentario.
// TODO: Step 1.5, Subscribe to location changes.
fusedLocationProviderClient.requestLocationUpdates(locationRequest, locationCallback, Looper.getMainLooper())
La llamada a requestLocationUpdates()
le indica al FusedLocationProviderClient
que quieres recibir actualizaciones de ubicación.
Es probable que reconozcas LocationRequest
y LocationCallback
que definiste antes. Esos le permiten al FusedLocationProviderClient
conocer los parámetros de calidad de servicio de tu solicitud y lo que debe llamar cuando tiene una actualización. Por último, el objeto Looper
especifica el subproceso para la devolución de llamada.
También puedes observar que este código se encuentra dentro de una sentencia try/catch
. Este método requiere el bloqueo debido a que una SecurityException
se produce cuando tu app no tiene permiso para acceder a la información de ubicación.
Cómo anular la suscripción a los cambios de ubicación
Cuando la app ya no necesita acceder a la información de ubicación, es importante anular la suscripción a las actualizaciones de ubicación.
- En el módulo
base
, buscaTODO: Step 1.6, Unsubscribe to location changes
en el archivoForegroundOnlyLocationService.kt
. - Agrega el siguiente código después del comentario.
// TODO: Step 1.6, Unsubscribe to location changes.
val removeTask = fusedLocationProviderClient.removeLocationUpdates(locationCallback)
removeTask.addOnCompleteListener { task ->
if (task.isSuccessful) {
Log.d(TAG, "Location Callback removed.")
stopSelf()
} else {
Log.d(TAG, "Failed to remove Location Callback.")
}
}
El método removeLocationUpdates()
configura una tarea para que FusedLocationProviderClient
sepa que ya no quieres recibir actualizaciones de ubicación de tu LocationCallback
. addOnCompleteListener()
proporciona la devolución de llamada para completar y ejecuta Task
.
Al igual que en el paso anterior, quizás hayas notado que este código se encuentra dentro de una sentencia try/catch
. Este método requiere el bloqueo debido a que una SecurityException
se produce cuando tu app no tiene permiso para acceder a la información de ubicación.
Es posible que te preguntes cuándo se llama a los métodos que contienen el código de suscripción o anulación de suscripción. Se activan en la clase principal cuando el usuario presiona el botón. Si deseas verlo, consulta la clase MainActivity.kt
.
Ejecutar app
Ejecuta la app desde Android Studio y prueba el botón de ubicación.
Deberías ver la información de ubicación en la pantalla de resultados. Esta es una app completamente funcional para Android 9.
5. Compatibilidad con Android 10
En esta sección, agregarás compatibilidad con Android 10.
Tu app ya se suscribió a los cambios de ubicación, por lo que no hay mucho trabajo por hacer.
De hecho, lo único que debes hacer es especificar que tu servicio en primer plano se use con fines de ubicación.
SDK de destino 29
- En el módulo
base
, buscaTODO: Step 2.1, Target Android 10 and then Android 11.
en el archivobuild.gradle
. - Realiza estos cambios:
- Establece
targetSdkVersion
en29
.
El código debería ser similar al siguiente:
android {
// TODO: Step 2.1, Target Android 10 and then Android 11.
compileSdkVersion 29
defaultConfig {
applicationId "com.example.android.whileinuselocation"
minSdkVersion 26
targetSdkVersion 29
versionCode 1
versionName "1.0"
}
...
}
Después de hacerlo, se te pedirá que sincronices tu proyecto. Haz clic en Sincronizar ahora.
Luego de esa fecha, tu app estará casi lista para Android 10.
Agrega un tipo de servicio en primer plano
En Android 10, debes incluir el tipo de tu servicio en primer plano si necesitas acceso a la ubicación mientras está en uso. En tu caso, se usa para obtener información de ubicación.
En el módulo base
, busca TODO: 2.2, Add foreground service type
en AndroidManifest.xml
y agrega el siguiente código al elemento <service>
:
android:foregroundServiceType="location"
El código debería ser similar al siguiente:
<application>
...
<!-- Foreground services in Android 10+ require type. -->
<!-- TODO: 2.2, Add foreground service type. -->
<service
android:name="com.example.android.whileinuselocation.ForegroundOnlyLocationService"
android:enabled="true"
android:exported="false"
android:foregroundServiceType="location" />
</application>
Eso es todo. Tu app admite la ubicación de Android 10 "mientras está en uso" siguiendo las prácticas recomendadas para la ubicación en Android.
Ejecutar app
Ejecuta la app desde Android Studio y prueba el botón de ubicación.
Todo debería funcionar como antes, pero ahora funciona en Android 10. Si no aceptaste los permisos para las ubicaciones, ahora deberías ver la pantalla de permisos.
6. Compatibilidad con Android 11
En esta sección, el objetivo es Android 11.
¡Buenas noticias! No necesitas realizar cambios en ningún archivo, excepto en el archivo build.gradle
.
SDK de destino 11
- En el módulo
base
, buscaTODO: Step 2.1, Target SDK
en el archivobuild.gradle
. - Realiza estos cambios:
- De
compileSdkVersion
a30
- De
targetSdkVersion
a30
El código debería ser similar al siguiente:
android {
TODO: Step 2.1, Target Android 10 and then Android 11.
compileSdkVersion 30
defaultConfig {
applicationId "com.example.android.whileinuselocation"
minSdkVersion 26
targetSdkVersion 30
versionCode 1
versionName "1.0"
}
...
}
Después de hacerlo, se te pedirá que sincronices tu proyecto. Haz clic en Sincronizar ahora.
Luego, tu app estará lista para Android 11.
Ejecutar app
Ejecuta la app desde Android Studio y, luego, intenta hacer clic en el botón.
Todo debería funcionar como antes, pero ahora funciona en Android 11. Si no aceptaste los permisos para las ubicaciones, ahora deberías ver la pantalla de permisos.
7. Estrategias de ubicación para Android
Si verificas y solicitas permisos de ubicación como se muestra en este codelab, tu app podrá hacer un seguimiento correcto de su nivel de acceso con respecto a la ubicación del dispositivo.
En esta página, se incluyen algunas prácticas recomendadas clave relacionadas con los permisos de ubicación. Para obtener más información sobre cómo mantener datos seguros, consulta Prácticas recomendadas de permisos de apps.
Solicita solo los permisos que necesitas
Solicita permisos solo cuando los necesites. Por ejemplo:
- No solicites un permiso de ubicación cuando se inicia la app, a menos que sea absolutamente necesario.
- Si tu app está orientada a Android 10 o versiones posteriores y tienes un servicio en primer plano, declara un
foregroundServiceType
de"location"
en el manifiesto. - No solicites permisos de ubicación en segundo plano, a menos que tengas un caso de uso válido, como se describe en Acceso más seguro y transparente a la ubicación del usuario.
Admite la degradación elegante si no se otorga permiso
Para mantener una buena experiencia del usuario, diseña tu app de modo que pueda manejar correctamente las siguientes situaciones:
- Tu app no tiene acceso a la información sobre la ubicación.
- Tu app no tiene acceso a la información de ubicación cuando se ejecuta en segundo plano.
8. Felicitaciones
Aprendiste a recibir actualizaciones de ubicación en Android teniendo en cuenta las prácticas recomendadas.
Más información
- Ejemplo completo para usar la ubicación en segundo plano si tienes un caso de uso válido
- Cómo solicitar actualizaciones de ubicación