Il geofencing combina la consapevolezza della posizione attuale dell'utente con la consapevolezza della sua vicinanza a luoghi che potrebbero essere di interesse. Per contrassegnare una località di interesse, devi specificarne la latitudine e la longitudine. Per regolare la vicinanza della località, aggiungi un raggio. La latitudine, la longitudine e il raggio definiscono un geofence, creando un'area circolare, o recinto, intorno alla località di interesse.
Puoi avere più geofence attivi, con un limite di 100 per app, per utente del dispositivo. Per ogni recinto virtuale, puoi chiedere ai servizi di localizzazione di inviarti eventi di entrata e uscita oppure puoi specificare una durata di attesa o di permanenza all'interno dell'area del recinto virtuale prima di attivare un evento. Puoi limitare la durata di qualsiasi geofence specificando una durata di scadenza in millisecondi. Una volta scaduto il geofence, i servizi di localizzazione lo rimuovono automaticamente.
Questa lezione mostra come aggiungere e rimuovere i geofence e poi ascoltare le transizioni dei geofence utilizzando un BroadcastReceiver.
Nota:sui dispositivi Wear, le API Geofencing non utilizzano l'energia in modo efficiente. Sconsigliamo di utilizzare queste API su Wear. Per saperne di più, leggi l'articolo Risparmia energia e batteria.
Configura il monitoraggio del geofencing
Il primo passaggio per richiedere il monitoraggio del geofencing consiste nel richiedere le autorizzazioni necessarie. Per utilizzare il geofencing, l'app deve richiedere quanto segue:
-
ACCESS_FINE_LOCATION -
ACCESS_BACKGROUND_LOCATIONse la tua app ha come target Android 10 (livello API 29) o versioni successive
Per scoprire di più, consulta la guida su come richiedere le autorizzazioni di accesso alla posizione.
Se vuoi utilizzare un BroadcastReceiver per ascoltare le transizioni dei geofence, aggiungi un elemento che specifica il nome del servizio. Questo elemento deve essere un elemento secondario dell'elemento
<application>:
<application android:allowBackup="true"> ... <receiver android:name=".GeofenceBroadcastReceiver"/> <application/>
Per accedere alle API di geolocalizzazione, devi creare un'istanza del client Geofencing. Per scoprire come connettere il client:
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 e aggiungi geofence
La tua app deve creare e aggiungere geofence utilizzando la classe builder dell'API di geolocalizzazione per creare oggetti Geofence e la classe di convenienza per aggiungerli. Inoltre, per gestire gli intent inviati dai servizi di localizzazione quando si verificano transizioni di geofencing, puoi definire un PendingIntent come mostrato in questa sezione.
Nota:sui dispositivi per un singolo utente, esiste un limite di 100 geofence per app. Per i dispositivi multiutente, il limite è di 100 geofence per app per utente del dispositivo.
Crea oggetti geofence
Innanzitutto, utilizza
Geofence.Builder per creare un geofence, impostando il raggio, la durata e i tipi di transizione desiderati per il geofence. Ad esempio, per compilare un oggetto elenco:
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());
Questo esempio estrae i dati da un file di costanti. In pratica, le app potrebbero creare dinamicamente geofence in base alla posizione dell'utente.
Specifica i geofence e gli attivatori iniziali
Il seguente snippet utilizza la classe
GeofencingRequest
e la relativa classe nidificata
GeofencingRequestBuilder per
specificare i geofence da monitorare e impostare la modalità di attivazione degli eventi di geofence correlati:
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(); }
Questo esempio mostra l'utilizzo di due attivatori di geofencing. La transizione
GEOFENCE_TRANSITION_ENTER viene attivata quando un dispositivo entra in un recinto virtuale, mentre la transizione
GEOFENCE_TRANSITION_EXIT viene attivata quando un dispositivo esce da un recinto virtuale. Se specifichi
INITIAL_TRIGGER_ENTER, indichi ai servizi di localizzazione che
GEOFENCE_TRANSITION_ENTER deve essere attivato se il dispositivo si trova già all'interno del recinto virtuale.
In molti casi, potrebbe essere preferibile utilizzare
INITIAL_TRIGGER_DWELL, che attiva gli eventi solo quando l'utente si ferma per un periodo di tempo definito all'interno di un recinto virtuale.
Questo approccio può contribuire a ridurre lo "spam di avvisi" derivante da un numero elevato di notifiche quando un dispositivo entra ed esce brevemente dai geofence. Un'altra strategia per ottenere i migliori risultati dai tuoi geofence è impostare un raggio minimo di 100 metri. In questo modo, viene presa in considerazione la precisione della localizzazione delle reti Wi-Fi tipiche e viene ridotto il consumo energetico del dispositivo.
Definisci un broadcast receiver per le transizioni dei geofence
Un Intent inviato dai servizi di localizzazione può attivare varie azioni nella tua app, ma non deve avviare un'attività o un frammento, perché i componenti devono diventare visibili solo in risposta a un'azione dell'utente. In molti casi, un BroadcastReceiver è un buon modo per gestire una transizione di geofence. Un BroadcastReceiver riceve aggiornamenti quando si verifica un evento, ad esempio una transizione in entrata o in uscita da un recinto virtuale, e può avviare un lavoro in background di lunga durata.
Il seguente snippet mostra come definire un PendingIntent che avvia un BroadcastReceiver:
Kotlin
class MainActivity : AppCompatActivity() { // ... private val geofencePendingIntent: PendingIntent by lazy { // We use FLAG_UPDATE_CURRENT so that we get the same pending intent back when calling // addGeofences() and removeGeofences(). val flags = PendingIntent.FLAG_UPDATE_CURRENT if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) { // Starting on Android S+ the pending intent has to be mutable. flags or PendingIntent.FLAG_MUTABLE } val intent = Intent(this, GeofenceBroadcastReceiver::class.java) PendingIntent.getBroadcast(this, 0, intent, flags) } }
Java
public class MainActivity extends AppCompatActivity { // ... private PendingIntent getGeofencePendingIntent() { // Reuse the PendingIntent if we already have it. if (geofencePendingIntent != null) { return geofencePendingIntent; } // We use FLAG_UPDATE_CURRENT so that we get the same pending intent back when calling // addGeofences() and removeGeofences(). int flags = PendingIntent.FLAG_UPDATE_CURRENT; if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) { // Starting on Android S+ the pending intent has to be mutable. flags |= PendingIntent.FLAG_MUTABLE; } Intent intent = new Intent(this, GeofenceBroadcastReceiver.class); geofencePendingIntent = PendingIntent.getBroadcast(this, 0, intent, flags); return geofencePendingIntent; } }
Aggiungi geofence
Per aggiungere geofence, utilizza il metodo
.
Fornisci l'oggetto GeofencingClient.addGeofences()
GeofencingRequest e PendingIntent.
Il seguente snippet mostra l'elaborazione dei risultati:
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 // ... } });
Gestisci le transizioni di geofencing
Quando i servizi di localizzazione rilevano che l'utente è entrato o uscito da un recinto virtuale, inviano l'oggetto Intent contenuto nell'oggetto PendingIntent che hai incluso nella richiesta di aggiunta di recinti virtuali. Un broadcast receiver come GeofenceBroadcastReceiver rileva che Intent è stato richiamato e può quindi ottenere l'evento di geofencing dall'intent, determinare il tipo di transizione o transizioni del geofence e determinare quale dei geofence definiti è stato attivato. Il broadcast receiver può indirizzare un'app per iniziare a eseguire attività in background o, se lo desideri, inviare una notifica come output.
Nota: su Android 8.0 (livello API 26) e versioni successive, se un'app è in esecuzione in background mentre monitora un recinto virtuale, il dispositivo risponde agli eventi di geofencing ogni due minuti. Per scoprire come adattare la tua app a questi limiti di risposta, consulta Limiti di geolocalizzazione in background.
Il seguente snippet mostra come definire un BroadcastReceiver che pubblica una notifica quando si verifica una transizione di geofencing. Quando l'utente
fa clic sulla notifica, viene visualizzata l'attività principale dell'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)); } } }
Dopo aver rilevato l'evento di transizione tramite PendingIntent,
BroadcastReceiver recupera il tipo di transizione del geofencing e
verifica se si tratta di uno degli eventi utilizzati dall'app per attivare
le notifiche, ovvero
GEOFENCE_TRANSITION_ENTER
o GEOFENCE_TRANSITION_EXIT
in questo caso. Il servizio invia quindi una notifica e registra i dettagli della transizione.
Interrompi il monitoraggio del recinto virtuale
L'interruzione del monitoraggio del geofencing quando non è più necessario o desiderato può contribuire a risparmiare batteria e cicli della CPU sul dispositivo. Puoi interrompere il monitoraggio dei geofence nell'attività principale utilizzata per aggiungere e rimuovere i geofence. La rimozione di un geofence ne interrompe immediatamente il monitoraggio. L'API fornisce metodi per rimuovere i geofence in base agli ID richiesta o rimuovendo i geofence associati a un determinato PendingIntent.
Il seguente snippet rimuove i geofence in base a PendingIntent, interrompendo tutte le notifiche successive quando il dispositivo entra o esce dai geofence aggiunti in precedenza:
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 // ... } });
Puoi combinare il geofencing con altre funzionalità di conoscenza della posizione, come gli aggiornamenti periodici della posizione. Per saperne di più, consulta le altre lezioni di questo corso.
Utilizza le best practice per il geofencing
Questa sezione descrive i consigli per l'utilizzo del geofencing con le API di geolocalizzazione per Android.
Ridurre il consumo di energia
Puoi utilizzare le seguenti tecniche per ottimizzare il consumo energetico nelle tue app che utilizzano il geofencing:
Imposta la reattività delle notifiche su un valore più alto. In questo modo, il consumo di energia viene migliorato aumentando la latenza degli avvisi di geofencing. Ad esempio, se imposti un valore di reattività di cinque minuti, l'app controlla la presenza di un avviso di ingresso o uscita solo una volta ogni cinque minuti. L'impostazione di valori inferiori non significa necessariamente che gli utenti ricevano una notifica entro quel periodo di tempo (ad esempio, se imposti un valore di 5 secondi, la ricezione dell'avviso potrebbe richiedere un po' più di tempo).
Utilizza un raggio di geofencing più ampio per le località in cui un utente trascorre molto tempo, come casa o lavoro. Sebbene un raggio più ampio non riduca direttamente il consumo di energia, riduce la frequenza con cui l'app controlla l'ingresso o l'uscita, riducendo di fatto il consumo di energia complessivo.
Scegli il raggio ottimale per il tuo recinto virtuale
Per ottenere risultati ottimali, il raggio minimo del recinto virtuale deve essere impostato tra 100 e 150 metri. Quando è disponibile il Wi-Fi, la precisione della localizzazione è in genere compresa tra 20 e 50 metri. Quando è disponibile la posizione al chiuso, l'intervallo di precisione può essere di soli 5 metri. A meno che tu non sappia che la localizzazione indoor è disponibile all'interno del geofence, supponi che la precisione della localizzazione Wi-Fi sia di circa 50 metri.
Quando la posizione Wi-Fi non è disponibile (ad esempio, quando guidi in aree rurali), la precisione della localizzazione diminuisce. L'intervallo di precisione può variare da diverse centinaia di metri a diversi chilometri. In casi come questo, devi creare geofence utilizzando un raggio più ampio.
Spiega agli utenti perché la tua app utilizza il geofencing
Poiché la tua app accede alla posizione in background quando utilizzi il geofencing, valuta in che modo l'app offre vantaggi agli utenti. Spiega chiaramente agli utenti perché la tua app ha bisogno di questo accesso per aumentare la comprensione e la trasparenza.
Per saperne di più sulle best practice relative all'accesso alla posizione, incluso il geofencing, consulta la pagina delle best practice per la privacy.
Utilizza il tipo di transizione di permanenza per ridurre lo spam degli avvisi
Se ricevi un numero elevato di avvisi quando guidi per un breve periodo oltre un recinto virtuale, il modo migliore per ridurre gli avvisi è utilizzare un tipo di transizione
GEOFENCE_TRANSITION_DWELL anziché
GEOFENCE_TRANSITION_ENTER. In questo modo, l'avviso di sosta viene inviato solo quando l'utente si ferma all'interno di un recinto virtuale per un determinato periodo di tempo. Puoi scegliere la durata impostando un
ritardo di sosta.
Registra nuovamente i geofence solo se necessario
I geofence registrati vengono conservati nel processo com.google.process.location di proprietà del pacchetto com.google.android.gms.
L'app non deve fare nulla per gestire i seguenti eventi, perché il sistema
ripristina i geofence dopo questi eventi:
- Google Play Services è stato aggiornato.
- Google Play Services viene terminato e riavviato dal sistema a causa di una limitazione delle risorse.
- Il processo di localizzazione si arresta in modo anomalo.
L'app deve registrare nuovamente i geofence se sono ancora necessari dopo i seguenti eventi, poiché il sistema non può recuperarli nei seguenti casi:
- Il dispositivo viene riavviato. L'app deve ascoltare l'azione di avvio completo del dispositivo e poi registrare nuovamente i geofence richiesti.
- L'app viene disinstallata e reinstallata.
- I dati dell'app vengono cancellati.
- I dati di Google Play Services vengono cancellati.
- L'app ha ricevuto un avviso
GEOFENCE_NOT_AVAILABLE. In genere, questo accade dopo la disattivazione di NLP (Network Location Provider di Android).
Risolvi i problemi relativi all'evento di ingresso nel recinto virtuale
Se i geofence non vengono attivati quando il dispositivo entra in un geofence
(l'avviso
GEOFENCE_TRANSITION_ENTER non viene attivato), assicurati innanzitutto che i geofence siano
registrati correttamente come descritto in questa guida.
Ecco alcuni possibili motivi per cui gli avvisi non funzionano come previsto:
- La posizione precisa non è disponibile all'interno del recinto virtuale o il recinto virtuale è troppo piccolo. Nella maggior parte dei dispositivi, il servizio di geofencing utilizza solo la posizione di rete per l'attivazione del geofencing. Il servizio utilizza questo approccio perché la posizione di rete consuma molta meno energia, richiede meno tempo per ottenere posizioni discrete e, soprattutto, è disponibile in ambienti interni.
Il Wi-Fi è disattivato sul dispositivo. La presenza del Wi-Fi può migliorare significativamente la precisione della localizzazione, quindi se il Wi-Fi è disattivato, l'applicazione potrebbe non ricevere mai gli avvisi di geofence a seconda di diverse impostazioni, tra cui il raggio del geofence, il modello del dispositivo o la versione di Android. A partire da Android 4.3 (livello API 18), abbiamo aggiunto la funzionalità "Modalità di sola scansione Wi-Fi", che consente agli utenti di disattivare il Wi-Fi, ma di ottenere comunque una buona posizione di rete. È buona norma chiedere all'utente di attivare il Wi-Fi o la modalità di sola scansione Wi-Fi e fornire una scorciatoia per farlo se entrambe le opzioni sono disattivate. Utilizza SettingsClient per assicurarti che le impostazioni di sistema del dispositivo siano configurate correttamente per un rilevamento della posizione ottimale.
Nota : se la tua app ha come target Android 10 (livello API 29) o versioni successive, non puoi chiamare direttamente
WifiManager.setEnabled(), a meno che non si tratti di un'app di sistema o di un controller di policy per i dispositivi (DPC). Utilizza invece un riquadro delle impostazioni.- Non esiste una connessione di rete affidabile all'interno del recinto virtuale. Se non è presente una connessione dati affidabile, gli avvisi potrebbero non essere generati. Questo perché il servizio di geofencing dipende dal provider di geolocalizzazione di rete, che a sua volta richiede una connessione dati.
- Gli avvisi possono essere in ritardo. Il servizio di geofencing non esegue query continue per la posizione, quindi è prevista una certa latenza nella ricezione degli avvisi. In genere la latenza è inferiore a 2 minuti, ancora meno quando il dispositivo è in movimento. Se sono attivi i limiti di geolocalizzazione in background, la latenza è di circa 2-3 minuti in media. Se il dispositivo è rimasto fermo per un periodo di tempo significativo, la latenza potrebbe aumentare (fino a 6 minuti).
Risorse aggiuntive
Per scoprire di più sul geofencing, consulta i seguenti materiali:
Esempi
App di esempio per la creazione e il monitoraggio dei geofence.