Ricevi aggiornamenti sulla posizione in Android con Kotlin

1. Prima di iniziare

Android 10 e 11 offrono agli utenti un maggiore controllo sulle impostazioni l'accesso alle posizioni dei loro dispositivi.

Quando un'app eseguita su Android 11 richiede l'accesso alla posizione, gli utenti hanno a disposizione quattro opzioni:

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

Android 10

6a1029175b467c77.png

Android 11

73d8cc88c5877c25.png

In questo codelab, imparerai a ricevere aggiornamenti sulla posizione e a supportare la posizione su qualsiasi versione di Android, in particolare Android 10 e 11. Al termine del codelab, dovresti avere un'app che segue le best practice attuali per il recupero degli aggiornamenti della posizione.

Prerequisiti

Attività previste

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

Che cosa ti serve

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

2. Per iniziare

Clonare il repository del progetto iniziale

Per iniziare il più rapidamente possibile, puoi creare a partire da questo progetto iniziale. Se hai installato Git, puoi semplicemente eseguire questo comando:

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

Visita direttamente la pagina GitHub.

Se non hai Git, puoi recuperare il progetto come file ZIP:

Importa il progetto.

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

Dopo il caricamento del progetto, potresti anche visualizzare un avviso che indica che Git non sta monitorando tutte le modifiche locali. Puoi fare clic su Ignora. Non eseguirai il push delle modifiche al repository Git.

Se ti trovi nella visualizzazione Android, nell'angolo in alto a sinistra della finestra del progetto dovresti vedere qualcosa di simile all'immagine seguente. (Se ti trovi nella vista Progetto, devi espandere il progetto per vedere lo stesso elemento.)

fa825dae96c5dc18.png

Sono presenti due cartelle (base e complete). Ciascuno di essi è noto come "modulo".

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

c2273e7835c0841a.png

Attendi che Android Studio abbia terminato l'indicizzazione e la creazione del progetto prima di apportare modifiche al codice. In questo modo, Android Studio potrà recuperare tutti i componenti necessari.

Se viene visualizzato il messaggio Ricarica per applicare le modifiche alla lingua? o un messaggio simile, seleziona .

Informazioni sul progetto iniziale

Ora è tutto pronto per richiedere la posizione nell'app. Utilizza il modulo base come punto di partenza. Durante ogni passaggio, aggiungi codice nel modulo base. Al termine di questo codelab, il codice nel modulo base dovrebbe corrispondere ai contenuti del modulo complete. Il modulo complete può essere utilizzato per controllare il tuo lavoro o come riferimento in caso di problemi.

I componenti chiave includono:

  • MainActivity: UI che consente all'utente di consentire all'app di accedere alla posizione del dispositivo.
  • LocationService: servizio che sottoscrive e annulla l'iscrizione ai cambiamenti di posizione e si promuove a servizio in primo piano (con una notifica) se l'utente esce dall'attività dell'app. Aggiungi qui il codice della località.
  • 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 l'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 base dal selettore a discesa e fai clic su Esegui:

99600e9d44527ab.png

  1. Sul tuo dispositivo viene visualizzata la seguente app:

99bf1dae46f99af3.png

Potresti notare che nella schermata di output non vengono visualizzate informazioni sulla posizione. Il motivo è che non hai ancora aggiunto il codice della località.

3. Aggiunta della località

Concetti

Lo scopo di questo codelab è mostrarti come ricevere aggiornamenti sulla posizione e come supportare Android 10 e Android 11.

Tuttavia, prima di iniziare a programmare, è opportuno rivedere le nozioni di base.

Tipi di accesso alla posizione

Forse ricordi le quattro diverse opzioni per l'accesso alla posizione all'inizio del codelab. Scopri cosa significano:

  • Consenti solo mentre l'app è in uso
  • Questa è l'opzione consigliata per la maggior parte delle app. Chiamato anche "durante l'uso" o "solo in primo piano" questa opzione è stata aggiunta in Android 10 e consente agli sviluppatori di recuperare la posizione soltanto mentre l'app è in uso. Un'app viene considerata attiva se si verifica una delle seguenti condizioni:
  • Un'attività è visibile.
  • Un servizio in primo piano è in esecuzione con una notifica continua.
  • Solo una volta
  • Aggiunta in Android 11, analoga a Consenti solo mentre l'app è in uso, ma per un periodo di tempo limitato. Per ulteriori informazioni, vedi 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 avere un caso d'uso valido e rispettare le norme sulla località. Non tratterai questa opzione in questo codelab, in quanto è un caso d'uso più raro. Tuttavia, se hai un caso d'uso valido e vuoi capire come gestire correttamente la posizione costante, incluso l'accesso alla posizione in background, esamina l'esempio di LocationUpdatesBackgroundKotlin.

Servizi, servizi in primo piano e associazione

