Cómo crear y supervisar el geovallado

El geovallado combina el conocimiento de la ubicación actual del usuario con el conocimiento de la proximidad del usuario a ubicaciones que podrían ser de su interés. Para marcar una ubicación de interés, especifica su latitud y longitud. A fin de ajustar la proximidad de la ubicación, agrega un radio. La latitud, la longitud y el radio definen una geovalla, que crea un área circular, o valla, alrededor de la ubicación de interés.

Puedes tener varias geovallas activas, con un límite de 100 por app y por usuario de dispositivo. Para cada geovalla, puedes pedirles a los Servicios de ubicación que te envíen los eventos de entrada y salida, puedes especificar una duración en el área de la geovalla para esperar, o permanecer, antes de activar un evento. Con el objetivo de limitar la duración de una geovalla, puedes especificar un vencimiento en milésimas de segundos. Una vez que vence la geovalla, los Servicios de ubicación la quitan automáticamente.

En esta lección, se muestra cómo agregar y quitar geovallas y, luego, detectar transiciones de geovallas con un BroadcastReceiver.

Configura la supervisión de geovallas

El primer paso para solicitar la supervisión de geovallas consiste en obtener los permisos necesarios. Para usar geovallado, tu app debe solicitar lo siguiente:

Para obtener más información, consulta la guía sobre cómo solicitar permisos de ubicación.

Si deseas usar un BroadcastReceiver para escuchar las transiciones de geovallas, agrega un elemento que especifique el nombre del servicio. Este elemento debe ser un elemento secundario de <application>:

<application
   android:allowBackup="true">
   ...
   <receiver android:name=".GeofenceBroadcastReceiver"/>
<application/>

Para acceder a las API de ubicación, debes crear una instancia del cliente de geovallado. Si deseas obtener información sobre cómo conectar tu cliente, consulta el código que aparece a continuación:

Kotlin

lateinit var geofencingClient: GeofencingClient

override fun onCreate(savedInstanceState: Bundle?) {
    // ...
    geofencingClient = LocationServices.getGeofencingClient(this)
}

Java

private GeofencingClient geofencingClient;

@Override
public void onCreate(Bundle savedInstanceState) {
    // ...
    geofencingClient = LocationServices.getGeofencingClient(this);
}

Crea y agrega geovallas

Tu app necesita crear y agregar geovallas mediante la clase del compilador de la API de ubicación a fin de crear objetos de geovallas, y mediante la clase de conveniencia para agregarlos. Además, a fin de administrar los intents enviados desde los Servicios de ubicación cuando se produce una transición de geovallas, puedes definir un PendingIntent como se muestra en esta sección.

Nota: En los dispositivos para un solo usuario, hay un límite de 100 geovallas por app. En el caso de los dispositivos para varios usuarios, el límite es de 100 geovallas por app, por usuario del dispositivo.

Crea objetos de geovallas

Primero, usa Geofence.Builder a fin de crear una geovalla y configura el radio, la duración y los tipos de transición deseados. Por ejemplo, para propagar un objeto de lista:

Kotlin

geofenceList.add(Geofence.Builder()
        // Set the request ID of the geofence. This is a string to identify this
        // geofence.
        .setRequestId(entry.key)

        // Set the circular region of this geofence.
        .setCircularRegion(
                entry.value.latitude,
                entry.value.longitude,
                Constants.GEOFENCE_RADIUS_IN_METERS
        )

        // Set the expiration duration of the geofence. This geofence gets automatically
        // removed after this period of time.
        .setExpirationDuration(Constants.GEOFENCE_EXPIRATION_IN_MILLISECONDS)

        // Set the transition types of interest. Alerts are only generated for these
        // transition. We track entry and exit transitions in this sample.
        .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER or Geofence.GEOFENCE_TRANSITION_EXIT)

        // Create the geofence.
        .build())

Java

geofenceList.add(new Geofence.Builder()
    // Set the request ID of the geofence. This is a string to identify this
    // geofence.
    .setRequestId(entry.getKey())

    .setCircularRegion(
            entry.getValue().latitude,
            entry.getValue().longitude,
            Constants.GEOFENCE_RADIUS_IN_METERS
    )
    .setExpirationDuration(Constants.GEOFENCE_EXPIRATION_IN_MILLISECONDS)
    .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER |
            Geofence.GEOFENCE_TRANSITION_EXIT)
    .build());

