Criar e monitorar regiões geográficas

A fronteira geográfica virtual combina o conhecimento da localização atual do usuário com o da proximidade do usuário em relação a locais que podem ser interessantes para ele. Para marcar um local de interesse, especifique a latitude e longitude. Para ajustar a proximidade do local, adicione um raio. A latitude, a longitude e o raio definem uma fronteira geográfica virtual, criando uma área circular, ou fronteira, ao redor do local de interesse.

Você pode ter várias fronteiras geográficas virtuais ativas, com um limite de 100 por app para cada usuário do dispositivo. É possível solicitar que os Serviços de localização enviem eventos de entrada e saída para cada uma delas, ou especificar uma duração na área da fronteira geográfica virtual para aguardar ou permanecer antes de acionar um evento. Você pode limitar a duração de qualquer fronteira geográfica virtual especificando a duração antes da expiração em milissegundos. Depois que a fronteira geográfica expira, os Serviços de localização a removem automaticamente.

Esta lição mostra como adicionar e remover fronteiras geográficas virtuais e, em seguida, detectar transições entre fronteiras usando um BroadcastReceiver.

Observação:em dispositivos Wear, as APIs de fronteira geográfica virtual não fazem uso eficiente de energia. Não recomendamos essas APIs no Wear. Consulte Economizar energia e bateria para mais informações.

Configurar o monitoramento de fronteiras geográficas virtuais

A primeira etapa para solicitar o monitoramento da fronteira geográfica virtual é solicitar as permissões necessárias. Para usar a fronteira geográfica virtual, seu app precisa solicitar o seguinte:

Para saber mais, consulte o guia sobre como solicitar permissões de localização.

Caso queira usar um BroadcastReceiver para detectar transições de fronteira geográfica virtual, adicione um elemento que especifique o nome do serviço. Esse elemento precisa ser um filho de um elemento <application>:

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

Para acessar as APIs Location, crie uma instância do cliente de fronteira geográfica virtual. Para saber como conectar seu cliente:

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);
}

Criar e adicionar fronteiras geográficas virtuais

Seu app precisa criar e adicionar fronteiras geográficas virtuais usando a classe builder da API Location para criar objetos de fronteira geográfica e a classe de conveniência para adicioná-los. Além disso, para gerenciar as intents enviadas a partir dos Serviços de localização quando as transições de fronteira geográfica virtual ocorrerem, defina uma PendingIntent conforme mostrado nesta seção.

Observação: em dispositivos de usuário único, há um limite de cem fronteiras por app. Para dispositivos multiusuários, o limite é de cem fronteiras por app por usuário.

Criar objetos de fronteira geográfica virtual

Primeiro, use Geofence.Builder para criar uma fronteira geográfica virtual, configurando o raio, a duração e os tipos de transição desejados para ela. Por exemplo, para preencher um 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());

Esse exemplo extrai dados de um arquivo de constantes. Na prática, os apps podem criar fronteiras geográficas virtuais de forma dinâmica com base na localização do usuário.

Especificar fronteiras geográficas virtuais e acionadores iniciais

O snippet a seguir usa a classe GeofencingRequest e a classe aninhada GeofencingRequestBuilder para especificar as fronteiras geográficas virtuais a serem monitoradas e definir como eventos de fronteira geográfica relacionados são acionados:

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();
}

Esse exemplo mostra o uso de dois acionadores de fronteiras geográficas virtuais. A transição GEOFENCE_TRANSITION_ENTER é acionada quando um dispositivo entra em uma fronteira geográfica virtual, e a transição GEOFENCE_TRANSITION_EXIT é acionada quando um dispositivo sai de uma. A especificação de INITIAL_TRIGGER_ENTER informa aos Serviços de localização que GEOFENCE_TRANSITION_ENTER precisa ser acionado se o dispositivo já estiver dentro da fronteira geográfica virtual.

Em muitos casos, pode ser preferível usar INITIAL_TRIGGER_DWELL, que aciona eventos somente quando o usuário permanece dentro de uma fronteira geográfica virtual por um período definido. Essa abordagem pode ajudar a reduzir o "spam de alertas" resultante de um grande número de notificações quando um dispositivo entra e sai brevemente das fronteiras geográficas virtuais. Outra estratégia para ter os melhores resultados com suas fronteiras é definir um raio mínimo de 100 metros. Isso ajuda na precisão da localização das redes Wi-Fi típicas e também reduz o consumo de energia do dispositivo.

Definir um broadcast receiver para transições de fronteira geográfica virtual

Uma Intent enviada dos Serviços de localização pode acionar várias ações no seu app, mas é importante que você não faça com que ela inicie uma atividade ou fragmento, já que componentes só ficarão visíveis em resposta a uma ação do usuário. Em muitos casos, um BroadcastReceiver é uma boa forma de gerenciar uma transição de fronteira geográfica virtual. Um BroadcastReceiver recebe atualizações quando um evento ocorre, como uma transição de entrada ou saída de uma fronteira geográfica virtual, e pode iniciar um trabalho em segundo plano de longa execução.