Per supportare completamente gli aggiornamenti della posizione Consenti solo mentre l'app è in uso, 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 Notification.

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

Poiché questo codelab è incentrato solo sulla ricezione di aggiornamenti sulla posizione, puoi trovare tutto il codice che ti serve nel corso ForegroundOnlyLocationService.kt. Puoi sfogliare il corso e il MainActivity.kt per vedere come interagiscono tra loro.

Per ulteriori informazioni, vedi Panoramica dei servizi e Panoramica dei servizi associati.

Autorizzazioni

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

Queste autorizzazioni riguardano le richieste Solo una volta e Consenti solo mentre l'app è in uso, quando la tua app viene utilizzata su un dispositivo con Android 10 o versioni successive.

Località

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

Dai un'occhiata alle classi principali:

  • FusedLocationProviderClient
  • È il componente centrale del framework della posizione. Una volta creato, puoi utilizzarlo per richiedere 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). Questo viene trasmesso all'FusedLocationProviderClient quando richiedi aggiornamenti della posizione.
  • LocationCallback
  • Viene utilizzato per ricevere notifiche quando la posizione del dispositivo cambia o non può più essere determinata. Questo viene passato un LocationResult in cui puoi recuperare l'elemento Location da salvare nel tuo database.

Ora che hai un'idea di base di ciò che stai facendo, inizia a utilizzare il codice.

4. Aggiungi caratteristiche di localizzazione

Questo codelab è incentrato sull'opzione relativa alla posizione più comune: Consenti solo mentre l'app è in uso.

Per ricevere 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 di posizione, non come richiedere autorizzazioni di accesso alla posizione, quindi il codice basato sulle autorizzazioni è già stato scritto automaticamente. Se lo conosci già, puoi tranquillamente ignorarlo.

Di seguito sono riportate le informazioni principali relative alle autorizzazioni (per questa parte non è richiesta alcuna azione):

  1. Dichiara quale autorizzazione usi in AndroidManifest.xml.
  2. Prima di tentare di accedere alle informazioni sulla posizione, controlla se l'utente ha concesso alla tua app l'autorizzazione a farlo. Se la tua app non ha ancora ricevuto l'autorizzazione, richiedi l'accesso.
  3. Gestisci la scelta dell'utente relativa all'autorizzazione. (Puoi visualizzare questo codice nel MainActivity.kt.)

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

Per maggiori informazioni, consulta la panoramica delle autorizzazioni.

Ora inizia a scrivere il codice di posizione.

Esamina le variabili chiave necessarie per gli aggiornamenti delle sedi

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

ForegroundOnlyLocationService.kt.

Non sono necessarie azioni in questo passaggio. Devi solo esaminare il seguente blocco di codice, insieme ai commenti, per comprendere le classi e le variabili chiave che utilizzi per ricevere 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 codice dovrebbe avere un aspetto simile al seguente:

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

Come accennato nei commenti precedenti, questa è la classe principale per ricevere aggiornamenti sulla posizione. La variabile è già stata inizializzata automaticamente, ma è importante esaminare il codice per capire come viene inizializzato. Aggiungi del codice qui in un secondo momento per richiedere aggiornamenti della 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 LocationRequest aggiunge i parametri di qualità del servizio aggiuntivi necessari per la tua richiesta (intervalli, tempo di attesa massimo e priorità).

// TODO: Step 1.3, Create a LocationRequest.
locationRequest = LocationRequest.create().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 capire come funzionano.

Inizializzare LocationCallback

  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)

        // 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 the 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))
        }
    }
}

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

Nel callback, ottieni innanzitutto la posizione più recente utilizzando un oggetto LocationResult. Successivamente, invierai a Activity la nuova posizione utilizzando una trasmissione locale (se è attiva) oppure aggiorni Notification se questo servizio è in esecuzione in primo piano Service.

  1. Leggi i commenti per capire la funzione di ogni parte.

Abbonarsi ai cambiamenti di località

Ora che hai inizializzato tutto, devi comunicare al 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.getMainLooper())

La chiamata requestLocationUpdates() consente al FusedLocationProviderClient di sapere che vuoi ricevere aggiornamenti sulla posizione.

Probabilmente riconosci i LocationRequest e LocationCallback che hai definito in precedenza. Consentono a FusedLocationProviderClient di conoscere i parametri della qualità del servizio per la tua richiesta e il nome da chiamare in caso di aggiornamento. Infine, l'oggetto Looper specifica il thread per il callback.

Potresti anche notare che questo codice si trova all'interno di un'istruzione try/catch. Questo metodo richiede un blocco di questo tipo perché un SecurityException si verifica quando la tua app non è autorizzata ad accedere ai dati sulla posizione.

Annullare l'iscrizione alle modifiche delle località