En este ejemplo, se extraen datos de un archivo de constantes. En la práctica, las apps podrían crear geovallas de manera dinámica en función de la ubicación del usuario.

Especifica geovallas y activadores iniciales

En el siguiente fragmento, se usa la clase GeofencingRequest y su clase GeofencingRequestBuilder anidada para especificar los geovallados que se deben supervisar y para configurar cómo se activan los eventos de geovallas relacionados:

Kotlin

private fun getGeofencingRequest(): GeofencingRequest {
    return GeofencingRequest.Builder().apply {
        setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER)
        addGeofences(geofenceList)
    }.build()
}

Java

private GeofencingRequest getGeofencingRequest() {
    GeofencingRequest.Builder builder = new GeofencingRequest.Builder();
    builder.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER);
    builder.addGeofences(geofenceList);
    return builder.build();
}

En este ejemplo, se muestra el uso de dos activadores de geovallas. Se activa la transición GEOFENCE_TRANSITION_ENTER cuando un dispositivo ingresa a una geovalla y se activa la transición GEOFENCE_TRANSITION_EXIT cuando un dispositivo sale de una. Si especificas INITIAL_TRIGGER_ENTER, se les indica a los Servicios de ubicación que GEOFENCE_TRANSITION_ENTER debería activarse si el dispositivo ya está dentro de la geovalla.

En muchos casos, conviene usar INITIAL_TRIGGER_DWELL, que activa eventos solo cuando el usuario se detiene en una geovalla durante un tiempo definido. Este enfoque puede ayudar a reducir el "spam de alertas" que se genera a partir de las grandes cantidades de notificaciones recibidas cuando un dispositivo entra a una geovalla y sale de ella rápidamente. Otra estrategia para obtener mejores resultados consiste en definir un radio mínimo de 100 metros. De esta manera, se tiene en cuenta la precisión de la ubicación de las redes Wi-Fi típicas y también se ayuda a reducir el consumo de batería del dispositivo.

Define un receptor de emisión para las transiciones de geovallas

Un Intent enviado desde los Servicios de ubicación puede activar varias acciones en tu app, pero no debería iniciar una actividad ni un fragmento, ya que los componentes solo deberían quedar visibles como respuesta a la acción de un usuario. En muchos casos, un BroadcastReceiver es una manera efectiva de administrar una transición de geovallas. Un BroadcastReceiver recibe actualizaciones cuando ocurre un evento, como una transición de entrada a o de salida de una geovalla, y puede iniciar trabajo en segundo plano de ejecución prolongada.

En el siguiente fragmento, se muestra cómo definir un PendingIntent que inicia un BroadcastReceiver:

Kotlin

class MainActivity : AppCompatActivity() {

    // ...

    private val geofencePendingIntent: PendingIntent by lazy {
        val intent = Intent(this, GeofenceBroadcastReceiver::class.java)
        // We use FLAG_UPDATE_CURRENT so that we get the same pending intent back when calling
        // addGeofences() and removeGeofences().
        PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
    }
}

Java

public class MainActivity extends AppCompatActivity {

    // ...

    private PendingIntent getGeofencePendingIntent() {
        // Reuse the PendingIntent if we already have it.
        if (geofencePendingIntent != null) {
            return geofencePendingIntent;
        }
        Intent intent = new Intent(this, GeofenceBroadcastReceiver.class);
        // We use FLAG_UPDATE_CURRENT so that we get the same pending intent back when
        // calling addGeofences() and removeGeofences().
        geofencePendingIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.
                FLAG_UPDATE_CURRENT);
        return geofencePendingIntent;
    }

Agrega geovallas

Para agregar geovallas, usa el método GeofencingClient.addGeofences(). Proporciona el objeto GeofencingRequest y el PendingIntent. En el siguiente fragmento, se demuestra el procesamiento de los resultados:

Kotlin

geofencingClient?.addGeofences(getGeofencingRequest(), geofencePendingIntent)?.run {
    addOnSuccessListener {
        // Geofences added
        // ...
    }
    addOnFailureListener {
        // Failed to add geofences
        // ...
    }
}

Java

