Serviços de localização no Android

Este guia apresenta o reconhecimento de localização em aplicativos Android e ilustra como obter a localização do usuário usando a API Android Location Service, bem como o provedor de localização fundido disponível com a API Google Location Services.

O Android fornece acesso a várias tecnologias de localização, como localização de torres de celular, Wi-Fi e GPS. Os detalhes de cada tecnologia de localização são abstraídos por meio de provedores de localização, permitindo que os aplicativos obtenham locais da mesma maneira, independentemente do provedor usado. Este guia apresenta o provedor de localização fundido, uma parte do Google Play Services, que determina de forma inteligente a melhor maneira de obter a localização dos dispositivos com base em quais provedores estão disponíveis e como o dispositivo está sendo usado. API do Serviço de Localização do Android e mostra como se comunicar com o Serviço de Localização do sistema usando um LocationManagerarquivo . A segunda parte do guia explora a API de serviços de localização do Android usando o LocationManager.

Como regra geral, os aplicativos devem preferir usar o provedor de localização fundido, recorrendo à API Android Location Service mais antiga somente quando necessário.

Fundamentos de localização

No Android, não importa qual API você escolha para trabalhar com dados de localização, vários conceitos permanecem os mesmos. Esta seção apresenta os provedores de localização e as permissões relacionadas à localização.

Provedores de localização

Várias tecnologias são usadas internamente para identificar a localização do usuário. O hardware usado depende do tipo de provedor de localização selecionado para o trabalho de coleta de dados. O Android usa três provedores de localização:

  • Provedor de GPS – O GPS fornece a localização mais precisa, usa mais energia e funciona melhor ao ar livre. Este provedor usa uma combinação de GPS e GPS assistido (aGPS), que retorna dados de GPS coletados por torres de celular.

  • Provedor de rede – Fornece uma combinação de dados WiFi e celular, incluindo dados aGPS coletados por torres de celular. Ele usa menos energia do que o provedor de GPS, mas retorna dados de localização com precisão variável.

  • Provedor passivo – Uma opção "piggyback" usando provedores solicitados por outros aplicativos ou serviços para gerar dados de localização em um aplicativo. Esta é uma opção menos confiável, mas com economia de energia, ideal para aplicativos que não exigem atualizações constantes de localização para funcionar.

Os provedores de localização nem sempre estão disponíveis. Por exemplo, podemos querer usar o GPS para nosso aplicativo, mas o GPS pode estar desativado em Configurações ou o dispositivo pode não ter GPS. Se um provedor específico não estiver disponível, a escolha desse provedor poderá retornar null.

Permissões de localização

Um aplicativo com reconhecimento de localização precisa acessar os sensores de hardware de um dispositivo para receber dados de GPS, Wi-Fi e celular. O acesso é controlado por meio de permissões apropriadas no Manifesto do Android do aplicativo. Há duas permissões disponíveis – dependendo dos requisitos do seu aplicativo e da sua escolha de API, você desejará permitir uma:

  • ACCESS_FINE_LOCATION – Permite que um aplicativo acesse o GPS. Necessário para as opções de Provedor de GPS e Provedor Passivo (o Provedor Passivo precisa de permissão para acessar os dados de GPS coletados por outro aplicativo ou Serviço). Permissão opcional para o provedor de rede.

  • ACCESS_COARSE_LOCATION – Permite que um aplicativo acesse a localização de celular e Wi-Fi. Necessário para o provedor de rede se ACCESS_FINE_LOCATION não estiver definido.

Para apps direcionados à API versão 21 (Android 5.0 Lollipop) ou superior, é possível ativar ACCESS_FINE_LOCATION e continuar sendo executados em dispositivos que não têm hardware de GPS. Se o app exigir hardware de GPS, adicione explicitamente um android.hardware.location.gps uses-feature elemento ao manifesto do Android. Para obter mais informações, consulte a referência do elemento uses-feature do Android.

Para definir as permissões, expanda a pasta Propriedades no Painel de Soluções e clique duas vezes em AndroidManifest.xml. As permissões serão listadas em Permissões necessárias:

Captura de tela das configurações de permissões necessárias do manifesto do Android

