Android - 定位資訊(GPS)相關類別使用說明
Android SDK為針對Location應用開發,提供了完整的<Location and Maps>模組與類別,讓開發者更容易地去結合
Google Map、擷取GPS或網路定位之資訊來完成自訂Application上的應用。
然而,使用Android提供的SDK說難不難,說簡單又不是很簡單,主要是因為它豐富的SDK,其中有些內容是需要花
不少時間去理解各模組運作的關係,才能成功的將結果的回報出來。
因此,我在撰寫擷取GPS資訊時,就一直遇到不少的問題,所以接下來,將針對GPS在取得的部分,加以說明二個重要
的元素:LocationManager與LocationListner,以及LocationProvider和相關使用上會有關聯的類別。
[基本準備]
在使用android.location裡的類別時,老樣子一定要記得到AppManiFile.xml去宣告user-premession,讓Activity可以正確
取得需要的SystemService。至於要使用GPS時,有三段指定要記得加上去,如下:
(a) 使用GPS模組:
1: //宣告Application會使用到GPS模組
2: <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
3:
4: //宣告當Application在Debug模式時,允許使用模擬資料(配合DDMS)
5: <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION" />
6:
(b) 使用網路模組(Wireless、AGPS):
1: //宣告Application使用到網路定位
2: <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
準備好基本設定之後,接下來進入重點的項目:
此類別提供連結到系統的Location服務,取得此服務也代表允許Application可以進一步承接設備週期性更新的地理資訊,
並且可以透過Intent將指定的地理資訊傳給特定的Application進行處理。由此可見,該類別則是運作定位服務的觸發者。
該類別本身無法被Instance(也就是不能被new),它需依賴以下的作法來取得:
1: //透過Context取得指定的服務
2: Context.getSystemService(Context.LOCATION_SERVICE).
上述程式片段提到的Context是整個Android的Activity運作裡最為特別的一個元素,它是個Abstract Class,
但是它被Android系統實作,因此Application中的每個Activity可以透過它取得資源、brocadcasting、Intent…等,
因此,Context它是非常重要的,在學習Android時會很常遇到它,需要特別注意。
了解LocaitonManager基本定義後,接下來就要了解它內部藏了些什麼,以下介紹幾個重要的屬性與方法:
(a) 取得Location Service相關參數與取得Location資訊(Provider、Location)
‧getAllProvider():
此方法回傳目前系統取得到的Location Provider清單。可以取得GPS、Network二種。
‧isProviderEnabled(LocationManager.GPS_PROVIDER | LocationManager.NETWORK_PROVIDER):
使用此方法確定目前何種Provider是可以使用的。
‧getLastKnownLocation(String provider):
擷取指定Provider離目前最近(最後一次)所得到的地理資訊。這個方法適用於如果Location Provider忽然關閉,
或有問題仍可以取得最後一次的地理資訊進行回報。
(b) 註冊監聽Location資訊/狀態更新事件
‧requestLocationUpdates(String provider, long minTime, float minDistance, LocationListener listener) :
‧requestLocationUpdates(String provider, long minTime, float minDistance, LocationListener listener, Looper looper):
要進行週期性監聽Location的狀態與資訊,需要向LocationManager註冊要監聽的對象,並且設定相關的參數當作觸發條件,
上述介紹的二個方法是一定要知道的,針對裡面的參數解釋如下:
b-1. provider:代表監聽Provider的類型:LocationManager.GPS_PROVIDER或LocationManager.NETWORK_PROVIDER;
b-2. minTime:代表最小多時間要進行週期性回報;單位:毫秒。通常設定為0,可以加快取得的時間;
b-3. minDistance:代表最小移動的距離要進行週期性回報;單位:公尺。通常設定為0,可以加快取得的機會;
b-4. looper:它是一個針對thread的message loop,用途是類似message queue來處理與佇列處理每一個收到的訊息;
第b-4.的參數比較少用到,通常會配合前3個參數進行註冊,但Looper何時會處理到呢,在下方[補充]會更詳細的解譯。
(c) 取消監聽Location資訊/狀態更新事件
‧removeUpdates(PendingIntent intent) :
‧removeUpdates(LocationListener listener):
用於取消任何目前在這個Activity上註冊的Pendingintent或LocaitonListener項目。取消之後,即使LocationManager
週期收到Location的資訊,也不會再被反應到指定的處理對象裡。通常在開發範例中看到的是直接使用LocationListener,
承接收到Location要處理的任務,但Pendingintent是什麼呢?簡單的說明如下:
它描述Intent、Brodcast、Service任務的一種象徵性的定義,當我們宣告一個Intent出來要做指定到特定的Activity或對象時,
startActivity會將該Intent送到PendingIntent再轉到另一個Activity。它由系統來負責維護,所以我們只需注意三種實例化的做法。
針對定位狀態與資訊改變時,由LocationManager發出觸發訊息,由指定的LocationListener來進行任務處理,
原本我以為該Listener相似Call value的方式,馬上按下去就會產出,後來發現不對,因為坐標定位的時間,
通常會比較久,所以避免程式整個被綁定,就透過LocationListener來做識別。以下將介紹四個重要的實作的事件:
(a) onLocationChanged(Location location):
當Location資訊改變時,LocationManager發出訊息,將觸發該事件。可以從傳入的Location參數取得相關的地理資訊。
(b) onProviderDisabled(String provider)/onProviderEnabled(String provider) :
當Provider被由User要求取消時,即觸發該事件。但要注意,當設定disable時,requestLocationUpdates也被呼叫,
那LocationManager會直接要求進入該事件,取消Provider的取得資訊。相反的,當Provider被User要求啟動時,
即會觸發onProviderEnabled事件。
(d) onStatusChanged(String provider, int status, Bundle extras):
該事件發生於當Provider使用的狀態變動時所觸發。因此,它傳入的參數有包括:Provider、Status與Bundle。定義如下:
‧status:代表目前Provider的狀態,有三個參數值:
OUT_OF_SERVICE(代表Provider不能用);TEMPORARILY_UNAVAILABLE(代表Provider暫時無法使用,但很快就會可用);
AVAILABLE(代表Provider正常運作)。
‧extras:一個可選擇使用的參數,如果有值代表是Provider一些額外的自訂參數。內容採用key/value儲存方式。
該類別為抽象類別,主要用於定義Location Provider為何(可能是GPS或Network),並且定期回報設備的地理資訊。
Provider可以配合一個<Criteria>物件來調整Provider接收GPS資訊的相關設定,包括:電量情境的使用、準確性等。
[重要] LocationListener與LocationManager運作的關係
了解LocationManager等另外相關的類別與事件處理之後,簡單透過下方的圖來說明,這些類別之間的關係。
[使用範例]
接下來做一個簡單的案例,實作取得GPS(從GPS硬體或Network取得),並且透過Toast顯示在畫面中。
(1) 建立一個Activity,並且有onCreate()增加取得LocationManager;
1: //宣告一個公用的LocationManager
2: private LocationManager gLManager;
3:
4: /** Called when the activity is first created. */
5: @Override
6: public void onCreate(Bundle savedInstanceState) {
7: super.onCreate(savedInstanceState);
8: setContentView(R.layout.main);
9:
10: //取得LocationService
11: gLManager = (LocationManager) this.getSystemService(LOCATION_SERVICE);
12:
13: String tMsg="";
14: try {
15: //指定向GPS裝置註冊要求取得地理資訊
16: gLManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0,
17: gLListenr);
18: }catch (Exception e){
19: tMsg = e.getMessage();
20: }
21: }
(2) 實作一個LocationListener(此處叫gLListener)類別,處理當接收到Location或Provider狀態改變的事件;
1: public LocationListener gLListenr = new LocationListener(){
2: //實作GPS位置被更新
3: public void onLocationChanged(Location location){
4: String tContent = String.format("緯度:%s\n經度:%s\n精度:%s\n標高:%s\n時間:%s\n速度:%s\n方位:%s\n",
5: location.getLatitude(),
6: location.getLongitude(),
7: location.getAccuracy(),
8: location.getAltitude(),
9: location.getTime(),
10: location.getSpeed(),
11: location.getBearing());
12: Toast.makeText(main.this, tContent, Toast.LENGTH_SHORT).show();
13: }
14:
15: @Override
16: public void onProviderDisabled(String provider) {
17: }
18:
19: @Override
20: public void onProviderEnabled(String provider) {
21: }
22:
23: @Override
24: public void onStatusChanged(String provider, int status, Bundle extras) {
25: }
26: };
照著上述二個步驟將程式建好就可以取得到GPS的座標資訊了,但別忘了宣告user-permission喔。
[補充]
〉Timer:
Timer在Android上按照Davlik的定義,它是獨立於程式thread之外的另一個thread,這代表什麼呢?
這代表當程式的Activity進入onStop或onPause,該Timer是不會停止下來的。透過Timer可以設定scheule
定期完成我們指定的TimerTask(),對於需要週期性處理的情形很適合。使用方法也很簡單如下:
1: Timer gTimer = new Timer();
2: //指定schedule要觸發的事件
3: TimerTask tTimerTask = new TimerTask(){
4: public void run(){
5: sendBroadcast(new Intent(ACTION));
6: }
7: };
8: //設定Timer執行週期,單位毫秒
9: gTimer.schedule(tTimerTask, 10000);
〉Looper:
〉Handler:
[新人知識]
‧final 修飾詞
其實我是第一次比較正式撰寫Java的程式語言,雖然在求學過程也有寫過一些,但實際上還是經驗不夠,
因此,邊學習邊把一些觀念給補齊。針對修飾詞的部分,找了一篇<Java 快速導覽 - 物件導向概念 final 成員>
把一些修飾詞的定義與使用規格加以了解。
[final修飾詞:代表常數的意思,無法被屬性、區域變數重新給值,final的方法在被繼承後也無法被override。]
======
以上是介紹如何使用Android所提供有關Location類別的使用經驗與自己覺得需要注意的地方,
個人覺得在撰寫相關擷取GPS的資訊,透過實機來測試會比較準確,使用DDMS來設定還是比較不方便些,
不過在學習過程最為困擾的是LocationManager與Listener的關聯,希望透過上圖可以幫助暸解。
如果有寫錯的地方也希望大家給我建議。謝謝。
References:
‧Location API and Google Maps in Android - Tutorial
‧Using GPS to get current location – Android tutorial
‧What is the simplest and most robust way to get the user's current location in Android?
‧How to Program Google Android
‧Android GPS timeout (有關使用GPS資訊的一些眉角)
‧Android find GPS location once, show loading dialog
‧Toast message not showing in service (必學,將Toast用在不同的Thread中)
‧Can't create handler inside thread that has not called Looper.prepare()