Ricevi aggiornamenti sulla posizione in Android con Kotlin

Android 10 e 11 offrono agli utenti un maggiore controllo sulle loro app; possono accedere alle posizioni dei loro dispositivi.

Quando un'app in esecuzione su Android 11 richiede l'accesso alla posizione, gli utenti hanno quattro opzioni:

  • Consenti sempre
  • Consenti solo mentre l'app è in uso (Android 10)
  • Solo una volta (in Android 11)
  • Rifiuta

Android 10

Android 11

In questo codelab, imparerai a ricevere aggiornamenti della posizione e come supportare la posizione su qualsiasi versione di Android, in particolare Android 10 e 11. Alla fine del codelab, dovresti avere un'app che segue le best practice attuali per recuperare gli aggiornamenti della posizione.

Prerequisiti

Attività previste

  • Segui le best practice per determinare la posizione in Android.
  • Gestire le autorizzazioni di accesso alla posizione in primo piano (quando l'utente richiede che l'app acceda alla posizione del dispositivo mentre è in uso).
  • Modifica un'app esistente per aggiungere il supporto per la richiesta di accesso alla posizione aggiungendo codice per l'iscrizione e l'annullamento dell'iscrizione alla posizione.
  • Aggiungi il supporto all'app per Android 10 e 11 aggiungendo logica per accedere alla posizione nella posizione in primo piano o durante l'uso.

Che cosa ti serve

  • Android Studio 3.4 o versioni successive per eseguire il codice
  • Un dispositivo/emulatore che esegue un'anteprima per gli sviluppatori di Android 10 e 11

Clona il repository del progetto iniziale

Per iniziare il più rapidamente possibile, puoi basarti su questo progetto iniziale. Se hai installato Git, puoi semplicemente eseguire il seguente comando:

 git clone https://github.com/googlecodelabs/while-in-use-location

Non esitare a visitare direttamente la pagina di GitHub.

Se non disponi di Git, puoi scaricare il progetto come file ZIP:

Scarica zip

Importa il progetto

Apri Android Studio, seleziona "Apri un progetto Android Studio" dalla schermata di benvenuto e apri la directory del progetto.

Una volta caricato il progetto, potresti anche visualizzare un avviso che indica che Git non sta monitorando tutte le modifiche locali. Puoi fare clic su Ignora. Non respingerai le modifiche al repository Git.

Nell'angolo in alto a sinistra della finestra del progetto, dovresti vedere qualcosa di simile a quello dell'immagine seguente se ti trovi nella visualizzazione di Android. Se ti trovi nella visualizzazione Progetto, devi espanderlo per vedere la stessa cosa.

Esistono due cartelle (base e complete; ognuna è nota come "modulo").

Tieni presente che Android Studio potrebbe impiegare diversi secondi per compilare il progetto in background per la prima volta. Durante questo periodo, vedrai il seguente messaggio nella barra di stato in fondo ad Android Studio:

Attendi che Android Studio abbia finito di indicizzare e creare il progetto prima di apportare modifiche al codice. In questo modo, Android Studio è in grado di importare tutti i componenti necessari.

Se ricevi il messaggio Ricarica per applicare le modifiche alla lingua? o altro, seleziona .

Comprendere il progetto iniziale

Ora puoi configurare la posizione nell'app e iniziare a usarlo. Utilizza il modulo base come punto di partenza. Durante ogni passaggio, aggiungi il codice al modulo base. Una volta completato questo codelab, il codice del modulo base deve corrispondere ai contenuti del modulo complete. Il modulo complete può essere utilizzato per controllare il tuo lavoro o per fare riferimento a te in caso di problemi.

I componenti chiave includono:

  • MainActivity: UI per l'utente per consentire all'app di accedere alla posizione del dispositivo
  • LocationService: servizio che sottoscrive e annulla l'iscrizione alle modifiche alla posizione e si promuove a un servizio in primo piano (con una notifica) se l'utente si allontana dall'attività dell'app. Aggiungi il codice della località qui.
  • Util: aggiunge funzioni di estensione per la classe Location e salva la posizione in SharedPreferences (livello dati semplificato).