A configuração de qualquer uma dessas permissões informa ao Android que seu aplicativo precisa de permissão do usuário para acessar os provedores de localização. Os dispositivos que executam a API de nível 22 (Android 5.1) ou versões anteriores solicitarão que o usuário conceda essas permissões sempre que o app for instalado. Em dispositivos com a API de nível 23 (Android 6.0) ou mais recente, o app precisa realizar uma verificação de permissão em tempo de execução antes de fazer uma solicitação ao provedor de localização.

Observação

Nota: A configuração ACCESS_FINE_LOCATION implica acesso a dados de localização grosseiros e finos. Você nunca deve ter que definir ambas as permissões, apenas a permissão mínima que seu aplicativo requer para funcionar.

Este snippet é um exemplo de como verificar se um aplicativo tem permissão para a ACCESS_FINE_LOCATION permissão:

 if (ContextCompat.CheckSelfPermission(this, Manifest.Permission.AccessFineLocation) == Permission.Granted)
{
    StartRequestingLocationUpdates();
    isRequestingLocationUpdates = true;
}
else
{
    // The app does not have permission ACCESS_FINE_LOCATION 
}

Os aplicativos devem ser tolerantes com o cenário em que o usuário não concederá permissão (ou revogou a permissão) e ter uma maneira de lidar normalmente com essa situação. Consulte o guia Permissões para obter mais detalhes sobre como implementar verificações de permissão em tempo de execução no Xamarin.Android.

Usando o provedor de localização de fusível

O provedor de localização combinado é a maneira preferida para os aplicativos Android receberem atualizações de localização do dispositivo, pois selecionará com eficiência o provedor de localização durante o tempo de execução para fornecer as melhores informações de localização de maneira eficiente em termos de bateria. Por exemplo, um usuário andando ao ar livre obtém a melhor leitura de localização com GPS. Se o usuário andar em ambientes fechados, onde o GPS funciona mal (se é que funciona), o provedor de localização fundido pode mudar automaticamente para WiFi, que funciona melhor em ambientes fechados.

A API do provedor de localização fundida fornece uma variedade de outras ferramentas para capacitar aplicativos com reconhecimento de localização, incluindo geofencing e monitoramento de atividades. Nesta seção, vamos nos concentrar nos conceitos básicos de configuração do LocationClient, estabelecendo provedores e obtendo a localização do usuário.

O provedor de localização combinada faz parte do Google Play Services. O pacote do Google Play Services deve ser instalado e configurado corretamente no aplicativo para que a API do provedor de localização fundida funcione, e o dispositivo deve ter o APK do Google Play Services instalado.

Antes que um aplicativo Xamarin.Android possa usar o provedor de localização fundido, ele deve adicionar o pacote Xamarin.GooglePlayServices.Location ao projeto. Além disso, as seguintes using instruções devem ser adicionadas a todos os arquivos de origem que fazem referência às classes descritas abaixo:

using Android.Gms.Common;
using Android.Gms.Location;

Verificando se o Google Play Services está instalado

Um Xamarin.Android falhará se tentar usar o provedor de localização fundido quando o Google Play Services não estiver instalado (ou desatualizado), ocorrerá uma exceção de runtime. Se o Google Play Services não estiver instalado, o aplicativo deverá retornar ao Serviço de Localização do Android discutido acima. Se o Google Play Services estiver desatualizado, o aplicativo poderá exibir uma mensagem para o usuário solicitando que ele atualize a versão instalada do Google Play Services.

Este snippet é um exemplo de como uma atividade do Android pode verificar programaticamente se o Google Play Services está instalado:

bool IsGooglePlayServicesInstalled()
{
    var queryResult = GoogleApiAvailability.Instance.IsGooglePlayServicesAvailable(this);
    if (queryResult == ConnectionResult.Success)
    {
        Log.Info("MainActivity", "Google Play Services is installed on this device.");
        return true;
    }

    if (GoogleApiAvailability.Instance.IsUserResolvableError(queryResult))
    {
        // Check if there is a way the user can resolve the issue
        var errorString = GoogleApiAvailability.Instance.GetErrorString(queryResult);
        Log.Error("MainActivity", "There is a problem with Google Play Services on this device: {0} - {1}",
                  queryResult, errorString);

        // Alternately, display the error to the user.
    }

    return false;
}

