Android 系統(tǒng)提供了地理位置服務(wù)相關(guān)的API,方便了開發(fā)者去獲得當前地理位置。用戶可以通過GPS接收器接受地理位置。也可以通過network(wifi或者基站)來獲得當前的定位。
知識點

一、梳理
1、誰提供的定位信息?
安卓的位置信息由“位置提供者”提供,在安卓中有三種位置提供者即:gps、network、passive。前兩者我們或許還可以理解但是這個passive是個啥東西?客官別急,,,,這就開始分析!
1、gps:gps提供者就是使用手機內(nèi)置的gps硬件接收器接受精確的位置信息。
2、network:就是使用網(wǎng)絡(luò)作為位置提供者,這里網(wǎng)絡(luò)可以為wifi、基站。因為wifi、基站也是可以提供粗略的定位的。
3、psssive:這個提供者有點特別。從這個單詞來看是“被動的”意思。其實這個就是個被動接收者。怎么理解呢?你可以這樣理解:當其他的app、或者服務(wù)(service)請求到位置信息時,這個接收者可以接受到他們請求到的位置信息。看到這里我們就明白了。也可以知道使用這個提供者時獲取到的位置并不一定是正確的。
2、安卓Location框架的架構(gòu)
如下圖(圖片來自網(wǎng)絡(luò),如有侵權(quán)聯(lián)系作者刪除,,,,)開發(fā)者主要使用LocationManager來獲得位置信息。然而位置信息的獲得是個跨進程通信的過程。真正的位置信息的獲取、位置提供者的查詢、位置的實時監(jiān)聽等都是由系統(tǒng)的LocationManagerService來負責(zé)接手的。
1、如果用戶是使用了gps定位方式,那么LocationManagerService就委托給GpsLocationProvider去獲得精確位置信息。
2、如果用戶使用的network 定位方式,LocationManagerService就委托給第三方實現(xiàn)去獲得粗略的位置信息。
ps:google的api中并未實現(xiàn)netWorkProvider的具體邏輯,這個功能交給了第三方服務(wù)去完成。國內(nèi)手機一般都集成了百度地圖、或者高德地圖。這兩個第三方的app中就有一個service,就是netWorkProvider的具體實現(xiàn)。這里又涉及到了 安卓系統(tǒng)位置服務(wù)于第三方app的位置服務(wù)跨進程通信。

二、常用類
1、LocationManager
位置管理者,應(yīng)用層(開發(fā)者)主要使用這個類和系統(tǒng)的位置服務(wù)進行通信。
(1)獲取方式
LocationManager locationManager =
(LocationManager) getSystemService(LOCATION_SERVICE);
(2)常用方法或字段
// 常用的靜態(tài)常量
GPS_PROVIDER // gps 提供者
NETWORK_PROVIDER // network提供者
PASSIVE_PROVIDER // passive提供者(需要)
// 返回手機支持的所有提供者(上述三種:gps、network、passive)
public List<String> getAllProviders()
//根據(jù)篩選條件返回最符合條件的位置提供者,一般篩選最符合條件能用的提供者。
public String getBestProvider(Criteria criteria, boolean enabledOnly)
// 一般參數(shù)為true,返回能用的提供者集合
List<String> getProviders(boolean enabledOnly)
// 獲得最近一次使用過的Location信息對象,最近沒有使用過時返回null。一般此方法不用。
public Location getLastKnownLocation(String provider);
// 注冊,需要位置的實時更新
//provider:位置提供者,表名位置數(shù)據(jù)由那種提供者提供。
//minTime:最小時間間隔,位置刷新期間的。多久刷新一次。
//minDistance:最小的位置,位置刷新期間。超過這個位置也觸發(fā)刷新。
// LocationListener :位置監(jiān)聽
void requestLocationUpdates(String provider, long minTime, float minDistance,
LocationListener listener)
// 解注冊
public void removeUpdates(LocationListener listener)
2、Location:
位置類,內(nèi)部封裝了經(jīng)緯度、海拔之類的位置信息。
location.getLongitude();//經(jīng)度
location.getLatitude();// 緯度
location.getProvider();//位置提供者
3、Geocoder:將地址轉(zhuǎn)換為相應(yīng)的地理坐標。這一過程稱為地理編碼?;蛘?,您可以將地理位置轉(zhuǎn)換為地址。地址查詢功能也稱為逆向地理編碼。