O snippet a seguir mostra como definir uma PendingIntent que inicia um 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;
    }

Adicionar fronteiras geográficas virtuais

Para adicionar fronteiras geográficas virtuais, use o método GeofencingClient.addGeofences(). Forneça o objeto GeofencingRequest e o PendingIntent. O snippet a seguir demonstra o processamento dos 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
                // ...
            }
        });

Gerenciar transições de fronteira geográfica virtual

Quando os Serviços de localização detectam que o usuário entrou ou saiu de uma fronteira geográfica virtual, eles enviam a Intent contida na PendingIntent que você incluiu na solicitação para adicionar fronteiras. Um broadcast receiver como GeofenceBroadcastReceiver detecta que a Intent foi invocada e pode então receber o evento de fronteira geográfica virtual da intent, determinar o tipo de transição de fronteira e determinar qual das fronteiras definidas foi acionada. O broadcast receiver pode direcionar o app para começar a executar o trabalho em segundo plano ou, se desejado, enviar uma notificação como saída.

Observação: no Android 8.0 (API de nível 26) e versões mais recentes, se um app estiver sendo executado em segundo plano durante o monitoramento de uma fronteira geográfica virtual, o dispositivo responderá a eventos de fronteira a cada dois minutos. Para saber como adaptar seu app a esses limites de resposta, consulte Limites da localização em segundo plano.

O snippet a seguir mostra como definir um BroadcastReceiver que publica uma notificação quando ocorre uma transição de fronteira geográfica virtual. Quando o usuário clica na notificação, a atividade principal do app é exibida:

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));
        }
    }
}

Depois de detectar o evento de transição pela PendingIntent, o BroadcastReceiver recebe o tipo de transição de fronteira geográfica virtual e testa se é um dos eventos que o app usa para acionar notificações, nesse caso, tanto GEOFENCE_TRANSITION_ENTER quanto GEOFENCE_TRANSITION_EXIT. Então, o serviço envia uma notificação e registra os detalhes da transição.

Parar o monitoramento da fronteira geográfica virtual

A interrupção do monitoramento de fronteira geográfica virtual quando ele não é mais necessário pode ajudar a economizar bateria e os ciclos de CPU do dispositivo. É possível parar o monitoramento de fronteira geográfica virtual na atividade principal usada para adicionar e remover fronteiras. A remoção de uma fronteira interrompe o monitoramento imediatamente. A API oferece métodos para remover fronteiras geográficas virtuais com IDs de solicitação ou por meio da remoção de fronteiras associadas com uma determinada PendingIntent.

O snippet a seguir remove fronteiras geográficas virtuais por PendingIntent, interrompendo todas as outras notificações quando o dispositivo entrar ou sair de fronteiras adicionadas anteriormente:

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
                // ...
            }
        });

Combine a fronteira geográfica virtual com outros recursos de reconhecimento de local, como atualizações periódicas de localização. Para mais informações, consulte as outras lições desta aula.

Usar as práticas recomendadas para fronteiras geográficas virtuais

Esta seção descreve recomendações para o uso de fronteiras geográficas virtuais com as APIs Location para Android.

Reduzir o consumo de energia

Use as técnicas a seguir para otimizar o consumo de energia nos seus apps que usam fronteira geográfica virtual:

  • Defina a capacidade de resposta a notificações como um valor mais alto. Isso melhora o consumo de energia aumentando a latência dos alertas de fronteira geográfica virtual. Por exemplo, se você definir um valor de capacidade de resposta de cinco minutos, seu app só verificará alertas de entrada e saída a cada cinco minutos. Definir valores mais baixos não significa necessariamente que os usuários serão notificados naquele período (por exemplo, se você definir um valor de cinco segundos, poderá levar mais tempo que isso para receber um alerta).

  • Use um raio de fronteira geográfica virtual maior para locais em que o usuário passa uma quantidade significativa de tempo, como em casa ou no trabalho. Embora um raio maior não reduza diretamente o consumo de energia, ele reduz a frequência com que o app verifica a entrada ou a saída, reduzindo o consumo geral de forma eficaz.

Escolher o raio ideal para sua fronteira geográfica virtual

Para melhores resultados, é importante que o raio mínimo da fronteira geográfica virtual seja definido entre 100 e 150 metros. Quando o Wi-Fi está disponível, a precisão da localização geralmente fica entre 20 e 50 metros. Quando a localização interna está disponível, o alcance da precisão pode ser de até 5 metros. A menos que você saiba que a localização interna está disponível dentro da fronteira geográfica, presuma que a precisão da localização por Wi-Fi é de cerca de 50 metros.

Quando a localização por Wi-Fi não está disponível (por exemplo, ao dirigir em áreas rurais), a precisão da localização diminui. O alcance da precisão pode ser de centenas de metros a vários quilômetros. Em casos como esse, crie fronteiras geográficas virtuais usando um raio maior.

Explicar aos usuários por que seu aplicativo usa a função fronteira geográfica virtual