geofencingClient.addGeofences(getGeofencingRequest(), getGeofencePendingIntent())
        .addOnSuccessListener(this, new OnSuccessListener<Void>() {
            @Override
            public void onSuccess(Void aVoid) {
                // Geofences added
                // ...
            }
        })
        .addOnFailureListener(this, new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {
                // Failed to add geofences
                // ...
            }
        });

Administra las transiciones de geovallas

Cuando los Servicios de ubicación detectan que el usuario entró a una geovalla o salió de ella, envían el Intent del PendingIntent que incluiste en la solicitud para agregar geovallas. Un receptor de emisión como GeofenceBroadcastReceiver detecta que se invocó el Intent y, entonces, puede obtener el evento de geovallado a partir del intent, y determinar el tipo de transición de geovalla y cuál de las geovallas definidas se activó. El receptor de emisión puede indicarle a una app que comience a realizar trabajo en segundo plano o, si se prefiere, que envíe una notificación como resultado.

Nota: En Android 8.0 (nivel de API 26) y versiones posteriores, si se ejecuta una app en segundo plano mientras se supervisa una geovalla, el dispositivo responde a los eventos de geovallado cada algunos minutos. Si deseas obtener información sobre cómo adaptar tu app a estos límites de respuesta, consulta Límites de ubicación en segundo plano.

En el siguiente fragmento, se muestra cómo definir un BroadcastReceiver que publique una notificación cuando se produzca una transición de geovalla. Cuando el usuario hace clic en la notificación, aparece la actividad principal de la app:

Kotlin

class GeofenceBroadcastReceiver : BroadcastReceiver() {
    // ...
    override fun onReceive(context: Context?, intent: Intent?) {
        val geofencingEvent = GeofencingEvent.fromIntent(intent)
        if (geofencingEvent.hasError()) {
            val errorMessage = GeofenceStatusCodes
                    .getStatusCodeString(geofencingEvent.errorCode)
            Log.e(TAG, errorMessage)
            return
        }

        // Get the transition type.
        val geofenceTransition = geofencingEvent.geofenceTransition

        // Test that the reported transition was of interest.
        if (geofenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER |
                geofenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT) {

            // Get the geofences that were triggered. A single event can trigger
            // multiple geofences.
            val triggeringGeofences = geofencingEvent.triggeringGeofences

            // Get the transition details as a String.
            val geofenceTransitionDetails = getGeofenceTransitionDetails(
                    this,
                    geofenceTransition,
                    triggeringGeofences
            )

            // Send notification and log the transition details.
            sendNotification(geofenceTransitionDetails)
            Log.i(TAG, geofenceTransitionDetails)
        } else {
            // Log the error.
            Log.e(TAG, getString(R.string.geofence_transition_invalid_type,
                    geofenceTransition))
        }
    }
}

Java

public class GeofenceBroadcastReceiver extends BroadcastReceiver {
    // ...
    protected void onReceive(Context context, Intent intent) {
        GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
        if (geofencingEvent.hasError()) {
            String errorMessage = GeofenceStatusCodes
                    .getStatusCodeString(geofencingEvent.getErrorCode());
            Log.e(TAG, errorMessage);
            return;
        }

        // Get the transition type.
        int geofenceTransition = geofencingEvent.getGeofenceTransition();

        // Test that the reported transition was of interest.
        if (geofenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER ||
                geofenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT) {

            // Get the geofences that were triggered. A single event can trigger
            // multiple geofences.
            List<Geofence> triggeringGeofences = geofencingEvent.getTriggeringGeofences();

            // Get the transition details as a String.
            String geofenceTransitionDetails = getGeofenceTransitionDetails(
                    this,
                    geofenceTransition,
                    triggeringGeofences
            );

            // Send notification and log the transition details.
            sendNotification(geofenceTransitionDetails);
            Log.i(TAG, geofenceTransitionDetails);
        } else {
            // Log the error.
            Log.e(TAG, getString(R.string.geofence_transition_invalid_type,
                    geofenceTransition));
        }
    }
}

Después de detectar el evento de transición con el PendingIntent, el BroadcastReceiver obtiene el tipo de transición de geovalla y verifica si es uno de los eventos que la app usa para activar notificaciones, ya sea GEOFENCE_TRANSITION_ENTER o GEOFENCE_TRANSITION_EXIT en este caso. Luego, el servicio envía una notificación y registra los detalles de la transición.