Quando l'app non ha più bisogno di accedere ai dati 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 comunicare a FusedLocationProviderClient che non vuoi più ricevere aggiornamenti di posizione per il tuo LocationCallback. addOnCompleteListener() fornisce il callback per il completamento ed esegue il Task.

Come nel passaggio precedente, potresti aver notato che questo codice si trova all'interno di un'istruzione try/catch. Questo metodo richiede un blocco di questo tipo perché un SecurityException si verifica quando la tua app non ha l'autorizzazione ad accedere alle informazioni sulla posizione

È possibile che si stia chiedendo quando vengono chiamati i metodi che contengono il codice di sottoscrizione/annullamento dell'iscrizione. Vengono attivati nella classe principale quando l'utente tocca il pulsante. Se vuoi vederlo, dai un'occhiata al corso MainActivity.kt.

Esegui app

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

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

2ae45c4e297e3681.png

d66089bfb532e993.png

5. Supporto per Android 10

In questa sezione aggiungi il supporto per Android 10.

La tua app è già in abbonamento per il cambio di posizione, quindi non c'è molto da fare.

Anzi, devi solo specificare che il tuo servizio in primo piano viene utilizzato per scopi di localizzazione.

SDK target 29

  1. Nel modulo base, cerca TODO: Step 2.1, Target Android 10 and then Android 11. nel file build.gradle.
  2. Apporta queste modifiche:
  3. Imposta targetSdkVersion su 29.

Il codice dovrebbe avere un aspetto simile al seguente:

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

Dopodiché ti verrà chiesto di sincronizzare il progetto. Fai clic su Sincronizza ora.

153f70847e0ec320.png

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

Aggiungi tipo di servizio in primo piano

Se hai bisogno dell'accesso alla posizione in uso, in Android 10 devi includere il tipo di servizio in primo piano. Nel tuo caso, viene utilizzato per ottenere informazioni sulla posizione.

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

android:foregroundServiceType="location"

Il codice dovrebbe avere un aspetto 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>

È tutto. La tua app supporta la posizione di Android 10 per l'utilizzo seguendo le best practice per la geolocalizzazione in Android.

Esegui app

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

Tutto dovrebbe funzionare come prima, ma ora funziona su Android 10. Se non hai già accettato le autorizzazioni per le località, ora dovresti visualizzare la schermata delle autorizzazioni.

6a1029175b467c77.png

c7c1d226e49a121.png

39a262b66a275f66.png

6. Supporto per Android 11

In questa sezione, scegli come target Android 11.

Ottime notizie, non è necessario apportare modifiche a nessun file, ad eccezione del file build.gradle.

SDK target 11

  1. Nel modulo base, cerca TODO: Step 2.1, Target SDK nel file build.gradle.
  2. Apporta queste modifiche:
  3. Da compileSdkVersion a 30
  4. Da targetSdkVersion a 30

Il codice dovrebbe avere un aspetto simile al seguente:

android {
   TODO: Step 2.1, Target Android 10 and then Android 11.
   compileSdkVersion 30
   defaultConfig {
       applicationId "com.example.android.whileinuselocation"
       minSdkVersion 26
       targetSdkVersion 30
       versionCode 1
       versionName "1.0"
   }
...
}

Dopodiché ti verrà chiesto di sincronizzare il progetto. Fai clic su Sincronizza ora.

153f70847e0ec320.png

Dopodiché, la tua app sarà 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 non hai già accettato le autorizzazioni per le località, ora dovresti visualizzare la schermata delle autorizzazioni.

73d8cc88c5877c25.png

cc98fac6e089bc4.png

7. Strategie di geolocalizzazione per Android

Controllando e richiedendo le autorizzazioni di accesso alla posizione nei modi mostrati in questo codelab, la tua app può monitorare correttamente il proprio livello di accesso relativo alla posizione del dispositivo.

In questa pagina sono elencate alcune best practice chiave relative alle autorizzazioni di accesso alla posizione. Per ulteriori informazioni su come mantenere le impostazioni sicurezza dei dati, consulta le Best practice per le 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 disponi di un servizio in primo piano, dichiara un valore foregroundServiceType di "location" nel file manifest.
  • Non richiedere autorizzazioni di accesso alla posizione in background a meno che tu non abbia un caso d'uso valido come descritto nella sezione Accesso più sicuro e trasparente alla posizione dell'utente.

Supporta la riduzione controllata se l'autorizzazione non viene concessa

Per garantire una buona esperienza utente, progetta la tua app in modo che possa gestire senza problemi le seguenti situazioni:

  • La tua app non ha accesso alle informazioni sulla posizione.
  • La tua app non ha accesso ai dati sulla posizione quando viene eseguita in background.

8. Complimenti

Hai imparato a ricevere aggiornamenti sulla posizione su Android, tenendo presenti le best practice.

Scopri di più