FusedLocationProviderClient

Para interagir com o provedor de localização fundido, um aplicativo Xamarin.Android deve ter uma instância do FusedLocationProviderClient. Essa classe expõe os métodos necessários para assinar atualizações de localização e recuperar a última localização conhecida do dispositivo.

O OnCreate método de uma atividade é um local adequado para obter uma referência ao FusedLocationProviderClient, conforme demonstrado no seguinte trecho de código:

public class MainActivity: AppCompatActivity
{
    FusedLocationProviderClient fusedLocationProviderClient;

    protected override void OnCreate(Bundle bundle) 
    {
        fusedLocationProviderClient = LocationServices.GetFusedLocationProviderClient(this);
    }
}

Obtendo a última localização conhecida

O FusedLocationProviderClient.GetLastLocationAsync() método fornece uma maneira simples e sem bloqueio para um aplicativo Xamarin.Android obter rapidamente o último local conhecido do dispositivo com sobrecarga mínima de codificação.

Este snippet mostra como usar o GetLastLocationAsync método para recuperar a localização do dispositivo:

async Task GetLastLocationFromDevice()
{
    // This method assumes that the necessary run-time permission checks have succeeded.
    getLastLocationButton.SetText(Resource.String.getting_last_location);
    Android.Locations.Location location = await fusedLocationProviderClient.GetLastLocationAsync();

    if (location == null)
    {
        // Seldom happens, but should code that handles this scenario
    }
    else
    {
        // Do something with the location 
        Log.Debug("Sample", "The latitude is " + location.Latitude);
    }
}

Assinando atualizações de localização

Um aplicativo Xamarin.Android também pode assinar atualizações de localização do provedor de localização fundido usando o FusedLocationProviderClient.RequestLocationUpdatesAsync método, conforme mostrado neste snippet de código:

await fusedLocationProviderClient.RequestLocationUpdatesAsync(locationRequest, locationCallback);

Este método usa dois parâmetros:

  • Android.Gms.Location.LocationRequest – Um LocationRequest objeto é como um aplicativo Xamarin.Android passa os parâmetros sobre como o provedor de localização fundido deve funcionar. O LocationRequest contém informações como a frequência com que as solicitações devem ser feitas ou a importância de uma atualização de localização precisa. Por exemplo, uma solicitação de localização importante fará com que o dispositivo use o GPS e, consequentemente, mais energia ao determinar a localização. Este snippet de código mostra como criar um LocationRequest para um local com alta precisão, verificando aproximadamente a cada cinco minutos se há uma atualização de local (mas não antes de dois minutos entre as solicitações). O provedor de localização fundido usará um LocationRequest como orientação para qual provedor de localização usar ao tentar determinar a localização do dispositivo:

    LocationRequest locationRequest = new LocationRequest()
                                      .SetPriority(LocationRequest.PriorityHighAccuracy)
                                      .SetInterval(60 * 1000 * 5)
                                      .SetFastestInterval(60 * 1000 * 2);
    
  • Android.Gms.Location.LocationCallback – Para receber atualizações de localização, um aplicativo Xamarin.Android deve subclasse a LocationProvider classe abstrata. Essa classe expôs dois métodos que podem ser invocados pelo provedor de localização fundido para atualizar o aplicativo com informações de localização. Isso será discutido com mais detalhes abaixo.

Para notificar um aplicativo Xamarin.Android sobre uma atualização de localização, o provedor de localização fundido invocará o LocationCallBack.OnLocationResult(LocationResult result). O Android.Gms.Location.LocationResult parâmetro conterá as informações do local de atualização.

Quando o provedor de localização fundido detectar uma alteração na disponibilidade de dados de localização, ele chamará o LocationProvider.OnLocationAvailability(LocationAvailability locationAvailability) método. Se a LocationAvailability.IsLocationAvailable propriedade retornar true, pode-se supor que os resultados de localização do dispositivo relatados por OnLocationResult são tão precisos e atualizados quanto exigido pelo LocationRequest. Se IsLocationAvailable for falso, nenhum resultado de localização será retornado por OnLocationResult.

