Google se compromete a impulsar la igualdad racial para las comunidades afrodescendientes. Obtén información al respecto.

Estrategias de ubicación

Nota: Las estrategias descritas en esta guía se aplican a la API de ubicación de la plataforma en android.location. La API de los Servicios de ubicación de Google, que forma parte de los Servicios de Google Play, proporciona un marco de trabajo más potente y de alto nivel que administra automáticamente a los proveedores de ubicación, el movimiento del usuario y la precisión de la ubicación. También administra la programación de actualizaciones de ubicación según los parámetros de consumo de energía que proporciones. En la mayoría de los casos, usando la API de los Servicios de ubicación, obtendrás un mejor rendimiento de la batería y una mayor precisión.

Para obtener más información sobre la API de los Servicios de ubicación, consulta Servicios de ubicación de Google para Android .

Saber dónde está el usuario permite que tu app sea más inteligente y ofrezca una mejor información al usuario. Cuando desarrollas una app para Android con conocimiento de la ubicación, puedes usar el GPS y el proveedor de ubicación de red de Android para obtener la ubicación del usuario. Aunque el GPS es más preciso, solo funciona en exteriores, consume rápidamente la batería y no muestra la ubicación tan rápido como los usuarios lo desean. El proveedor de ubicación de red de Android determina la ubicación del usuario utilizando señales de torres de telefonía celular y de Wi-Fi, por lo que proporciona información de ubicación de un modo que funciona en interiores y exteriores, tiene una respuesta más rápida y utiliza menos energía de la batería. Para obtener la ubicación del usuario en tu app, puedes usar el GPS y el proveedor de ubicación de red, o solo uno de los dos.

Desafíos para determinar la ubicación del usuario

Obtener la ubicación del usuario desde un dispositivo móvil puede ser complicado. Existen varias razones por las que una lectura de ubicación (sin importar la fuente) puede contener errores y ser inexacta. Entre las fuentes de errores de la ubicación del usuario, se incluyen las siguientes:

  • Varias fuentes de ubicación

    El GPS, el ID del celular y la red Wi-Fi pueden proporcionar una pista sobre la ubicación del usuario. Cuando se determina qué dispositivos usar y en cuáles se confía, se suele perder algo de precisión, velocidad y eficiencia de la batería.

  • Movimiento del usuario

    Debido a que la ubicación del usuario cambia, debes tener en cuenta el movimiento y volver a estimar la ubicación del usuario cada cierto tiempo.

  • Precisión variable

    Las estimaciones de ubicación que provienen de cada fuente de ubicación no tienen una precisión coherente. Una ubicación obtenida hace 10 segundos de una fuente puede ser más precisa que la ubicación más reciente de otra fuente o de la misma.

Estos problemas pueden dificultar la obtención de una lectura confiable de la ubicación del usuario. En este documento, se incluye información que te ayudará a cumplir estos desafíos y obtener una lectura confiable de tu ubicación. También se incluyen ideas que puedes usar en tu app para proporcionar al usuario una experiencia de ubicación geográfica precisa y con capacidad de respuesta.

Cómo solicitar actualizaciones de ubicación

Antes de abordar algunos de los errores de ubicación que se describen más arriba, aquí presentamos una introducción de cómo puedes obtener la ubicación del usuario en Android.

Obtener la ubicación del usuario en Android funciona mediante una devolución de llamada. Debes indicar que deseas recibir actualizaciones de ubicación del LocationManager llamando a requestLocationUpdates() y pasándole un LocationListener. Tu LocationListener debe implementar varios métodos de devolución de llamada a los que LocationManager llama cuando cambia la ubicación del usuario o cuando cambia el estado del servicio.

Nota: En Android 8.0 (API nivel 26) y versiones posteriores, si una app se ejecuta en segundo plano cuando solicita la ubicación actual, el dispositivo calcula la ubicación solo unas pocas veces por hora. Si deseas obtener información sobre cómo adaptar tu app a estos límites de cálculo, consulta Límites de ubicación en segundo plano.

En el siguiente código, se muestra cómo definir un LocationListener y solicitar actualizaciones de ubicación:

Kotlin

    // Acquire a reference to the system Location Manager
    val locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager

    // Define a listener that responds to location updates
    val locationListener = object : LocationListener {

        override fun onLocationChanged(location: Location) {
            // Called when a new location is found by the network location provider.
            makeUseOfNewLocation(location)
        }

        override fun onStatusChanged(provider: String, status: Int, extras: Bundle) {
        }

        override fun onProviderEnabled(provider: String) {
        }

        override fun onProviderDisabled(provider: String) {
        }
    }

    // Register the listener with the Location Manager to receive location updates
    locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0f,locationListener)
    

