Share via


Android 上的位置服務

本指南介紹Android應用程式中的位置感知,並說明如何使用Android位置服務 API取得使用者的位置,以及Google Location Services API 提供的融合位置提供者。

Android 可讓您存取各種位置技術,例如細胞塔位置、Wi-Fi 和 GPS。 每個位置技術的詳細數據都會透過 位置提供者抽象化,讓應用程式以相同方式取得位置,而不論使用的提供者為何。 本指南介紹融合的位置提供者,這是 Google Play 服務的一部分,它會根據可用的提供者以及裝置的使用方式,以智慧方式判斷取得裝置位置的最佳方法。 Android 位置服務 API,並示範如何使用 與系統位置服務 LocationManager通訊。 本指南的第二個部分會使用 LocationManager探索Android位置服務 API。

作為一般經驗法則,應用程式應該偏好使用融合的位置提供者,只在必要時回復較舊的Android位置服務 API。

位置基本概念

在 Android 中,無論您選擇哪個 API 來使用位置數據,數個概念都保持不變。 本節介紹位置提供者和位置相關許可權。

位置提供者

內部會使用數種技術來找出使用者的位置。 所使用的硬體取決於為收集數據作業選取的位置提供者類型。 Android 使用三個位置提供者:

  • GPS 提供者 – GPS 提供最精確的位置、使用最多功率,併發揮最佳戶外效果。 此提供者使用 GPS 和輔助 GPS(aGPS)的組合,其會傳回行動電話塔收集的 GPS 數據。

  • 網路提供者 – 提供WiFi和行動電話數據的組合,包括由數據塔收集的GPS數據。 其使用的功率小於 GPS 提供者,但會傳回不同精確度的位置數據。

  • 被動提供者 – 使用其他應用程式或服務要求以在應用程式中產生位置數據的提供者的「piggyback」選項。 這是較不可靠但省電選項,適用於不需要固定位置更新才能運作的應用程式。

位置提供者不一定可用。 例如,我們可能想要將 GPS 用於我們的應用程式,但 GPS 可能會在 設定 中關閉,或者裝置可能根本沒有 GPS。 如果無法使用特定提供者,選擇該提供者可能會傳回 null

位置許可權

位置感知應用程式需要存取裝置的硬體感測器,才能接收 GPS、Wi-Fi 和行動數據。 存取權是透過應用程式 Android 指令清單中的適當許可權來控制。 有兩個可用許可權 - 視應用程式的需求和您選擇的 API 而定,您會想要允許一個:

  • ACCESS_FINE_LOCATION – 允許應用程式存取 GPS。 GPS 提供者和被動提供者選項的必要選項(被動提供者需要許可權才能存取另一個應用程式或服務收集的 GPS 數據)。 網路提供者選擇性許可權。

  • ACCESS_COARSE_LOCATION – 允許應用程式存取行動數據與Wi-Fi位置。 如果未ACCESS_FINE_LOCATION設定網路提供者,則為必要專案。

針對以 API 21 版 (Android 5.0 Lollipop) 或更高版本為目標的應用程式,您可以啟用 ACCESS_FINE_LOCATION 且仍可在沒有 GPS 硬體的裝置上執行。 如果您的應用程式需要 GPS 硬體,您應該明確地將元素新增 android.hardware.location.gpsuses-feature 至 Android 指令清單。 如需詳細資訊,請參閱 Android uses-feature 元素參考。

若要設定許可權,請展開SolutionPad中的 [屬性] 資料夾,然後按兩下 [AndroidManifest.xml]。 權限會列在 [必要許可權] 底下

Android 指令清單必要許可權設定的螢幕快照

設定其中一個許可權會告訴 Android 您的應用程式需要用戶的許可權,才能存取位置提供者。 執行 API 層級 22(Android 5.1) 或更低層級的裝置會在每次安裝應用程式時要求使用者授與這些許可權。 在執行 API 層級 23 (Android 6.0) 或更高版本的裝置上,應用程式應該先執行運行時間許可權檢查,再提出位置提供者的要求。

注意

注意:設定 ACCESS_FINE_LOCATION 表示存取粗略和精細的位置數據。 您不應該同時設定這兩個許可權,只有 應用程式需要運作的最低 許可權。

此代碼段是如何檢查應用程式是否具有權限的 ACCESS_FINE_LOCATION 範例:

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

應用程式必須能夠容忍使用者不會授與許可權的案例(或已撤銷許可權),並有辦法正常處理這種情況。 如需在 Xamarin.Android 中實作運行時間許可權檢查的詳細資訊,請參閱 許可權指南

使用融合位置提供者

融合位置提供者是 Android 應用程式從裝置接收位置更新的慣用方式,因為它會在運行時間有效率地選取位置提供者,以省電方式提供最佳位置資訊。 例如,使用者四處走動戶外會取得使用 GPS 的最佳位置讀取。 如果使用者接著在室內行走,GPS 的運作不佳(如果一切),融合的位置提供者可能會自動切換到WiFi,這在室內效果更好。