Configurazione dell'emulatore

Per informazioni sulla configurazione di un emulatore Android, vedi Eseguire su un emulatore.

Esegui il progetto iniziale

Esegui la tua app.

  1. Collega il dispositivo Android al computer o avvia un emulatore. Assicurati che sul dispositivo sia installato Android 10 o versioni successive.
  2. Nella barra degli strumenti, seleziona la configurazione di base dal selettore a discesa e fai clic su Esegui:


  1. Sul tuo dispositivo viene visualizzata la seguente app:


È possibile che nella schermata di output non venga visualizzata alcuna informazione sulla posizione. Questo perché non hai ancora aggiunto il codice di posizione.

Concetti

L'obiettivo di questo codelab è mostrarti come ricevere gli aggiornamenti della posizione e, infine, supportare Android 10 e Android 11.

Prima di iniziare a programmare, è utile rivedere le nozioni di base.

Tipi di accesso alla posizione

Potresti ricordare le quattro diverse opzioni per l'accesso alla posizione, dall'inizio del codelab. Scoprine il significato:

  • Consenti solo mentre l'app è in uso
  • Questa è l'opzione consigliata per la maggior parte delle app. Questa opzione, nota anche come "durante l'uso", o "solo in primo piano", è stata aggiunta in Android 10 e consente agli sviluppatori di recuperare la posizione soltanto mentre l'app è in uso. Un'app è considerata attiva se si verifica una delle seguenti condizioni:
  • È visibile un'attività.
  • È in esecuzione un servizio in primo piano con una notifica in corso.
  • Solo una volta
  • La funzionalità è stata aggiunta in Android 11, ed è uguale a Consenti solo mentre l'app è in uso, ma per un periodo di tempo limitato. Per scoprire di più, consulta l'articolo Autorizzazioni una tantum.
  • Nega
  • Questa opzione impedisce l'accesso alle informazioni sulla posizione.
  • Consenti sempre
  • Questa opzione consente sempre l'accesso alla posizione, ma richiede un'autorizzazione aggiuntiva per Android 10 e versioni successive. Devi inoltre assicurarti di disporre di un caso d'uso valido e di rispettare le norme sulla località. Non coprirai questa opzione in questo codelab, poiché è un caso d'uso più raro. Tuttavia, se disponi di un caso d'uso valido e vuoi capire come gestire correttamente la posizione sempre valida, incluso l'accesso alla posizione in background, esamina l'esempio LocationUpdatesBackgroundKotlin.

Servizi, servizi in primo piano e associazioni

Per supportare completamente gli aggiornamenti della posizione Consenti solo mentre utilizzi l'app, devi tenere conto del momento in cui l'utente esce dalla tua app. Se vuoi continuare a ricevere aggiornamenti in questa situazione, devi creare un Service in primo piano e associarlo a un Notification.

Inoltre, se vuoi utilizzare lo stesso Service per richiedere aggiornamenti della posizione quando l'app è visibile e quando l'utente esce dall'app, devi associare/scollegare tale Service all'elemento UI.

Poiché questo codelab si concentra solo sulla ricezione di aggiornamenti sulla posizione, puoi trovare tutto il codice di cui hai bisogno nella classe ForegroundOnlyLocationService.kt. Puoi consultare il corso e il MainActivity.kt per vedere come funzionano insieme.

Per ulteriori informazioni, vedi le sezioni Panoramica dei servizi e Panoramica dei servizi limitati.

Autorizzazioni

Per ricevere gli aggiornamenti sulla posizione da un elemento NETWORK_PROVIDER o GPS_PROVIDER, devi richiedere l'autorizzazione dell'utente dichiarando, rispettivamente, o ACCESS_FINE_LOCATION nel file manifest di Android. Senza queste autorizzazioni, la tua app non potrà richiedere l'accesso alla posizione in fase di esecuzione.