Java

    // Acquire a reference to the system Location Manager
    LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);

    // Define a listener that responds to location updates
    LocationListener locationListener = new LocationListener() {
        public void onLocationChanged(Location location) {
          // Called when a new location is found by the network location provider.
          makeUseOfNewLocation(location);
        }

        public void onStatusChanged(String provider, int status, Bundle extras) {}

        public void onProviderEnabled(String provider) {}

        public void onProviderDisabled(String provider) {}
      };

    // Register the listener with the Location Manager to receive location updates
    locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListener);
    

El primer parámetro de requestLocationUpdates() es el tipo de proveedor de ubicación que se usará (en este caso, el proveedor de ubicación de red basada en la torre de telefonía celular y Wi-Fi). Puedes controlar la frecuencia con la que el objeto de escucha recibe actualizaciones configurando el segundo y tercer parámetro (el segundo es el intervalo de tiempo mínimo entre notificaciones y el tercero es el cambio mínimo en la distancia entre notificaciones) para que soliciten entre cero notificaciones de ubicación y tantas como sea posible. El último parámetro es tu LocationListener, que recibe devoluciones de llamada para actualizaciones de ubicación.

Para solicitar actualizaciones de ubicación del proveedor de GPS, usa GPS_PROVIDER en lugar de NETWORK_PROVIDER. También puedes solicitar actualizaciones de ubicación desde el GPS y el proveedor de ubicación de red llamando a requestLocationUpdates() dos veces (una para NETWORK_PROVIDER y otra para GPS_PROVIDER).

Cómo solicitar permisos de usuario

Para recibir actualizaciones de ubicación desde 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 del manifiesto de Android. Sin estos permisos, tu app fallará durante el tiempo de ejecución cuando solicite actualizaciones de ubicación.

Si estás usando tanto NETWORK_PROVIDER como GPS_PROVIDER, debes solicitar solo el permiso ACCESS_FINE_LOCATION, ya que incluye permiso para ambos proveedores. El permiso para ACCESS_COARSE_LOCATION solo permite el acceso a NETWORK_PROVIDER.

Precaución: Si tu app se orienta a Android 5.0 (API nivel 21) o superior, debes declarar que utiliza la función de hardware android.hardware.location.network o android.hardware.location.gps en el archivo del manifiesto, en función de si la app recibe actualizaciones de ubicación de NETWORK_PROVIDER o de GPS_PROVIDER. Si tu app recibe información de ubicación de cualquiera de estas fuentes de proveedores de ubicación, debes declarar que usa estas funciones de hardware en el manifiesto de la app. En los dispositivos que ejecutan versiones anteriores a Android 5.0 (API nivel 21), solicitar el permiso ACCESS_FINE_LOCATION o ACCESS_COARSE_LOCATION incluye una solicitud implícita de funciones de hardware de ubicación. Sin embargo, cuando se solicitan esos permisos, no se solicitan automáticamente funciones de hardware de ubicación en Android 5.0 (API nivel 21) y posteriores.

En el siguiente ejemplo de código, se muestra cómo declarar el permiso y la función de hardware en el archivo de manifiesto de una app que lee datos del GPS del dispositivo:

    <manifest ... >
        <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
        ...
        <!-- Needed only if your app targets Android 5.0 (API level 21) or higher. -->
        <uses-feature android:name="android.hardware.location.gps" />
        ...
    </manifest>
    

Cómo definir un modelo para obtener el mejor rendimiento

Las apps basadas en la ubicación son comunes, pero, debido a que su precisión no es óptima, al movimiento del usuario, a la variedad de métodos para obtener la ubicación y al deseo de conservar la batería, es complicado obtener la ubicación del usuario. Si quieres superar el obstáculo de obtener una buena ubicación del usuario mientras se conserva la batería, debes definir un modelo coherente que especifique cómo obtiene tu app la ubicación del usuario. Este modelo incluye cuándo comienzas a detectar actualizaciones, cuándo dejas de hacerlo y cuándo usar datos de ubicación en caché.

Flujo para obtener la ubicación del usuario

A continuación, se describe el flujo típico de procedimientos para obtener la ubicación del usuario:

  1. Inicia la app.
  2. Un tiempo después, comienza a detectar las actualizaciones de los proveedores de ubicación que desees.
  3. Mantén una "mejor estimación actual" de la ubicación filtrando ajustes nuevos, pero menos precisos.
  4. Deja de detectar las actualizaciones de ubicación.
  5. Aprovecha la mejor estimación de ubicación más reciente.