Este snippet de código é um exemplo de implementação do LocationCallback objeto:

public class FusedLocationProviderCallback : LocationCallback
{
    readonly MainActivity activity;

    public FusedLocationProviderCallback(MainActivity activity)
    {
        this.activity = activity;
    }

    public override void OnLocationAvailability(LocationAvailability locationAvailability)
    {
        Log.Debug("FusedLocationProviderSample", "IsLocationAvailable: {0}",locationAvailability.IsLocationAvailable);
    }

    public override void OnLocationResult(LocationResult result)
    {
        if (result.Locations.Any())
        {
            var location = result.Locations.First();
            Log.Debug("Sample", "The latitude is :" + location.Latitude);
        }
        else
        {
            // No locations to work with.
        }
    }
}

Como usar a API do serviço de localização do Android

O Serviço de Localização do Android é uma API mais antiga para usar informações de localização no Android. Os dados de localização são coletados por sensores de hardware e coletados por um serviço do sistema, que é acessado no aplicativo com uma LocationManager classe e um ILocationListener.

O Serviço de localização é mais adequado para aplicativos que devem ser executados em dispositivos que não têm o Google Play Services instalado.

O Serviço de Localização é um tipo especial de Serviço gerenciado pelo Sistema. Um serviço do sistema interage com o hardware do dispositivo e está sempre em execução. Para acessar as atualizações de localização em nosso aplicativo, assinaremos as atualizações de localização do Serviço de Localização do sistema usando uma LocationManager e uma RequestLocationUpdates chamada.

Para obter a localização do usuário usando o Serviço de Localização do Android, são necessárias várias etapas:

  1. Obtenha uma referência ao LocationManager serviço.
  2. Implemente a ILocationListener interface e manipule eventos quando o local for alterado.
  3. Use o LocationManager para solicitar atualizações de localização para um provedor especificado. O ILocationListener da etapa anterior será usado para receber retornos de chamada do LocationManager.
  4. Interrompa as atualizações de localização quando o aplicativo não for mais apropriado receber atualizações.

Gerente de localização

Podemos acessar o serviço de localização do sistema com uma instância da LocationManager classe. LocationManager é uma classe especial que nos permite interagir com o serviço de localização do sistema e chamar métodos nele. Um aplicativo pode obter uma referência ao LocationManager chamando GetSystemService e passando um tipo de serviço, conforme mostrado abaixo:

LocationManager locationManager = (LocationManager) GetSystemService(Context.LocationService);

OnCreate é um bom lugar para obter uma referência ao LocationManager. É uma boa ideia manter a LocationManager variável de classe as, para que possamos chamá-la em vários pontos do ciclo de vida da Atividade.

Solicitar atualizações de localização do LocationManager

Depois que o aplicativo tiver uma referência ao LocationManager, ele precisará informar que LocationManager tipo de informações de localização são necessárias e com que frequência essas informações devem ser atualizadas. Faça isso chamando RequestLocationUpdates o LocationManager objeto e passando alguns critérios para atualizações e um retorno de chamada que receberá as atualizações de localização. Esse retorno de chamada é um tipo que deve implementar a ILocationListener interface (descrita com mais detalhes posteriormente neste guia).

O RequestLocationUpdates método informa ao serviço de localização do sistema que seu aplicativo gostaria de começar a receber atualizações de localização. Esse método permite especificar o provedor, bem como os limites de tempo e distância para controlar a frequência de atualização. Por exemplo, o método abaixo solicita atualizações de localização do provedor de localização GPS a cada 2000 milissegundos e somente quando a localização muda mais de 1 metro:

// For this example, this method is part of a class that implements ILocationListener, described below
locationManager.RequestLocationUpdates(LocationManager.GpsProvider, 2000, 1, this);

Um aplicativo deve solicitar atualizações de localização apenas com a frequência necessária para que o aplicativo tenha um bom desempenho. Isso preserva a vida útil da bateria e cria uma experiência melhor para o usuário.

Respondendo a atualizações do LocationManager

