O Android 10 e 11 oferece aos usuários mais controle sobre os apps e o acesso à localização do dispositivo.
Quando um app em execução no Android 11 solicita acesso à localização, os usuários têm quatro opções:
- Permitir o tempo todo
- Permitir durante o uso do app (no Android 10)
- Somente uma vez (no Android 11)
- Negar
Android 10
Android 11
Neste codelab, você aprenderá a receber atualizações de localização e oferecer compatibilidade com a localização em qualquer versão do Android, especialmente Android 10 e 11. Ao final do codelab, você terá um app que segue as práticas recomendadas atuais para recuperar atualizações de localização.
Pré-requisitos
- Conhecer o desenvolvimento para Android
- alguma familiaridade com atividades, serviços e permissões
O que você aprenderá
- Siga as práticas recomendadas para localização no Android.
- Lidar com permissões de localização em primeiro plano (quando o usuário solicita que seu app acesse a localização do dispositivo enquanto está em uso)
- Para modificar um app existente e adicionar suporte para solicitar acesso ao local, adicione um código para assinar e cancelar a inscrição no local.
- Adicione compatibilidade com o app para o Android 10 e 11 adicionando lógica para acessar a localização na localização em primeiro plano ou durante o uso.
O que é necessário
- Android Studio 3.4 ou mais recente para executar o código
- Um dispositivo/emulador com uma Visualização do desenvolvedor do Android 10 e 11
Clonar o repositório inicial do projeto
Para começar o mais rápido possível, use este projeto inicial. Se você tiver o Git instalado, basta executar o seguinte comando:
git clone https://github.com/googlecodelabs/while-in-use-location
Fique à vontade para acessar a página do GitHub diretamente.
Se você não tiver o Git, poderá acessar o projeto como um arquivo ZIP:
Importar o projeto
Abra o Android Studio, selecione "Open an existing Android Studio project" na tela de boas-vindas e abra o diretório do projeto.
Após o carregamento do projeto, você verá um alerta informando que o Git não está rastreando todas as mudanças locais. Clique em Ignorar. Você não enviará mudanças ao repositório Git.
No canto superior esquerdo da janela do projeto, você verá algo semelhante à imagem abaixo se estiver na visualização Android. Se você estiver na visualização Project, precisará expandi-la para ver a mesma coisa.
Há duas pastas (base
e complete
). Cada uma é conhecida como um "módulo".
O Android Studio pode levar vários segundos para compilar o projeto em segundo plano pela primeira vez. Durante esse período, você verá a seguinte mensagem na barra de status na parte inferior do Android Studio:
Aguarde até que o Android Studio termine de indexar e criar o projeto antes de fazer mudanças no código. Isso permitirá que o Android Studio extraia todos os componentes necessários.
Se você receber a mensagem Reload for language changes to take take? ou algo semelhante, selecione Yes.
Entender o projeto inicial
Você está pronto e pronto para solicitar a localização no app. Use o módulo base
como ponto de partida. Em cada etapa, adicione código ao módulo base
. No fim deste codelab, o código no módulo base
será igual ao conteúdo do módulo complete
. O módulo complete
pode ser usado para verificar seu trabalho ou para consulta em caso de problemas.
Os componentes principais incluem:
MainActivity
: IU para o usuário permitir que o app acesse a localização do dispositivoLocationService
: serviço que se inscreve e cancela a inscrição para alterações de local e se promove como um serviço em primeiro plano (com uma notificação) quando o usuário sai da atividade do app. Adicione o código do local aqui.Util
: adiciona funções de extensão para a classeLocation
e salva o local emSharedPreferences
(camada de dados simplificada).
Configuração do emulador
Para saber mais sobre como configurar um Android Emulator, consulte Executar em um emulador.
Executar o projeto inicial
Execute o app.
- Conecte seu dispositivo Android ao computador ou inicie um emulador. Confira se o dispositivo está executando o Android 10 ou mais recente.
- Na barra de ferramentas, selecione a configuração
base
no seletor suspenso e clique em Executar:
- O seguinte app aparece no seu dispositivo:
Observe que nenhuma informação de localização aparecerá na tela de saída. Isso ocorre porque você ainda não adicionou o código de local.
conceitos
O foco deste codelab é mostrar como receber atualizações de localização para oferecer compatibilidade com o Android 10 e o Android 11.
No entanto, antes de começar a programar, vale a pena revisar os princípios básicos.
Tipos de acesso à localização
Você pode se lembrar das quatro opções diferentes de acesso à localização desde o início do codelab. Veja o que eles significam:
- Permitir durante o uso do app
- Essa é a opção recomendada para a maioria dos apps. Também conhecida como acesso "durante o uso" ou "somente em primeiro plano", essa opção foi adicionada no Android 10 e permite que os desenvolvedores recuperem a localização apenas enquanto o app está sendo ativamente usado. Um app será considerado ativo se uma das seguintes condições for verdadeira:
- Uma atividade está visível.
- Um serviço em primeiro plano está em execução com uma notificação em andamento.
- Apenas uma vez
- Adicionado no Android 11, isso é o mesmo que Permitir apenas durante o uso do app, mas por um período limitado. Para mais informações, consulte Permissões únicas.
- Negar
- Essa opção impede o acesso a informações de localização.
- Permitir o tempo todo
- Essa opção permite acesso à localização o tempo todo, mas requer uma permissão extra para o Android 10 e versões mais recentes. Também é preciso ter um caso de uso válido e obedecer às políticas de local. Você não abordará essa opção neste codelab, já que esse é um caso de uso mais raro. No entanto, se você tiver um caso de uso válido e quiser entender como processar corretamente a localização o tempo todo, incluindo o acesso à localização em segundo plano, consulte a amostra LocationUpdatesBackgroundKotlin.
Serviços, serviços em primeiro plano e vinculação
Para oferecer compatibilidade total com as atualizações de localização Permitir apenas durante o uso do app, é necessário considerar quando o usuário sai do app. Se você quiser continuar recebendo atualizações nessa situação, precisará criar um Service
em primeiro plano e associá-lo a um Notification
.
Além disso, se você quiser usar o mesmo Service
para solicitar atualizações de localização quando o app estiver visível e quando o usuário sair do app, será necessário vincular/desvincular o Service
ao elemento da IU.
Como este codelab se concentra apenas em receber atualizações de localização, você pode encontrar todo o código necessário na classe ForegroundOnlyLocationService.kt
. Você pode navegar por essa classe e pelo MainActivity.kt
para ver como elas funcionam juntas.
Veja mais informações em Visão geral dos serviços e Visão geral dos serviços vinculados.
Permissões
Para receber atualizações de localização de um NETWORK_PROVIDER
ou GPS_PROVIDER
, é necessário solicitar a permissão do usuário declarando a permissão ACCESS_COARSE_LOCATION
ou ACCESS_FINE_LOCATION
, respectivamente, no seu arquivo de manifesto do Android. Sem essas permissões, o app não poderá solicitar acesso à localização no momento da execução.
Essas permissões abrangem os casos Somente uma vez e Permitir durante o uso do app quando ele é usado em dispositivos com o Android 10 ou versões mais recentes.
Local
O app pode acessar o conjunto de serviços de localização compatíveis por meio de classes no pacote com.google.android.gms.location
.
Veja as principais classes:
FusedLocationProviderClient
- Este é o componente central do framework de localização. Depois de criado, você pode usá-lo para solicitar atualizações e conseguir o último local conhecido.
LocationRequest
- Esse é um objeto de dados que contém parâmetros de qualidade de serviço para solicitações (intervalos de atualizações, prioridades e precisão). Ela é transmitida para o
FusedLocationProviderClient
quando você solicita atualizações de localização. LocationCallback
- Usado para receber notificações quando a localização do dispositivo mudar ou não for mais determinada. Ela recebe um
LocationResult
, onde é possível salvarLocation
no banco de dados.
Agora que você tem uma ideia básica do que está fazendo, comece a usar o código.
Este codelab se concentra na opção de local mais comum: Permitir durante o uso do app.
Para receber atualizações de localização, o app precisa ter uma atividade visível ou um serviço em execução em primeiro plano (com uma notificação).
Permissões
O objetivo deste codelab é mostrar como receber atualizações de localização, e não como solicitar permissões de localização. Portanto, o código com base em permissão já está programado. Se você já entende, é só pular.
Veja a seguir os destaques das permissões (nenhuma ação é necessária):
- Declare qual permissão você usa no
AndroidManifest.xml
. - Antes de tentar acessar as informações de localização, verifique se o usuário concedeu permissão para que seu app faça isso. Se o app ainda não tiver recebido permissão, solicite acesso.
- Processe a escolha de permissão do usuário. É possível ver esse código no
MainActivity.kt
.
Se você pesquisar TODO: Step 1.0, Review Permissions
na AndroidManifest.xml
ou na MainActivity.kt
, verá todo o código escrito para permissões.
Para saber mais, consulte Visão geral de permissões.
Agora, comece a escrever um código de localização.
Analise as principais variáveis necessárias para atualizações de localização
No módulo base
, pesquise TODO: Step 1.1, Review variables
na
arquivo ForegroundOnlyLocationService.kt
.
Nenhuma ação é necessária nesta etapa. Você só precisa analisar o bloco de código a seguir, junto com os comentários, para entender as principais classes e variáveis que você usa para receber atualizações de localização.
// 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
Analisar a inicialização de FusedLocationProviderClient
No módulo base
, pesquise TODO: Step 1.2, Review the FusedLocationProviderClient
no arquivo ForegroundOnlyLocationService.kt
. O código vai ficar assim:
// TODO: Step 1.2, Review the FusedLocationProviderClient.
fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this)
Como mencionado nos comentários anteriores, esta é a principal classe para receber atualizações de localização. A variável já foi inicializada, mas é importante analisar o código para entender como ela foi inicializada. Adicione código aqui para solicitar atualizações de localização.
Inicialize o LocationRequest
- No módulo
base
, pesquiseTODO: Step 1.3, Create a LocationRequest
no arquivoForegroundOnlyLocationService.kt
. - Adicione o seguinte código após o comentário.
O código de inicialização LocationRequest
adiciona a qualidade extra dos parâmetros de serviço que você precisa para sua solicitação (intervalos, tempo de espera máximo e prioridade).
// 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
}
- Leia os comentários para entender como cada um funciona.
Inicialize o LocationCallback
- No módulo
base
, pesquiseTODO: Step 1.4, Initialize the LocationCallback
no arquivoForegroundOnlyLocationService.kt
. - Adicione o seguinte código após o comentário.
// 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.")
}
}
}
O LocationCallback
que você criar aqui é o callback que o FusedLocationProviderClient
chamará quando uma nova atualização de local estiver disponível.
No retorno de chamada, primeiro acesse o local mais recente usando um objeto LocationResult
. Depois disso, você notifica a Activity
sobre o novo local usando uma transmissão local (se ela estiver ativa) ou atualiza a Notification
caso esse serviço seja executado como um Service
em primeiro plano.
- Leia os comentários para entender o que cada parte faz.
Inscrever-se para receber alterações de local
Agora que você inicializou tudo, é necessário informar ao FusedLocationProviderClient
que você quer receber atualizações.
- No módulo
base
, pesquiseStep 1.5, Subscribe to location changes
no arquivoForegroundOnlyLocationService.kt
. - Adicione o seguinte código após o comentário.
// TODO: Step 1.5, Subscribe to location changes.
fusedLocationProviderClient.requestLocationUpdates(locationRequest, locationCallback, Looper.myLooper())
A chamada requestLocationUpdates()
informa ao FusedLocationProviderClient
que você quer receber atualizações de localização.
Você provavelmente reconhece os LocationRequest
e os LocationCallback
que definiu anteriormente. Esse parâmetro informa ao FusedLocationProviderClient
os parâmetros de qualidade do serviço para sua solicitação e o que ele precisa chamar quando tiver uma atualização. Por fim, o objeto Looper
especifica a linha de execução para o callback.
Você também pode notar que esse código está em uma instrução try/catch
. Esse método exige esse bloco porque ocorre uma SecurityException
quando o app não tem permissão para acessar informações de localização.
Cancelar inscrição nas alterações de local
Quando o app não precisar mais de informações de localização, é importante cancelar a inscrição das atualizações de localização.
- No módulo
base
, pesquiseTODO: Step 1.6, Unsubscribe to location changes
no arquivoForegroundOnlyLocationService.kt
. - Adicione o seguinte código após o comentário.
// 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.")
}
}
O método removeLocationUpdates()
configura uma tarefa para informar ao FusedLocationProviderClient
que você não quer mais receber atualizações de localização para o LocationCallback
. O addOnCompleteListener()
fornece o callback para conclusão e executa o Task
.
Como na etapa anterior, você deve ter percebido que esse código está em uma instrução try/catch
. Esse método exige esse bloco porque ocorre uma SecurityException
quando o app não tem permissão para acessar as informações de localização.
Você pode se perguntar quando os métodos que contêm o código de inscrição/cancelamento são chamados. Eles são acionados na classe principal quando o usuário toca no botão. Se quiser, veja a classe MainActivity.kt
.
Execute o app.
Execute o app no Android Studio e teste o botão de localização.
Você verá as informações de localização na tela de saída. Este é um app totalmente funcional para o Android 9.
Nesta seção, você vai adicionar compatibilidade com o Android 10.
Como seu app já é assinante de mudanças de localização, não há muito trabalho a fazer.
Na verdade, tudo o que você precisa fazer é especificar que seu serviço em primeiro plano é usado para fins de localização.
SDK de destino 29
- No módulo
base
, pesquiseTODO: Step 2.1, Target SDK 10
no arquivobuild.gradle
. - Faça estas mudanças:
- Defina
compileSdkVersion
como29
. - Defina
buildToolsVersion
como"29.0.3"
. - Defina
targetSdkVersion
como29
.
O código vai ficar assim:
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"
}
...
}
Em seguida, você precisará sincronizar o projeto. Clique em Sync Now.
Depois disso, o app estará quase pronto para o Android 10.
Adicionar tipo de serviço em primeiro plano
No Android 10, você precisará incluir o tipo do serviço em primeiro plano se precisar de acesso à localização durante o uso. No seu caso, ela está sendo usada para coletar informações de local.
No módulo base
, pesquise a TODO: 2.2, Add foreground service type
no AndroidManifest.xml
e adicione o seguinte código ao elemento <service>
:
android:foregroundServiceType="location"
O código vai ficar assim:
<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>
Pronto! Seu app é compatível com a localização do Android 10 enquanto ele está em uso, seguindo as práticas recomendadas de localização no Android.
Execute o app.
Execute o app no Android Studio e teste o botão de localização.
Tudo deveria funcionar como antes, mas agora funciona no Android 10. Se você não tinha aceitado as permissões dos locais antes, a tela de permissões será exibida.
Nesta seção, o app será direcionado ao Android 11.
Uma boa notícia é que você não precisa fazer alterações em nenhum arquivo, exceto no arquivo build.gradle
.
SDK de destino R
- No módulo
base
, pesquiseTODO: Step 2.1, Target SDK
no arquivobuild.gradle
. - Faça estas mudanças:
compileSdkVersion
a"android-R"
targetSdkVersion
a"R"
O código vai ficar assim:
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"
}
...
}
Em seguida, você precisará sincronizar o projeto. Clique em Sync Now.
Depois disso, o app estará pronto para o Android 11.
Execute o app.
Execute o app no Android Studio e tente clicar no botão.
Tudo deveria funcionar como antes, mas agora funciona no Android 11. Se você não tinha aceitado as permissões dos locais antes, a tela de permissões será exibida.
Ao verificar e solicitar permissões de localização das formas mostradas neste codelab, seu app pode acompanhar o nível de acesso dele à localização do dispositivo.
Esta página lista algumas práticas recomendadas relacionadas às permissões de localização. Para mais informações sobre como manter os dados dos seus usuários seguros, consulte Práticas recomendadas de permissões do app.
Peça apenas as permissões necessárias
Solicite permissões somente quando necessário. Exemplo:
- Não solicite uma permissão de localização na inicialização do aplicativo, a menos que seja absolutamente necessário.
- Se o app for destinado ao Android 10 ou versões mais recentes e você tiver um serviço em primeiro plano, declare um
foregroundServiceType
de"location"
no manifesto. - Não solicite permissões de localização em segundo plano, a menos que você tenha um caso de uso válido, conforme descrito em Acesso mais seguro e transparente à localização do usuário.
Apoiar a degradação suave se a permissão não for concedida
Para manter uma boa experiência do usuário, projete seu app para que ele possa lidar adequadamente com as seguintes situações:
- Seu app não tem acesso a informações de localização.
- Seu app não tem acesso às informações de localização quando executado em segundo plano.
Você aprendeu a receber atualizações de localização no Android seguindo as práticas recomendadas em mente.
Saiba mais
- Amostra completa para usar a localização em segundo plano se você tiver um caso de uso válido.
- Solicitar atualizações de localização