En la Figura 1, se muestra este modelo en una línea de tiempo en la que se visualiza el período en el que una app detecta actualizaciones de ubicación y los eventos que se producen durante ese tiempo.

Figura 1: Una línea de tiempo que representa la ventana en la que una app detecta actualizaciones de ubicación

Este modelo de ventana, durante el cual se reciben actualizaciones de ubicación, enmarca muchas de las decisiones que debes tomar cuando agregas a tu app servicios basados en ubicación.

Cómo decidir cuándo comenzar a detectar actualizaciones

Es posible que quieras comenzar a detectar actualizaciones de ubicación tan pronto como se inicie la app o solo después de que los usuarios activen una función determinada. Ten en cuenta que las ventanas largas para detectar ajustes de ubicación pueden consumir mucha batería, pero también es posible que en los períodos breves no se obtenga una precisión suficiente.

Como se demostró más arriba, puedes comenzar a detectar actualizaciones llamando a requestLocationUpdates():

Kotlin

    val locationProvider: String = LocationManager.NETWORK_PROVIDER
    // Or, use GPS location data:
    // val locationProvider: String = LocationManager.GPS_PROVIDER;

    locationManager.requestLocationUpdates(locationProvider, 0, 0, locationListener)
    

Java

    String locationProvider = LocationManager.NETWORK_PROVIDER;
    // Or, use GPS location data:
    // String locationProvider = LocationManager.GPS_PROVIDER;

    locationManager.requestLocationUpdates(locationProvider, 0, 0, locationListener);
    

Cómo obtener una solución rápida con la ubicación más reciente

El tiempo que tarda tu objeto de escucha de ubicación en recibir el primer ajuste de ubicación suele ser demasiado largo. Hasta que se proporcione una ubicación más precisa al objeto de escucha de ubicación, debes utilizar una ubicación en caché llamando a getLastKnownLocation(String):

Kotlin

    val locationProvider: String = LocationManager.NETWORK_PROVIDER
    // Or use LocationManager.GPS_PROVIDER

    val lastKnownLocation: Location = locationManager.getLastKnownLocation(locationProvider)
    

Java

    String locationProvider = LocationManager.NETWORK_PROVIDER;
    // Or use LocationManager.GPS_PROVIDER

    Location lastKnownLocation = locationManager.getLastKnownLocation(locationProvider);
    

Cómo decidir cuándo dejar de detectar actualizaciones

En función de la app, la lógica para decidir cuándo los nuevos ajustes ya no son necesarios puede ir de muy simple a muy compleja. Un espacio corto entre el momento en que se adquiere la ubicación y el momento en que se usa permite mejorar la precisión de la estimación. Siempre debes tener en cuenta que detectar durante mucho tiempo consume mucha batería, de modo que, cuando tengas la información que necesitas, debes dejar de detectar actualizaciones llamando a removeUpdates(PendingIntent):

Kotlin

    // Remove the listener you previously added
    locationManager.removeUpdates(locationListener)
    

Java

    // Remove the listener you previously added
    locationManager.removeUpdates(locationListener);
    

Cómo mantener una mejor estimación actual

Tal vez creas que el ajuste de ubicación más reciente es el más preciso. Sin embargo, debido a que la precisión de un ajuste de ubicación varía, el ajuste más reciente no siempre es el mejor. Debes implementar la lógica para elegir los ajustes de ubicación según varios criterios. Los criterios también varían según los casos prácticos de la app y las pruebas de campo.

A continuación, se incluyen algunos pasos que puedes seguir para validar la precisión de un ajuste de ubicación:

  • Comprueba si la ubicación recuperada es mucho más reciente que la estimación anterior.
  • Verifica si la precisión de la ubicación es mejor o peor que la estimación anterior.
  • Verifica de qué proveedor es la nueva ubicación y determina si confías más en él.

Un ejemplo elaborado de esta lógica puede tener el siguiente aspecto:

Kotlin

    private const val TWO_MINUTES: Long = 1000 * 60 * 2

    /** Determines whether one Location reading is better than the current Location fix
     * @param location The new Location that you want to evaluate
     * @param currentBestLocation The current Location fix, to which you want to compare the new one
     */
    fun isBetterLocation(location: Location, currentBestLocation: Location?): Boolean {
        if (currentBestLocation == null) {
            // A new location is always better than no location
            return true
        }

        // Check whether the new location fix is newer or older
        val timeDelta: Long = location.time - currentBestLocation.time
        val isSignificantlyNewer: Boolean = timeDelta > TWO_MINUTES
        val isSignificantlyOlder:Boolean = timeDelta < -TWO_MINUTES

        when {
            // If it's been more than two minutes since the current location, use the new location
            // because the user has likely moved
            isSignificantlyNewer -> return true
            // If the new location is more than two minutes older, it must be worse
            isSignificantlyOlder -> return false
        }

        // Check whether the new location fix is more or less accurate
        val isNewer: Boolean = timeDelta > 0L
        val accuracyDelta: Float = location.accuracy - currentBestLocation.accuracy
        val isLessAccurate: Boolean = accuracyDelta > 0f
        val isMoreAccurate: Boolean = accuracyDelta < 0f
        val isSignificantlyLessAccurate: Boolean = accuracyDelta > 200f

        // Check if the old and new location are from the same provider
        val isFromSameProvider: Boolean = location.provider == currentBestLocation.provider

        // Determine location quality using a combination of timeliness and accuracy
        return when {
            isMoreAccurate -> true
            isNewer && !isLessAccurate -> true
            isNewer && !isSignificantlyLessAccurate && isFromSameProvider -> true
            else -> false
        }
    }
    

Java

    private static final int TWO_MINUTES = 1000 * 60 * 2;

    /** Determines whether one Location reading is better than the current Location fix
      * @param location  The new Location that you want to evaluate
      * @param currentBestLocation  The current Location fix, to which you want to compare the new one
      */
    protected boolean isBetterLocation(Location location, Location currentBestLocation) {
        if (currentBestLocation == null) {
            // A new location is always better than no location
            return true;
        }

        // Check whether the new location fix is newer or older
        long timeDelta = location.getTime() - currentBestLocation.getTime();
        boolean isSignificantlyNewer = timeDelta > TWO_MINUTES;
        boolean isSignificantlyOlder = timeDelta < -TWO_MINUTES;
        boolean isNewer = timeDelta > 0;

        // If it's been more than two minutes since the current location, use the new location
        // because the user has likely moved
        if (isSignificantlyNewer) {
            return true;
        // If the new location is more than two minutes older, it must be worse
        } else if (isSignificantlyOlder) {
            return false;
        }

        // Check whether the new location fix is more or less accurate
        int accuracyDelta = (int) (location.getAccuracy() - currentBestLocation.getAccuracy());
        boolean isLessAccurate = accuracyDelta > 0;
        boolean isMoreAccurate = accuracyDelta < 0;
        boolean isSignificantlyLessAccurate = accuracyDelta > 200;

        // Check if the old and new location are from the same provider
        boolean isFromSameProvider = isSameProvider(location.getProvider(),
                currentBestLocation.getProvider());

        // Determine location quality using a combination of timeliness and accuracy
        if (isMoreAccurate) {
            return true;
        } else if (isNewer && !isLessAccurate) {
            return true;
        } else if (isNewer && !isSignificantlyLessAccurate && isFromSameProvider) {
            return true;
        }
        return false;
    }

    /** Checks whether two providers are the same */
    private boolean isSameProvider(String provider1, String provider2) {
        if (provider1 == null) {
          return provider2 == null;
        }
        return provider1.equals(provider2);
    }
    

Cómo ajustar el modelo para ahorrar batería e intercambio de datos

Cuando pruebes tu app, es posible que el modelo para proporcionar una buena ubicación y un buen rendimiento necesite algunos ajustes. A continuación, te mostramos lo que puedes cambiar para encontrar un buen equilibrio entre ambos.

Reduce el tamaño de la ventana

Una ventana de detección de actualizaciones de ubicación más pequeña implica menos interacción con el GPS y los servicios de ubicación de red, lo que conserva la duración de la batería. Sin embargo, también permite que haya menos ubicaciones para elegir una mejor estimación.

Configura los proveedores de ubicación para que muestren actualizaciones con menos frecuencia

Reducir la frecuencia con la que aparecen nuevas actualizaciones durante la ventana también puede mejorar la eficiencia de la batería, aunque a costa de la precisión. Esta pérdida de precisión depende de cómo se use tu app. Puedes reducir la frecuencia de actualizaciones aumentando los parámetros de requestLocationUpdates() que especifican el tiempo de intervalo y el cambio de distancia mínimo.

Restringe los proveedores

Según el entorno en el que se usa tu app o el nivel de precisión deseado, puedes optar por usar solo el proveedor de ubicación de red o solo el GPS, en lugar de ambos. La interacción con solo uno de los servicios reduce el uso de la batería, pero es posible que se pierda precisión.

