前言:
HI阻逮,歡迎來到《每周一博》粱快。從九月開始,我將繼續(xù)每周更新一篇博客叔扼,配合不定期更新的喜馬拉雅事哭,讓自己一直保持學(xué)習(xí)的狀態(tài)。今天是九月第一周瓜富,我先給大家分享一些和定位相關(guān)的知識鳍咱。
Android提供了定位服務(wù)和地理編碼服務(wù),它們分別是由LocationManager和Geocoder來提供的与柑,下面我來分別介紹這2部分內(nèi)容谤辜。
一. LocationManager:
LocationManager系統(tǒng)服務(wù)是位置服務(wù)的核心組件蓄坏,它提供了一系列方法來處理與位置相關(guān)的問題,比如查詢上一個已知位置丑念,定期更新設(shè)備的地理位置涡戳,或者當(dāng)設(shè)備進入給定地理位置附近時,觸發(fā)應(yīng)用指定意圖等渠欺;
(A). 使用LocationManager需要以下過程妹蔽;
1.獲取LocationManager,它不能直接實例化:
LocationManager lm = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
2.了解LocationProvider:它是位置信息提供者挠将,系統(tǒng)一般提供三種方式獲取地理位置信息:
(1)GPS_PROVIDER:通過 GPS 來獲取地理位置的經(jīng)緯度信息胳岂;
優(yōu)點:獲取地理位置信息精確度高;
缺點:只能在戶外使用舔稀,獲取經(jīng)緯度信息耗時乳丰,耗電;
(2)NETWORK_PROVIDER:通過移動網(wǎng)絡(luò)的基站或者 Wi-Fi 來獲取地理位置内贮;
優(yōu)點:只要有網(wǎng)絡(luò)产园,就可以快速定位,室內(nèi)室外都可夜郁;
缺點:精確度不高什燕;
(3)PASSIVE_PROVIDER:被動接收更新地理位置信息,而不用自己請求地理位置信息竞端。 PASSIVE_PROVIDER 返回的位置是通過其他 providers 產(chǎn)生的屎即,可以查詢 getProvider() 方法決定位置更新的由來,需要 ACCESS_FINE_LOCATION 權(quán)限事富,但是如果未啟用 GPS技俐,則此 provider 可能只返回粗略位置匹配;
獲取provider的方法有g(shù)etProviders统台,getAllProviders雕擂,getBestProvider(根據(jù)一組條件來返回合適的provider)
List<String> list = locationManager.getProviders(true);
if (list != null) {
for (String x : list) {
Log.e("gzq", "name:" + x);
}
}
LocationProvider lpGps = locationManager.getProvider(LocationManager.GPS_PROVIDER);
LocationProvider lpNet = locationManager.getProvider(LocationManager.NETWORK_PROVIDER);
LocationProvider lpPsv = locationManager.getProvider(LocationManager.PASSIVE_PROVIDER);
Criteria criteria = new Criteria();
// Criteria是一組篩選條件
criteria.setAccuracy(Criteria.ACCURACY_FINE);
//設(shè)置定位精準(zhǔn)度
criteria.setAltitudeRequired(false);
//是否要求海拔
criteria.setBearingRequired(true);
//是否要求方向
criteria.setCostAllowed(true);
//是否要求收費
criteria.setSpeedRequired(true);
//是否要求速度
criteria.setPowerRequirement(Criteria.NO_REQUIREMENT);
//設(shè)置電池耗電要求
criteria.setBearingAccuracy(Criteria.ACCURACY_HIGH);
//設(shè)置方向精確度
criteria.setSpeedAccuracy(Criteria.ACCURACY_HIGH);
//設(shè)置速度精確度
criteria.setHorizontalAccuracy(Criteria.ACCURACY_HIGH);
//設(shè)置水平方向精確度
criteria.setVerticalAccuracy(Criteria.ACCURACY_HIGH);
//設(shè)置垂直方向精確度
//返回滿足條件的當(dāng)前設(shè)備可用的provider,第二個參數(shù)為false時返回當(dāng)前設(shè)備所有provider中最符合條件的那個provider贱勃,但是不一定可用
String mProvider = locationManager.getBestProvider(criteria, true);
if (mProvider != null) {
Log.e("gzq", "mProvider:" + mProvider);
}
}
打印出來的結(jié)果就是passive井赌,gps,network贵扰;
3.聲明權(quán)限
(1)ACCESS_FINE_LOCATION是精確位置族展,如果使用GPS_PROVIDER或者同時使用GPS_PROVIDER和NETWORK_PROVIDER,需聲明該權(quán)限拔鹰,它對于這兩個provider都是有效的仪缸;
(2)ACCESS_COARSE_LOCATION是粗略位置,該權(quán)限只針對NETWORK_PROVIDER列肢。
4.注冊一個位置監(jiān)聽器來接受結(jié)果
private final class MyLocationListener implements LocationListener{
public void onLocationChanged(Location location) {
Log.e("gzq", "onLocationChanged" + location.toString());
}
public void onStatusChanged(String provider, int status, Bundle extras) {
Log.e("gzq", "onStatusChanged" + status);
}
public void onProviderEnabled(String provider) {
Log.e("gzq", "onProviderEnabled");
}
public void onProviderDisabled(String provider) {
Log.e("gzq", "onProviderDisabled");
}
}
這個回調(diào)里面有4個方法恰画;
(1)onLocationChanged:當(dāng)位置發(fā)生改變后就會回調(diào)該方法宾茂,經(jīng)緯度相關(guān)信息存在Location里面;
(2)onStatusChanged:我們所采用的provider狀態(tài)改變時會回調(diào)拴还,該狀態(tài)有3種跨晴;
LocationProvider.OUT_OF_SERVICE = 0:無服務(wù)
LocationProvider.AVAILABLE = 2:provider可用
LocationProvider.TEMPORARILY_UNAVAILABLE = 1:provider不可用
(3)onProviderEnabled:當(dāng)provider可用時被觸發(fā),比如定位模式切換到了使用精確位置時GPSProvider就會回調(diào)該方法片林;
(4)onProviderDisabled:當(dāng)provider不可用時被觸發(fā)端盆,比如定位模式切換到了使用使用網(wǎng)絡(luò)定位時GPSProvider就會回調(diào)該方法;
5.獲取位置信息费封,調(diào)用監(jiān)聽方法焕妙,不過在獲取位置前先判斷一下要調(diào)用的provider是否可用;
if (mLocationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 5,10, locationListener);
}
這個方法表明要跟蹤GPS位置的變化弓摘,并且每5秒刷新一次焚鹊,同時兩次的位置的間隔要超過10米;
在不需要位置的時候解注冊監(jiān)聽:
locationManager.removeUpdates(locationListener);
關(guān)于requestLocationUpdates有一些問題需要注意:
(1)剛才我們傳入的是Listener,其實也可以用PendingIntent來代替Listener韧献,當(dāng)位置更新時會通過廣播回調(diào)末患,使用2個鍵KEY_LOCATION_CHANGED和Location來接收位置變化;
(2)參數(shù)這里里可以傳遞一個Looper锤窑,如果不指定的話璧针,則調(diào)用線程必須已經(jīng)是一個Looper線程,比如調(diào)用Activity的主線程渊啰,如果指定了Looper探橱,則在提供的Looper線程上進行回調(diào);
(B). LocationManager還提供了一些其他有用的方法:
1.獲取緩存中的位置信息getLastKnownLocation
Location location = lm.getLastKnownLocation(LocationManager.GPS_PROVIDER);
該方法不會發(fā)起監(jiān)聽虽抄,返回的是上一次的位置信息,但此前如果沒有位置更新的話独柑,返回的位置信息可能是錯誤的迈窟;
2.獲取一次定位結(jié)果requestSingleUpdate
locationManager.requestSingleUpdate(LocationManager.GPS_PROVIDER,locationListener忌栅,null);
如果不想一直監(jiān)聽位置信息车酣,那么可以用requestSingleUpdate來實現(xiàn)只請求一次定位,該方法也要在主線程上執(zhí)行索绪;
3.發(fā)送輔助指令sendExtraCommand
mLocationManager.sendExtraCommand("LOC","NOTIFY_DOWNLOAD", extras);
可以通過Bundle參數(shù)來發(fā)送相關(guān)信息湖员;
4.判斷provider是否可用
mLocationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
5.添加地理圍欄
locationManager.addProximityAlert(38.234, 114.234, 5, -1, PendingIntent.getBroadcast(this, 1, new Intent(), 3));
可以設(shè)置一個區(qū)域,當(dāng)進入或離開這個區(qū)域的時候會收到通知瑞驱,前兩個參數(shù)指定一個點娘摔,第三個參數(shù)是半徑,第四個參數(shù)是超時時間唤反,設(shè)置為-1表示不存在超時凳寺,最后一個是廣播接收器鸭津。
觸發(fā)的Intent將使用鍵KEY_PROXIMITY_ENTERING,如果值為true肠缨,則設(shè)備進入鄰近區(qū)域逆趋,如果是false,說明設(shè)備離開該區(qū)域晒奕。
二. Geocoder:
上面介紹了LocationManager的使用闻书,下面介紹下地理編碼的使用。
Geocoder 用于處理地理編碼和反地理編碼脑慧,地理編碼是將街道地址或其他地理位置變換為經(jīng)緯度的過程魄眉。逆向地理編碼是將經(jīng)緯度轉(zhuǎn)換為地址的過程。反地理編碼位置描述中的細(xì)節(jié)數(shù)量可能會有所不同漾橙,例如可能包含最近建筑物的完整街道地址杆融,而另一個可能只包含城市名稱和郵政編碼。
Geocoder 類需要一個未包含在核心Android框架中的服務(wù)霜运。如果平臺中沒有該服務(wù)脾歇,Geocoder查詢方法將返回一個空列表。使用 isPresent() 方法可以來確定Geocoder實現(xiàn)是否存在淘捡。由于國內(nèi)用不了GoogleServices 服務(wù)藕各,因此一般的手機廠商都會在自己的手機內(nèi)內(nèi)置百度或高德地圖服務(wù)。
主要方法有
1.isPresent():判斷當(dāng)前設(shè)備是否內(nèi)置了地理位置服務(wù)焦除,返回 true 表示地理編碼可以使用激况,false為不可使用;
2.getFromLocation():根據(jù)經(jīng)緯度返回對應(yīng)的地理位置信息膘魄,參數(shù)maxResults表示返回地址的數(shù)目乌逐,建議使用1-5;
Geocoder geocoder = new Geocoder(this);
boolean flag = Geocoder.isPresent();
if (flag) {
try {
List<Address> addresses = geocoder.getFromLocation(39.345345, 116.345, 1);
if (addresses.size() > 0) {
Address address = addresses.get(0);
String sAddress;
if (!TextUtils.isEmpty(address.getLocality())) {
if (!TextUtils.isEmpty(address.getFeatureName())) {
//市和周邊地址
sAddress = address.getLocality() + " " + address.getFeatureName();
} else {
sAddress = address.getLocality();
}
} else {
sAddress = "定位失敗";
}
Log.e("gzq", "sAddress:" + sAddress);
}
} catch (IOException e) {
}
}
該方法返回一個List创葡,從中取出第一個元素浙踢,如果有l(wèi)ocality(地址位置)屬性,并且有featureName(地址要素)灿渴,就可以去取相關(guān)信息洛波,比如國家(countryName),郵編(postalCode)骚露,國家編碼(countryCode)蹬挤,省份(adminArea),二級省份(subAdminArea)棘幸,二級城市(subLocality)焰扳,道路(thoroughfare)等;
3.getFromLocationName():返回描述地理位置信息的集合,maxResults是返回地址的數(shù)目蓝翰,建議使用1-5光绕;
List<Address> addresses = geocoder.getFromLocationName("西二旗", 1);
if (addresses.size() > 0) {
//返回當(dāng)前位置,精度可調(diào)
Address address = addresses.get(0);
if (address != null) {
Log.e("gzq", "sAddress:" + address.getLatitude());
Log.e("gzq", "sAddress:" + address.getLongitude());
}
}
Geocoder的使用比較簡單畜份,getFromLocation和getFromLocationName都可以在線程里面執(zhí)行诞帐。
結(jié)尾:
本周給大家分享了客戶端應(yīng)該如何使用定位相關(guān)服務(wù),主要就是使用LocationManger和Geocoder的API爆雹,下周我將走讀LocationMangerService源碼停蕉,詳細(xì)分析定位服務(wù)系統(tǒng)是如何實現(xiàn)的,我們不僅要知道how钙态,還要知道why慧起,下周再見。