融合的位置提供者 API 提供各種不同的其他工具,以賦予位置感知應用程式的能力,包括地理柵欄和活動監視。 在本節中,我們將著重於設定 LocationClient、建立提供者和取得使用者位置的基本概念。

融合的位置提供者是Google Play服務的部分。 您必須在應用程式中正確安裝並設定 Google Play 服務套件,讓融合位置提供者 API 能夠運作,且裝置必須安裝 Google Play 服務 APK。

在 Xamarin.Android 應用程式可以使用融合的位置提供者之前,它必須將 Xamarin.GooglePlayServices.Location 套件新增至專案。 此外,應該將下列 using 語句新增至參考下列類別的任何來源檔案:

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

檢查是否已安裝Google Play服務

如果 Xamarin.Android 嘗試在未安裝 Google Play 服務時使用融合位置提供者,則會發生運行時例外狀況。 如果未安裝 Google Play 服務,則應用程式應該回復至上述的 Android 位置服務。 如果 Google Play 服務過期,則應用程式可能會向使用者顯示訊息,要求他們更新已安裝的 Google Play 服務版本。

此代碼段是 Android 活動如何以程式設計方式檢查是否已安裝 Google Play 服務的範例:

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

若要與融合位置提供者互動,Xamarin.Android 應用程式必須有 的 FusedLocationProviderClient實例。 這個類別會公開訂閱位置更新的必要方法,並擷取裝置的最後一個已知位置。

OnCreate活動的方法是取得 參考FusedLocationProviderClient的適當位置,如下列代碼段所示:

public class MainActivity: AppCompatActivity
{
    FusedLocationProviderClient fusedLocationProviderClient;

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

取得最後一個已知位置

此方法 FusedLocationProviderClient.GetLastLocationAsync() 為 Xamarin.Android 應用程式提供簡單的非封鎖方式,可快速取得裝置的最後一個已知位置,且程式代碼撰寫額外負荷最少。

此代碼段示範如何使用 GetLastLocationAsync 方法來擷取裝置的位置:

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);
    }
}

訂閱位置更新

Xamarin.Android 應用程式也可以使用 方法訂閱來自融合位置提供者 FusedLocationProviderClient.RequestLocationUpdatesAsync 的位置更新,如下列代碼段所示:

await fusedLocationProviderClient.RequestLocationUpdatesAsync(locationRequest, locationCallback);

此方法採用兩個參數:

  • Android.Gms.Location.LocationRequest – 物件 LocationRequest 是 Xamarin.Android 應用程式傳遞融合位置提供者應該如何運作的參數。 會 LocationRequest 保留資訊,例如應該提出要求的頻率,或正確位置更新的重要性。 例如,重要位置要求會導致裝置在判斷位置時使用 GPS,因而增加電源。 此代碼段示範如何為高精確度的位置建立 LocationRequest ,大約每五分鐘檢查一次位置更新(但要求之間不超過兩分鐘)。 融合位置提供者會使用 LocationRequest 作為嘗試判斷裝置位置時所要使用的位置提供者的指引:

    LocationRequest locationRequest = new LocationRequest()
                                      .SetPriority(LocationRequest.PriorityHighAccuracy)
                                      .SetInterval(60 * 1000 * 5)
                                      .SetFastestInterval(60 * 1000 * 2);
    
  • Android.Gms.Location.LocationCallback – 為了接收位置更新,Xamarin.Android 應用程式必須子類別抽象 LocationProvider 類。 這個類別公開了兩種方法,這些方法可能由融合位置提供者叫用,以使用位置資訊更新應用程式。 以下將更詳細地討論這一點。

若要通知 Xamarin.Android 應用程式位置更新,融合的位置提供者將會叫用 LocationCallBack.OnLocationResult(LocationResult result)。 參數 Android.Gms.Location.LocationResult 將包含更新位置資訊。

當融合位置提供者偵測到位置數據可用性變更時,它會呼叫 LocationProvider.OnLocationAvailability(LocationAvailability locationAvailability) 方法。 LocationAvailability.IsLocationAvailable如果 屬性傳true回 ,則可以假設所OnLocationResult報告的裝置位置結果正確且符合 所需的最新狀態LocationRequest。 如果 IsLocationAvailable 為 false,則不會傳 OnLocationResult回任何位置結果。

此代碼段是 物件的範例實作 LocationCallback

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.
        }
    }
}

使用Android位置服務 API

Android 位置服務是較舊的 API,可用於在 Android 上使用位置資訊。 位置數據是由硬體感測器所收集,並由系統服務所收集,而系統服務會使用 LocationManager 類別和 ILocationListener來存取該應用程式。

位置服務最適合在未安裝Google Play服務的裝置上執行的應用程式。