Casos de apps comunes

Es posible que quieras obtener la ubicación del usuario en tu app por diferentes motivos. A continuación, se presentan algunos escenarios en los que puedes usar la ubicación del usuario para enriquecer tu app. Además, para cada escenario se describen prácticas recomendadas sobre cuándo debes comenzar a detectar la ubicación y cuándo debes dejar de hacerlo, a fin de obtener una buena lectura y ayudar a conservar la duración de la batería.

Cómo etiquetar contenido creado por el usuario con una ubicación

Es posible que estés creando una app en la que el contenido creado por el usuario esté etiquetado con una ubicación. Piensa en los usuarios que comparten sus experiencias locales, publican una opinión sobre un restaurante o registran contenido que se puede ampliar con su ubicación actual. En la Figura 2, se muestra un modelo de cómo podría ocurrir esta interacción con respecto a los servicios de ubicación.

Figura 2: Una línea de tiempo que representa la ventana en la que se obtiene la ubicación del usuario y se detiene la detección cuando el usuario consume la ubicación actual

Esto se relaciona con el modelo anterior sobre cómo se obtiene la ubicación del usuario en el código (Figura 1). Para mejorar la precisión de la ubicación, puedes comenzar a detectar actualizaciones de ubicación cuando los usuarios comienzan a crear el contenido o incluso cuando se inicia la app. Luego, deja de detectar actualizaciones cuando el contenido esté listo para publicarse o grabarse. Es posible que debas considerar cuánto tiempo tarda la tarea típica de crear contenido y juzgar si esta duración permite la recopilación eficiente de una estimación de ubicación.

Cómo ayudar al usuario a decidir adónde ir

Es posible que estés creando una app que intente proporcionar a los usuarios un conjunto de opciones sobre adónde ir. Por ejemplo, tal vez quieras ofrecer una lista de restaurantes, tiendas y lugares de entretenimiento cercanos, y el orden de las recomendaciones cambia según la ubicación del usuario.

Para adaptar un flujo de este tipo, tienes las siguientes opciones:

  • Reordenar las recomendaciones cuando se obtenga una nueva mejor estimación.
  • Dejar de detectar actualizaciones si se estabilizó el orden de las recomendaciones.

Este tipo de modelo se muestra en la Figura 3.

Cronograma de eventos donde se muestran datos de ubicación que mejoran de forma iterativa

Figura 3: Una línea de tiempo que representa la ventana en la que se actualiza un conjunto dinámico de datos cada vez que se actualiza la ubicación del usuario

Cómo proporcionar datos de ubicación ficticia

A medida que desarrolles tu app, seguramente deberás probar con cuánta precisión obtiene tu modelo la ubicación del usuario. Esto es más fácil con un dispositivo real con Android. Sin embargo, si no tienes un dispositivo, puedes probar las funciones basadas en la ubicación con datos de ubicación ficticia en el emulador de Android. Puedes enviar a tu app este tipo de datos mediante la opción de ubicación ficticia incluida en las opciones para desarrolladores del dispositivo o mediante el comando geo en la consola del emulador.

Nota: Proporcionar datos de ubicación ficticia funciona como con datos de ubicación de GPS, por lo que debes solicitar actualizaciones de ubicación de GPS_PROVIDER para que funcionen los datos de ubicación ficticia.

Cómo usar opciones para desarrolladores

En tu dispositivo, habilita las opciones para desarrolladores y la depuración por USB; luego, sigue las instrucciones para usar la opción Seleccionar app de ubicación ficticia.

Usa el comando geo en la consola del emulador

Para enviar datos de ubicación ficticia desde la línea de comandos, haz lo siguiente:

  1. Inicia tu app en el emulador de Android y abre una terminal/consola en el directorio /tools del SDK.
  2. Conéctate a la consola del emulador:
    telnet localhost <console-port>
  3. Envía los datos de ubicación:
    • geo fix para enviar una ubicación geográfica fija.

      Este comando acepta una longitud y una latitud en grados decimales, y una altitud opcional en metros. Por ejemplo:

      geo fix -121.45356 46.51119 4392
    • geo nmea para enviar una oración NMEA 0183.

      Este comando acepta una sola oración NMEA de tipo '$GPGGA' (datos fijos) o '$GPRMC' (datos del transporte público). Por ejemplo:

      geo nmea $GPRMC,081836,A,3751.65,S,14507.36,E,000.0,360.0,130998,011.3,E*62

Para obtener información sobre cómo conectarte a la consola del emulador, consulta Cómo usar la consola del emulador.