Queste autorizzazioni coprono i casi Solo una volta e Consenti solo mentre l'app è in uso quando la tua app viene utilizzata su un dispositivo Android 10 o versioni successive.

Località

La tua app può accedere all'insieme dei servizi di geolocalizzazione supportati tramite le classi del pacchetto com.google.android.gms.location.

Guarda le classi principali:

  • FusedLocationProviderClient
  • È il componente centrale del framework della località. Una volta creato, puoi utilizzarlo per richiedere gli aggiornamenti sulla posizione e ottenere l'ultima posizione nota.
  • LocationRequest
  • Si tratta di un oggetto dati che contiene parametri di qualità del servizio per le richieste (intervalli di aggiornamenti, priorità e accuratezza). Viene trasmesso al FusedLocationProviderClient quando richiedi gli aggiornamenti della posizione.
  • LocationCallback
  • Viene utilizzato per ricevere notifiche quando la posizione del dispositivo è cambiata o non può più essere determinata. È stato superato un LocationResult in cui puoi scaricare la Location per salvare nel tuo database.

Ora che hai un'idea di base su cosa stai facendo, inizia con il codice!

Questo codelab è incentrato sull'opzione di località più comune: Consenti solo mentre l'app è in uso.

Per ricevere gli aggiornamenti sulla posizione, la tua app deve avere un'attività visibile o un servizio in esecuzione in primo piano (con una notifica).

Autorizzazioni

Lo scopo di questo codelab è mostrare come ricevere gli aggiornamenti della posizione, non come richiedere le autorizzazioni di accesso alla posizione, quindi il codice basato su autorizzazione è già stato scritto per te. Non esitare a ignorare l'annuncio se lo conosci già.

Di seguito vengono riportate le autorizzazioni principali (per questa parte non sono richieste azioni):

  1. Dichiara l'autorizzazione che utilizzi in AndroidManifest.xml.
  2. Prima di tentare di accedere alle informazioni sulla posizione, controlla se l'utente ha concesso l'autorizzazione alla tua app. Se la tua app non ha ancora ricevuto l'autorizzazione, richiedi l'accesso.
  3. Gestisci la scelta delle autorizzazioni dell'utente. Puoi visualizzare questo codice nel MainActivity.kt.

Se cerchi TODO: Step 1.0, Review Permissions in AndroidManifest.xml o MainActivity.kt, viene visualizzato tutto il codice scritto per le autorizzazioni.

Per ulteriori informazioni, consulta la panoramica sulle autorizzazioni.

Ora inizia a scrivere del codice di posizione.

Esamina le variabili chiave necessarie per gli aggiornamenti della località

Nel modulo base, cerca TODO: Step 1.1, Review variables nella

ForegroundOnlyLocationService.kt.

In questo passaggio non è necessario alcun intervento. Devi solo esaminare il seguente blocco di codice, insieme ai commenti, per capire le classi e le variabili chiave che utilizzi per ricevere gli aggiornamenti sulla posizione.

// TODO: Step 1.1, Review variables (no changes).
// FusedLocationProviderClient - Main class for receiving location updates.
private lateinit var fusedLocationProviderClient: FusedLocationProviderClient

// LocationRequest - Requirements for the location updates, i.e., how often you
// should receive updates, the priority, etc.
private lateinit var locationRequest: LocationRequest

// LocationCallback - Called when FusedLocationProviderClient has a new Location.
private lateinit var locationCallback: LocationCallback

// Used only for local storage of the last known location. Usually, this would be saved to your
// database, but because this is a simplified sample without a full database, we only need the
// last location to create a Notification if the user navigates away from the app.
private var currentLocation: Location? = null

Esamina l'inizializzazione di FusedLocationProviderClient

Nel modulo base, cerca TODO: Step 1.2, Review the FusedLocationProviderClient nel file ForegroundOnlyLocationService.kt. Il tuo codice sarà simile al seguente:

// TODO: Step 1.2, Review the FusedLocationProviderClient.
fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this)