Detén la supervisión de geovallas

Detener la supervisión de geovallas cuando ya no sea necesaria puede ayudar a ahorrar batería y ciclos de CPU en el dispositivo. Puedes detener la supervisión de geovallas en la actividad principal utilizada para agregar y quitar geovallas. Cuando lo haces, estas se detienen inmediatamente. La API proporciona métodos para quitar geovallados, ya sea con IDs de solicitud, o bien borrando geovallados asociados con un PendingIntent determinado.

En el siguiente fragmento, se quitan las geovallas mediante PendingIntent y se detienen todas las notificaciones que se reciben cuando el dispositivo entra a geovallas agregadas anteriormente o sale de ellas:

Kotlin

geofencingClient?.removeGeofences(geofencePendingIntent)?.run {
    addOnSuccessListener {
        // Geofences removed
        // ...
    }
    addOnFailureListener {
        // Failed to remove geofences
        // ...
    }
}

Java

geofencingClient.removeGeofences(getGeofencePendingIntent())
        .addOnSuccessListener(this, new OnSuccessListener<Void>() {
            @Override
            public void onSuccess(Void aVoid) {
                // Geofences removed
                // ...
            }
        })
        .addOnFailureListener(this, new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {
                // Failed to remove geofences
                // ...
            }
        });

Puedes combinar el geovallado con otras funciones que reconocen la ubicación, como las actualizaciones de ubicación periódicas. Para obtener más información, consulta las otras lecciones de esta clase.

Usa prácticas recomendadas para el geovallado

En esta sección, se describen recomendaciones que te permitirán usar el geovallado con las API de ubicación de Android.

Reduce el consumo de energía

Puedes usar las siguientes técnicas a fin de optimizar el consumo de energía en las apps que usan el geovallado:

  • Establece la capacidad de respuesta de las notificaciones en un valor superior. De esta manera, podrás reducir el consumo de energía si aumentas la latencia de las alertas de geovallas. Por ejemplo, si estableces un valor de capacidad de respuesta de cinco minutos, tu app solo verificará una alerta de entrada o salida cada cinco minutos. Establecer valores inferiores no necesariamente quiere decir que los usuarios recibirán notificaciones en ese período de tiempo (por ejemplo, si estableces un valor de 5 segundos, podría ser necesario un poco más de tiempo para recibir la alerta).

  • Usa un radio de geovalla más amplio para las ubicaciones en las que un usuario pasa una cantidad significativa de tiempo, como su casa o el trabajo. Aunque un radio más amplio no reduce el consumo de energía directamente, sí reduce la frecuencia con la que una app verifica la entrada o la salida, lo que reduce el consumo de energía general.

Elige el radio óptimo para tu geovalla

Para obtener mejores resultados, debes definir el radio mínimo de la geovalla entre 100 y 150 metros. Cuando hay una conexión Wi-Fi disponible, por lo general, la precisión de la ubicación es de 20 a 50 metros. Cuando está disponible la ubicación en interiores, el rango de precisión puede ser de apenas 5 metros. A menos que sepas que la ubicación en interiores está disponible dentro de la geovalla, debes suponer que la precisión de la ubicación de Wi-Fi es de alrededor de 50 metros.

Cuando no está disponible la ubicación de Wi-Fi (por ejemplo, cuando conduces en áreas rurales), la precisión disminuye. El rango de precisión puede ser de varios cientos de metros a varios kilómetros. En casos como este, debes crear geovallas con un radio más amplio.

Explica a los usuarios por qué tu app usa geovallado

Debido a que tu app accede a la ubicación en segundo plano cuando usas geovallado, ten en cuenta de qué manera ofrece beneficios a los usuarios. Explica claramente por qué tu app necesita este acceso a fin de aumentar la comprensión de los usuarios y proporcionar transparencia.

Para obtener más información sobre las prácticas recomendadas relacionadas con el acceso a la ubicación, incluido el geovallado, consulta la página de prácticas recomendadas de privacidad.

Cómo usar el tipo de transición de permanencia para reducir el spam de alertas