Considerando que seu app acessa a localização em segundo plano ao usar a fronteira geográfica virtual, pense em como isso beneficia os usuários. Explique claramente por que seu app precisa desse acesso para aumentar a compreensão e a transparência do usuário.

Para ver mais informações sobre as práticas recomendadas em relação ao acesso à localização, incluindo fronteiras geográficas virtuais, consulte a página Práticas recomendadas de privacidade.

Usar o tipo de transição de permanência para reduzir o spam de alertas

Se você recebe um número grande de alertas ao passar rapidamente por uma fronteira geográfica virtual, a melhor forma de reduzir os alertas é usar um tipo de transição GEOFENCE_TRANSITION_DWELL em vez de GEOFENCE_TRANSITION_ENTER. Dessa forma, o alerta de permanência é enviado apenas quando o usuário para dentro de uma fronteira geográfica virtual por um determinado período. Escolha a duração definindo um atraso de espera.

Registrar fronteiras geográficas virtuais novamente apenas quando solicitado

As fronteiras geográficas virtuais registradas são mantidas no processo com.google.process.location do pacote com.google.android.gms. O app não precisa fazer nada para processar os eventos a seguir, porque o sistema restaura as fronteiras geográficas virtuais depois deles:

  • Google Play Services passou por um upgrade.
  • Google Play Services foi desativado e reiniciado pelo sistema devido a restrições de recursos.
  • O processo de localização falhou.

O app precisa registrar novamente as fronteiras geográficas virtuais caso ainda sejam necessárias depois dos eventos a seguir, já que o sistema não pode recuperar as fronteiras nos seguintes casos:

  • Se o dispositivo for reinicializado. O app detectará a ação completa de inicialização do dispositivo e registrará novamente as fronteiras necessárias.
  • Se o app for desinstalado e reinstalado.
  • Se os dados do app forem apagados.
  • Se os dados do Google Play Services forem apagados.
  • Se o app receber um alerta GEOFENCE_NOT_AVAILABLE. Isso normalmente acontece depois que o Provedor de localização de rede (NLP, na sigla em inglês) do Android é desativado.

Solucionar problemas do evento de entrada da fronteira geográfica virtual

Se as fronteiras geográficas virtuais não estiverem sendo acionadas quando o dispositivo entrar em uma fronteira (o alerta GEOFENCE_TRANSITION_ENTER não é acionado), verifique primeiro se as fronteiras estão registradas corretamente, conforme descrito neste guia.

Veja alguns possíveis motivos para que os alertas não funcionem conforme o esperado:

  • A localização exata não está disponível dentro da sua fronteira geográfica virtual ou sua fronteira é muito pequena. Na maioria dos dispositivos, o serviço de fronteira geográfica virtual usa apenas a localização da rede para acionar a fronteira. O serviço usa essa abordagem porque a localização da rede consome muito menos energia, leva menos tempo para receber localizações discretas e, o mais importante, está disponível em ambientes fechados.
  • O Wi-Fi está desativado no dispositivo. Ativar o Wi-Fi pode melhorar significativamente a precisão da localização. Portanto, se o Wi-Fi estiver desativado, seu aplicativo poderá nunca receber alertas de fronteira geográfica virtual nunca, dependendo de várias configurações, como o raio da fronteira, o modelo do dispositivo ou a versão do Android. A partir do Android 4.3 (API de nível 18), adicionamos o recurso “modo de somente busca por Wi-Fi”, que permite que os usuários desativem o Wi-Fi, mas continuem com uma boa localização de rede. É uma prática recomendada solicitar que o usuário ative o Wi-Fi ou o "modo de somente busca por Wi-Fi" se os dois estiverem desativados, assim como oferecer um atalho para isso. Use SettingsClient para garantir que as configurações do sistema do dispositivo sejam definidas adequadamente para ter uma detecção de local ideal.

    Observação: caso seu app seja voltado para o Android 10 (API de nível 29) ou versões mais recentes, não chame WifiManager.setEnabled() diretamente, a não ser que esse seja um app do sistema ou um controlador de política de dispositivo (DPC). Em vez disso, use um painel de configurações.

  • Não há uma conectividade de rede confiável dentro da sua fronteira geográfica virtual. Se não houver uma conexão de dados confiável, os alertas poderão não ser gerados. Isso ocorre porque o serviço de fronteira geográfica virtual depende do provedor de localização de rede que, por sua vez, requer uma conexão de dados.
  • Os alertas podem chegar atrasados. O serviço de fronteira geográfica virtual não consulta a localização de forma contínua. Por isso, é possível que haja certa latência ao receber alertas. Geralmente, a latência é inferior a dois minutos, ou até menos quando o dispositivo está em movimento. Se os Limites da localização em segundo plano estiverem em funcionamento, a latência será de, em média, dois a três minutos. Se o dispositivo ficar parado por um período significativo, a latência poderá aumentar (até seis minutos).

Outros recursos

Para saber mais sobre a fronteira geográfica virtual, consulte estes recursos:

Exemplos

App de exemplo (em inglês) para criação e monitoramenteo de fronteiras geográficas virtuais.