Come indicato nei commenti precedenti, questa è la classe principale per ricevere aggiornamenti sulla posizione. La variabile è già stata inizializzata, ma è importante rivedere il codice per capire come viene inizializzato. In seguito aggiungi un codice per richiedere gli aggiornamenti sulla posizione.

Inizializza la richiesta di posizione

  1. Nel modulo base, cerca TODO: Step 1.3, Create a LocationRequest nel file ForegroundOnlyLocationService.kt.
  2. Aggiungi il seguente codice dopo il commento.

Il codice di inizializzazione di LocationRequest aggiunge la qualità aggiuntiva dei parametri di servizio che ti servono per la tua richiesta (intervalli, tempo di attesa massimo e priorità).

// TODO: Step 1.3, Create a LocationRequest.
locationRequest = LocationRequest().apply {
   // Sets the desired interval for active location updates. This interval is inexact. You
   // may not receive updates at all if no location sources are available, or you may
   // receive them less frequently than requested. You may also receive updates more
   // frequently than requested if other applications are requesting location at a more
   // frequent interval.
   //
   // IMPORTANT NOTE: Apps running on Android 8.0 and higher devices (regardless of
   // targetSdkVersion) may receive updates less frequently than this interval when the app
   // is no longer in the foreground.
   interval = TimeUnit.SECONDS.toMillis(60)

   // Sets the fastest rate for active location updates. This interval is exact, and your
   // application will never receive updates more frequently than this value.
   fastestInterval = TimeUnit.SECONDS.toMillis(30)

   // Sets the maximum time when batched location updates are delivered. Updates may be
   // delivered sooner than this interval.
   maxWaitTime = TimeUnit.MINUTES.toMillis(2)

   priority = LocationRequest.PRIORITY_HIGH_ACCURACY
}
  1. Leggi i commenti per comprenderne il funzionamento.

Inizializza il callback di chiamata

  1. Nel modulo base, cerca TODO: Step 1.4, Initialize the LocationCallback nel file ForegroundOnlyLocationService.kt.
  2. Aggiungi il seguente codice dopo il commento.
// TODO: Step 1.4, Initialize the LocationCallback.
locationCallback = object : LocationCallback() {
   override fun onLocationResult(locationResult: LocationResult?) {
       super.onLocationResult(locationResult)

       if (locationResult?.lastLocation != null) {

           // Normally, you want to save a new location to a database. We are simplifying
           // things a bit and just saving it as a local variable, as we only need it again
           // if a Notification is created (when user navigates away from app).
           currentLocation = locationResult.lastLocation

           // Notify our Activity that a new location was added. Again, if this was a
           // production app, the Activity would be listening for changes to a database
           // with new locations, but we are simplifying things a bit to focus on just
           // learning the location side of things.
           val intent = Intent(ACTION_FOREGROUND_ONLY_LOCATION_BROADCAST)
           intent.putExtra(EXTRA_LOCATION, currentLocation)
           LocalBroadcastManager.getInstance(applicationContext).sendBroadcast(intent)

           // Updates notification content if this service is running as a foreground
           // service.
           if (serviceRunningInForeground) {
               notificationManager.notify(
                   NOTIFICATION_ID,
                   generateNotification(currentLocation))
           }
       } else {
           Log.d(TAG, "Location information isn't available.")
       }
   }
}

Il LocationCallback che crei qui è il callback che chiamerà FusedLocationProviderClient quando è disponibile un nuovo aggiornamento della posizione.

Nel callback, ricevi prima la posizione più recente utilizzando un oggetto LocationResult. Dopodiché, avvisa Activity della nuova posizione utilizzando un annuncio locale (se è attivo) oppure aggiorni il valore Notification se il servizio viene eseguito come Service in primo piano.

  1. Leggi attentamente i commenti per comprendere le funzioni.

Iscriversi a modifiche della posizione

Ora che hai inizializzato tutto, devi far sapere a FusedLocationProviderClient che vuoi ricevere gli aggiornamenti.

  1. Nel modulo base, cerca Step 1.5, Subscribe to location changes nel file ForegroundOnlyLocationService.kt.
  2. Aggiungi il seguente codice dopo il commento.