Si recibes una cantidad considerable de alertas mientras conduces durante algunos instantes por una geovalla, la manera más efectiva de reducirlas consiste en usar un tipo de transición GEOFENCE_TRANSITION_DWELL, en lugar de GEOFENCE_TRANSITION_ENTER. De esta manera, solo se envía la alerta de permanencia cuando el usuario se detiene en una geovalla durante un período determinado. Para elegir la duración, define un retraso de merodeo.

Vuelve a registrar geovallas solo cuando sea necesario

Se guardan las geovallas registradas en el proceso com.google.process.location que le pertenece al paquete com.google.android.gms. Como el sistema restaura las geovallas después de los siguientes eventos, la app no necesita ninguna acción de tu parte para administrarlos:

  • Se actualizan los servicios de Google Play.
  • El sistema finaliza los servicios de Google Play y los reinicia debido a una restricción de recursos.
  • Falla el proceso de ubicación.

La app debe volver a registrar geovallas si todavía son necesarias después de los siguientes eventos, ya que el sistema no puede recuperarlas en los siguientes casos:

  • Se reinicia el dispositivo. La app debería detectar que se completó el inicio del dispositivo y, luego, volver a registrar los geovallados requeridos.
  • Se desinstala y se vuelve a instalar la app.
  • Se borran los datos de la app.
  • Se borran los datos de los servicios de Google Play.
  • La app recibió una alerta de GEOFENCE_NOT_AVAILABLE. Esto suele suceder cuando se inhabilita el NLP (proveedor de ubicación de red de Android, según sus siglas en inglés).

Soluciona problemas relacionados con el evento de entrada a la geovalla

Si no se activan las geovallas cuando el dispositivo entra a una (no se activa la alerta GEOFENCE_TRANSITION_ENTER), primero asegúrate de que estén registradas de manera correcta como se describe en esta guía.

Estos son algunos de los motivos por los que posiblemente no funcionen las alertas según lo esperado:

  • No está disponible la ubicación precisa dentro de tu geovalla o bien la geovalla es demasiado pequeña. En la mayoría de los dispositivos, el servicio de geovallas solo usa la ubicación de la red para activar las geovallas. El servicio utiliza este enfoque debido a que la ubicación de la red consume mucha menos energía, se necesita menos tiempo para obtener ubicaciones discretas y, lo que es más importante, está disponible en interiores.
  • La conexión Wi-Fi está desactivada en el dispositivo. Tener la red Wi-Fi activada puede mejorar significativamente la precisión de la ubicación. Por lo tanto, si la red está desactivada, es posible que tu aplicación nunca reciba alertas de geovallas en función de varias opciones de configuración, como el radio de la geovalla, el modelo del dispositivo o la versión de Android. A partir de Android 4.3 (nivel 18 de API), agregamos la capacidad de "modo de solo búsqueda de Wi-Fi", que les permite a los usuarios inhabilitar la conexión Wi-Fi sin dejar de obtener una buena ubicación de la red. Se recomienda proporcionarle al usuario una combinación de teclas para habilitar la red Wi-Fi o el modo de solo búsqueda de Wi-Fi si ambos están inhabilitados. Usa SettingsClient para asegurarte de que la configuración del sistema del dispositivo sea la correcta para una detección óptima de la ubicación.

    Nota: Si tu app se orienta a Android 10 (nivel de API 29) o versiones posteriores, no podrás llamar directamente a WifiManager.setEnabled(), a menos que tu app sea una app del sistema o un controlador de política de dispositivo (DPC). En su lugar, usa el panel de configuración.

  • No hay conexión de red confiable dentro de la geovalla. Si no hay una conexión de datos confiable, es posible que no se generen alertas. Esto se debe a que el servicio de geovallas depende del proveedor de ubicación de red que, a su vez, requiere una conexión de datos.
  • Las alertas pueden llegar tarde. El servicio de geovallas no consulta la ubicación de manera continua. Por lo tanto, puedes experimentar latencia cuando recibes las alertas. La latencia suele ser de menos de 2 minutos, o incluso menos cuando el dispositivo estuvo en movimiento. Si los límites de la ubicación en segundo plano están activados, la latencia es de 2 a 3 minutos, en promedio. Si el dispositivo estuvo inmóvil durante un período considerable, la latencia podría aumentar (hasta 6 minutos).

Recursos adicionales

Para obtener más información sobre el geovallado, consulta los siguientes materiales:

Ejemplos

App de ejemplo para crear y supervisar geovallas.