maxResults:返回地址的數(shù)目取1~5之間
因為同一經(jīng)緯度可能對應(yīng)多個地址
public List<Address> getFromLocation(double latitude, double longitude, int maxResults)
1、可見提供了三個方法,常用的為第一個。根據(jù)經(jīng)緯度獲取Address集合。
2、Geocoder 請求的是一個后臺服務(wù),但是該服務(wù)不包括在標準android framework中。因此如果當前設(shè)備不包含location services,則Geocoder返回的地址或者經(jīng)緯度為空。當然你可以使用 Geocoder#isPresent()方法來判斷當前設(shè)備是否包含地理位置服務(wù)。由于國內(nèi)使用不了Google Services服務(wù),因此一般的手機廠商都會在自己的手機內(nèi)內(nèi)置百度地圖服務(wù),或者高德地圖服務(wù)來替代Google Services服務(wù)。
4、Address:詳細的地理位置
一般通過如下api我們即可獲得 XXX市XXX區(qū)XXX街區(qū)XXX小區(qū)信息。

三、權(quán)限&api請求流程
1、動態(tài)權(quán)限
<!-- GPS定位權(quán)限 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- wifi/基站定位權(quán)限-->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
安卓6.0開始,系統(tǒng)對危險權(quán)限添加了動態(tài)權(quán)限申請的功能。上述的位置權(quán)限在6.0或者以上的手機中需要動態(tài)申請下。
參考文章:安卓動態(tài)權(quán)限
2、大致使用流程圖解