// TODO: Step 1.5, Subscribe to location changes.
fusedLocationProviderClient.requestLocationUpdates(locationRequest, locationCallback, Looper.myLooper())

La chiamata requestLocationUpdates() fa sapere a FusedLocationProviderClient che vuoi ricevere gli aggiornamenti sulla posizione.

Probabilmente riconosci i LocationRequest e le LocationCallback che hai definito in precedenza. Questi elementi consentono a FusedLocationProviderClient di conoscere i parametri di qualità del servizio per la tua richiesta e come deve essere chiamato quando è disponibile un aggiornamento. Infine, l'oggetto Looper specifica il thread per il callback.

Potresti anche notare che questo codice è incluso in un'istruzione try/catch. Questo metodo richiede l'applicazione di un blocco perché una SecurityException si verifica quando l'app non è autorizzata ad accedere alle informazioni sulla posizione.

Annullare l'iscrizione alle modifiche alla posizione

Quando l'app non ha più bisogno di accedere alle informazioni sulla posizione, è importante annullare l'iscrizione agli aggiornamenti sulla posizione.

  1. Nel modulo base, cerca TODO: Step 1.6, Unsubscribe to location changes nel file ForegroundOnlyLocationService.kt.
  2. Aggiungi il seguente codice dopo il commento.
// TODO: Step 1.6, Unsubscribe to location changes.
val removeTask = fusedLocationProviderClient.removeLocationUpdates(locationCallback)
removeTask.addOnCompleteListener { task ->
   if (task.isSuccessful) {
       Log.d(TAG, "Location Callback removed.")
       stopSelf()
   } else {
       Log.d(TAG, "Failed to remove Location Callback.")
   }
}

Il metodo removeLocationUpdates() configura un'attività per informare FusedLocationProviderClient che non vuoi più ricevere gli aggiornamenti sulla posizione per LocationCallback. addOnCompleteListener() fornisce il callback per il completamento ed esegue Task.

Come per il passaggio precedente, potresti aver notato che questo codice è incluso in un'istruzione try/catch. Questo metodo richiede il blocco dell'elemento perché un'SecurityException si verifica quando la tua app non è autorizzata ad accedere alle informazioni sulla posizione

Potresti chiederti quando vengono chiamati i metodi che contengono il codice di abbonamento. Vengono attivati nella classe principale quando l'utente tocca il pulsante. Se vuoi vederlo, dai un'occhiata alla classe MainActivity.kt.

Esegui app

Esegui l'app da Android Studio e prova il pulsante Posizione.

Dovresti vedere le informazioni sulla posizione nella schermata di output. Questa è un'app completamente funzionante per Android 9.

In questa sezione aggiungerai il supporto di Android 10.

La tua app è già autorizzata a modificare la posizione, quindi non c'è molto da fare.

Di fatto, devi solo specificare che il servizio in primo piano venga utilizzato per la localizzazione.

SDK di destinazione 29

  1. Nel modulo base, cerca TODO: Step 2.1, Target SDK 10 nel file build.gradle.
  2. Apporta le seguenti modifiche:
  1. Imposta compileSdkVersion su 29.
  2. Imposta buildToolsVersion su "29.0.3".
  3. Imposta targetSdkVersion su 29.

Il tuo codice sarà simile al seguente:

android {
   // TODO: Step 2.1, Target Android 10.
   compileSdkVersion 29
   buildToolsVersion "29.0.3"
   defaultConfig {
       applicationId "com.example.android.whileinuselocation"
       minSdkVersion 26
       targetSdkVersion 29
       versionCode 1
       versionName "1.0"
   }
...
}

Dopo aver eseguito questa operazione, ti verrà chiesto di sincronizzare il progetto. Fai clic su Sync Now (Sincronizza ora).

Dopodiché, la tua app è quasi pronta per Android 10.

Aggiungi tipo di servizio in primo piano