Depois que um aplicativo solicita atualizações do LocationManager, ele pode receber informações do Serviço implementando a ILocationListener interface. Essa interface fornece quatro métodos para ouvir o serviço de localização e o provedor de localização, OnLocationChanged. O sistema chamará OnLocationChanged quando a localização do usuário mudar o suficiente para se qualificar como uma mudança de localização de acordo com os critérios definidos ao solicitar atualizações de localização.

O código a seguir mostra os métodos na ILocationListener interface:

public class MainActivity : AppCompatActivity, ILocationListener
{
    TextView latitude;
    TextView longitude;
    
    public void OnLocationChanged (Location location)
    {
        // called when the location has been updated.
    }
    
    public OnProviderDisabled(string locationProvider)
    {
        // called when the user disables the provider
    }
    
    public OnProviderEnabled(string locationProvider)
    {
        // called when the user enables the provider
    }
    
    public OnStatusChanged(string locationProvider, Availability status, Bundle extras)
    {
        // called when the status of the provider changes (there are a variety of reasons for this)
    }
}

Cancelar a assinatura de atualizações do LocationManager

Para conservar os recursos do sistema, um aplicativo deve cancelar a assinatura das atualizações de localização o mais rápido possível. O RemoveUpdates método diz ao para LocationManager parar de enviar atualizações para nosso aplicativo. Por exemplo, uma Activity pode chamar RemoveUpdates o OnPause método para que possamos economizar energia se um aplicativo não precisar de atualizações de localização enquanto sua Activity não estiver na tela:

protected override void OnPause ()
{
    base.OnPause ();
    locationManager.RemoveUpdates (this);
}

Se o aplicativo precisar obter atualizações de localização em segundo plano, você desejará criar um serviço personalizado que assine o serviço de localização do sistema. Consulte o guia Backgrounding with Android Services para obter mais informações.

Determinando o melhor provedor de localização para o LocationManager

O aplicativo acima define o GPS como o provedor de localização. No entanto, o GPS pode não estar disponível em todos os casos, como se o dispositivo estiver em ambientes fechados ou não tiver um receptor GPS. Se for esse o caso, o resultado é um null retorno para o Provedor.

Para fazer seu aplicativo funcionar quando o GPS não estiver disponível, use o GetBestProvider método para solicitar o melhor provedor de localização disponível (compatível com o dispositivo e habilitado para o usuário) na inicialização do aplicativo. Em vez de passar um provedor específico, você pode informar GetBestProvider os requisitos para o provedor - como precisão e potência - com um Criteria objeto. GetBestProvider retorna o melhor provedor para os critérios fornecidos.

O código a seguir mostra como obter o melhor provedor disponível e usá-lo ao solicitar atualizações de localização:

Criteria locationCriteria = new Criteria();   
locationCriteria.Accuracy = Accuracy.Coarse;
locationCriteria.PowerRequirement = Power.Medium;

locationProvider = locationManager.GetBestProvider(locationCriteria, true);

if(locationProvider != null)
{
    locationManager.RequestLocationUpdates (locationProvider, 2000, 1, this);
}
else
{
    Log.Info(tag, "No location providers available");
}

Observação

Se o usuário tiver desativado todos os provedores de localização, GetBestProvider retornará null. Para ver como esse código funciona em um dispositivo real, certifique-se de ativar o GPS, o Wi-Fi e as redes celulares no Modo de localização > das configurações > do Google, conforme mostrado nesta captura de tela:

Configurações Tela do modo de localização em um telefone Android

A captura de tela abaixo demonstra o aplicativo de localização em execução usando GetBestProvider:

Aplicativo GetBestProvider exibindo latitude, longitude e provedor

Lembre-se de que GetBestProvider isso não altera o provedor dinamicamente. Em vez disso, ele determina o melhor provedor disponível uma vez durante o ciclo de vida da atividade. Se o status do provedor for alterado depois de definido, o aplicativo exigirá código adicional nos ILocationListener métodos – OnProviderEnabled, OnProviderDisablede OnStatusChanged – para lidar com todas as possibilidades relacionadas à troca de provedor.

Resumo

Este guia abordou a obtenção da localização do usuário usando o Serviço de localização do Android e o provedor de localização fundido da API de Serviços de Localização do Google.