四 、栗子及其Criteria類的補充
1、Criteria
String bestProvider = locationManager.getBestProvider(new Criteria(), true);
細心的你可能會留意到,上述api簡介中,獲得provider時我們還會用到Criteria類。其實這個類就是篩選合適的provider的。常見的篩選條件如下:
public void setAccuracy(int accuracy):位置解析精確度,參數(shù)Criteria.ACCURACY_FINE:表示高精確度。Criteria.ACCURACY_COARSE:表示模糊精確度。
public void setAltitudeRequired(boolean altitudeRequired ):是否要求海拔信息。
public void setBearingRequired(boolean bearingRequired):是否要求方向信息。
public void setCostAllowed(boolean costAllowed):是否允許收費。
public void setSpeedRequired(boolean speedRequired):是否要求速度信息。
public void setBearingRequired(boolean bearingRequired):是否要求方向信息。
public void setPowerRequirement(int level):設(shè)置電池消耗要求,參數(shù) Criteria. NO_REQUIREMENT, Criteria. POWER_LOW, Criteria. POWER_MEDIUM, Criteria. POWER_HIGH。分別表示 :無、低、中、高,。
public void setBearingAccuracy(int accuracy):設(shè)置方向的精準度。參數(shù) Criteria.NO_REQUIREMENT, Criteria.ACCURACY_LOW, Criteria.ACCURACY_HIGH。分別表示:無,低,高。
public void setSpeedAccuracy(int accuracy):設(shè)置速度的精準度。參數(shù)同上。
public void setHorizontalAccuracy(int accuracy):設(shè)置水平方向的精準度。參數(shù)同上。
public void setVerticalAccuracy(int accuracy):設(shè)置垂直方向的精準度。參數(shù)同上。
Criteria類該怎么使用?代碼示例如下:
criteria.setAccuracy(Criteria.ACCURACY_FINE);//設(shè)置定位精準度
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è)置垂直方向精確度
//返回滿足條件的,當前設(shè)備可用的location provider,當?shù)诙€參數(shù)為false時,返回當前設(shè)備所有provider中最符合條件的那個provider(但是不一定可用)。
String mProvider = mLocationManager.getBestProvider(criteria,true);
2、栗子
public class MainActivity extends AppCompatActivity {
private TextView tvLocation;
private TextView tvAddress;
private Geocoder geocoder;
private List<Address> addressList;
private StringBuilder sb;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initUi();
initData();
}
private void initData() {
// 獲取經(jīng)緯度坐標
// 1 獲取位置管理者對象
LocationManager lm = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
// 2 通過lm獲得經(jīng)緯調(diào)度坐標
// (參數(shù): provider(定位方式 提供者 通過 LocationManager靜態(tài)調(diào)用),
// minTime(獲取經(jīng)緯度間隔的最小時間 時時刻刻獲得傳參數(shù)0),
// minDistance(移動的最小間距 時時刻刻傳0),LocationListener(監(jiān)聽))
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
return;
}
lm.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, new LocationListener() {
@Override
public void onLocationChanged(Location location) {
// 獲取經(jīng)緯度主要方法
double latitude = location.getLatitude();
double longitude = location.getLongitude();
tvLocation.setText("latitude"+latitude+" "+"longitude"+longitude);
sb = new StringBuilder();
geocoder = new Geocoder(MainActivity.this);
addressList = new ArrayList<Address>();
try {
// 返回集合對象泛型address
addressList= geocoder.getFromLocation(latitude,longitude,1);
if (addressList.size() > 0) {
Address address = addressList.get(0);
for (int i = 0; i < address.getMaxAddressLineIndex(); i++) {
sb.append(address.getAddressLine(i)).append("\n");
}
sb.append(address.getFeatureName());//周邊地址
}
tvAddress.setText("當前位置"+sb.toString());
;
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onStatusChanged(String s, int i, Bundle bundle) {
//狀態(tài)發(fā)生改變監(jiān)聽
}
@Override
public void onProviderEnabled(String s) {
// ProviderEnabled
}
@Override
public void onProviderDisabled(String s) {
// ProviderDisabled
}
});
}
private void initUi() {
tvLocation = (TextView) findViewById(R.id.tv_location);
tvAddress = (TextView) findViewById(R.id.tv_address);
}
}
貼圖如下

五、擴展及其采坑
1、擴展
安卓手機從屏幕上端下滑,拉出功能欄,點擊GPS圖標開啟GPS,這時你的手機設(shè)置->位置信息模式 會選中“高精確度”定位方式,當你點擊GPS圖標關(guān)閉時,會使用默認的“低耗電量”(如下圖)

2、采坑network 提供者下onLocationChange不回調(diào),拿不到Location對象。
1、場景:
安卓TV開發(fā)的同學(xué)或許會碰到這種情況,TV真機上onLocationChange沒有回調(diào)。拿不到Location對象。
2、解釋:
其實TV不支持gps硬件功能,如果想采用定位那只有使用network提供者了,但是通過上述的架構(gòu)圖我們也可以知道。提供者為network時,位置的獲取需要第三方服務(wù)。由于TV上一般都沒有地圖類app所以第三方就是空實現(xiàn)了。location對象就拿不到了。
3、驗證依據(jù):
- 安卓系統(tǒng)開機時可以打印系統(tǒng)log(使用adb logcat 命令)
- LocationManagerService的初始化在安卓系統(tǒng)類的SystemServer的startOtherService方法中完成。這個操作在安卓系統(tǒng)啟動時就進行。
4、進行驗證:重啟TV打印系統(tǒng)log(如下圖找到你指定輸出的log.txt文件),結(jié)果搜索LocationManagerService關(guān)鍵字發(fā)現(xiàn)“no network location provider found”,默認使用的gms(google map service)也沒找到。
5、一種解決方案:在tv上集成三方sdk比如高德地圖。

參考:
1、Android網(wǎng)絡(luò)定位源碼分析
2、https://developer.android.google.cn/reference/android/location/LocationManager?hl=en