In Android 10, devi includere il tipo di servizio in primo piano se hai bisogno di accedere alla posizione mentre sei in uso. Nel tuo caso viene utilizzato per ottenere informazioni sulla posizione.

Nel modulo base, cerca TODO: 2.2, Add foreground service type nell'elemento AndroidManifest.xml e aggiungi il seguente codice all'elemento <service>:

android:foregroundServiceType="location"

Il tuo codice sarà simile al seguente:

<application>
   ...

   <!-- Foreground services in Android 10+ require type. -->
   <!-- TODO: 2.2, Add foreground service type. -->
   <service
       android:name="com.example.android.whileinuselocation.ForegroundOnlyLocationService"
       android:enabled="true"
       android:exported="false"
       android:foregroundServiceType="location" />
</application>

Ecco fatto. La tua app supporta la posizione Android 10 per "durante l'uso" seguendo le best practice per la posizione in Android.

Esegui app

Esegui l'app da Android Studio e prova il pulsante Posizione.

Tutto dovrebbe funzionare come prima, ma ora funziona su Android 10. Se in precedenza non avevi accettato le autorizzazioni per le posizioni, ora dovresti vedere la schermata di autorizzazione.

In questa sezione, scegli come target Android 11.

Ottime notizie: non devi apportare modifiche ai file, tranne che al file build.gradle.

SDK R target

  1. Nel modulo base, cerca TODO: Step 2.1, Target SDK nel file build.gradle.
  2. Apporta le seguenti modifiche:
  1. Da compileSdkVersion a "android-R"
  2. Da targetSdkVersion a "R"

Il tuo codice sarà simile al seguente:

android {
   // TODO: Step 2.1, Target Android 10.
   compileSdkVersion "android-R"
   buildToolsVersion "29.0.2"
   defaultConfig {
       applicationId "com.example.android.whileinuselocation"
       minSdkVersion 26
       targetSdkVersion "R"
       versionCode 1
       versionName "1.0"
   }
...
}

Dopo aver eseguito questa operazione, ti verrà chiesto di sincronizzare il progetto. Fai clic su Sync Now (Sincronizza ora).

Dopodiché, la tua app è pronta per Android 11.

Esegui app

Esegui l'app da Android Studio e prova a fare clic sul pulsante.

Tutto dovrebbe funzionare come prima, ma ora funziona su Android 11. Se in precedenza non avevi accettato le autorizzazioni per le posizioni, ora dovresti vedere la schermata di autorizzazione.

Se controlli e richiedi le autorizzazioni di accesso alla posizione nei modi mostrati in questo codelab, la tua app può tenere traccia del suo livello di accesso in relazione alla posizione del dispositivo.

Questa pagina elenca alcune best practice chiave relative alle autorizzazioni di accesso alla posizione. Per ulteriori informazioni su come proteggere i dati dei tuoi utenti, vedi le best practice relative alle autorizzazioni app.

Chiedi solo le autorizzazioni che ti servono

Chiedi le autorizzazioni solo quando necessario. Ad esempio:

  • Non richiedere un'autorizzazione di accesso alla posizione all'avvio dell'app, a meno che non sia assolutamente necessario.
  • Se la tua app ha come target Android 10 o versioni successive e hai un servizio in primo piano, dichiara un foregroundServiceType di "location" nel file manifest.
  • Non richiedere autorizzazioni di accesso alla posizione in background, a meno che tu non disponga di un caso d'uso valido, come descritto nell'articolo Accesso più sicuro e trasparente alla posizione dell'utente.

Supporto di una riduzione controllata se l'autorizzazione non viene concessa

Per offrire un'esperienza utente positiva, progetta la tua app in modo che possa gestire facilmente le seguenti situazioni:

  • La tua app non ha accesso alle informazioni sulla posizione.
  • La tua app non ha accesso alle informazioni sulla posizione quando è in esecuzione in background.

Hai imparato a ricevere aggiornamenti sulla posizione in Android, tenendo presente le best practice.

Scopri di più