位置服務是系統管理的特殊服務類型。 系統服務會與裝置硬體互動,且一律正在執行。 若要點選應用程式中的位置更新,我們會使用 LocationManagerRequestLocationUpdates 呼叫,訂閱來自系統位置服務的位置更新。

若要使用Android位置服務取得使用者的位置,需要幾個步驟:

  1. 取得服務的 LocationManager 參考。
  2. 實作 介面, ILocationListener 並在位置變更時處理事件。
  3. LocationManager使用 來要求指定提供者的位置更新。 上 ILocationListener 一個步驟中的 將用來接收的 LocationManager回呼。
  4. 當應用程式不再適合接收更新時,停止位置更新。

位置管理員

我們可以使用 類別的 LocationManager 實例來存取系統位置服務。 LocationManager 是一個特殊的類別,可讓我們與系統位置服務互動,並在其上呼叫方法。 應用程式可以藉由呼叫 GetSystemService 並傳入服務類型來取得 的LocationManager參考,如下所示:

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

OnCreate 是取得 參考 LocationManager的好位置。 最好將 保留 LocationManager 為類別變數,以便我們可以在活動生命週期的各個點呼叫它。

從 LocationManager 要求位置更新

一旦應用程式具有的 LocationManager參考,它必須告知 LocationManager 所需的位置資訊類型,以及該資訊要更新的頻率。 在物件上LocationManager呼叫 RequestLocationUpdates ,並傳入一些更新準則和將接收位置更新的回呼,以執行此動作。 此回呼是必須實 ILocationListener 作 介面的類型(本指南稍後會詳細說明)。

方法 RequestLocationUpdates 會告知系統位置服務您的應用程式想要開始接收位置更新。 這個方法可讓您指定提供者,以及控制更新頻率的時間和距離臨界值。 例如,下列方法會每隔 2000 毫秒向 GPS 位置提供者要求位置更新,而且只有在位置變更超過 1 公尺時:

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

應用程式應該只會視需要要求位置更新,應用程式才能正常執行。 這會保留電池使用時間,併為用戶創造更好的體驗。

回應 LocationManager 的更新

一旦應用程式向 LocationManager要求更新,即可藉由實 ILocationListener 作 介面,從服務接收資訊。 這個介面提供四種方法來接聽位置服務和位置提供者 OnLocationChanged 當使用者的位置變更足以符合要求位置更新時所設定的 Criteria 變更資格時,系統會呼叫 OnLocationChanged 系統。

下列程式代碼顯示 介面中 ILocationListener 的方法:

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

取消訂閱LocationManager更新

為了節省系統資源,應用程式應儘快取消訂閱位置更新。 方法 RemoveUpdatesLocationManager 告知 停止將更新傳送至應用程式。 例如,活動可能會在 方法中OnPause呼叫 RemoveUpdates ,以便在應用程式不需要位置更新時,如果應用程式不在畫面上時不需要位置更新,我們就能夠節省電源:

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

如果您的應用程式需要在背景中取得位置更新,您要建立訂閱系統位置服務的自定義服務。 如需 詳細資訊,請參閱使用Android服務 的背景設定指南。

判斷 LocationManager 的最佳位置提供者

上述應用程式會將 GPS 設定為位置提供者。 不過,在所有情況下,GPS 可能無法使用,例如裝置在室內或沒有 GPS 接收器。 如果是這種情況,則結果會 null 傳回 Provider。

若要讓您的 app 在無法使用 GPS 時運作,您可以使用 GetBestProvider 方法在應用程式啟動時要求最佳的可用(裝置支援和使用者啟用)位置提供者。 您可以使用 物件來告知GetBestProvider提供者的需求,例如精確度和電源,Criteria而不是傳入特定提供者。 GetBestProvider 會傳回指定之 Criteria 的最佳提供者。

下列程式代碼示範如何取得最佳的可用提供者,並在要求位置更新時使用它:

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");
}

注意

如果使用者已停用所有位置提供者, GetBestProvider 將會傳回 null。 若要查看此程式代碼在真實裝置上的運作方式,請務必在Google 設定 > 位置>模式啟用 GPS、Wi-Fi 和行動電話網路,如下列螢幕快照所示:

Android 手機上 設定 位置模式畫面

下列螢幕快照示範使用 GetBestProvider執行的位置應用程式:

顯示緯度、經度和提供者的 GetBestProvider 應用程式

請記住, GetBestProvider 不會動態變更提供者。 相反地,它會在活動生命週期期間判斷最佳的可用提供者一次。 如果提供者狀態在設定之後變更,應用程式將需要方法 - OnProviderEnabledOnProviderDisabledOnStatusChanged - 中的其他ILocationListener程式碼來處理與提供者參數相關的每一種可能性。

摘要

本指南涵蓋使用 Android 位置服務和 Google 位置服務 API 的融合位置提供者來取得使用者的位置。