前言:
我們知道我們的APP有可能需要獲取一些地理位置信息。比如定位用戶當前的位置,自動選定城市或者區(qū)域等塘揣。所以這次做個關(guān)于定位的一些總結(jié)。
正文
我們按照二大塊來進行分析:一塊是相關(guān)權(quán)限宿崭,一塊是具體獲取地理信息的相關(guān)代碼亲铡。(而實際開發(fā)代碼中,代碼這二塊是寫在一起的葡兑,單純是為了文章分析從而分開奖蔓。)
1.相關(guān)權(quán)限
這里的權(quán)限我特指了二塊:
- 一個是本身我們平常開發(fā)的app需要獲取各種權(quán)限,比如相機等讹堤,這時候我們既然要獲取當前手機的地理信息吆鹤,肯定也要有一個Location相關(guān)的權(quán)限。
- 本身手機需要打開相應的定位功能洲守,不然app有權(quán)限獲取疑务,但是手機關(guān)閉了整個的定位功能沾凄,就還是獲取不到。
1.1 app獲取手機權(quán)限
emmm......這塊我覺得應該不需要花更多的時間來說明了吧知允,主要就是:
- 檢查權(quán)限 (checkSelfPermission)
- 請求權(quán)限(requestPermissions)
- 回調(diào)事件處理(onRequestPermissionsResult)
而我們要申請的權(quán)限無非就是Location相關(guān)的權(quán)限撒蟀。
android.permission.ACCESS_COARSE_LOCATION
允許一個程序訪問CellID或WiFi熱點來獲取粗略的位置
android.permission.ACCESS_FINE_LOCATION
允許一個程序訪問精良位置(如GPS)
我們可以看到第一個權(quán)限中的英文單詞COARSE
是粗略
的意思,所以在想要粗略的獲取一個地理位置的時候廊镜,比如我們通過網(wǎng)絡(luò)來獲取牙肝,我們只需要申請這個權(quán)限即可;第二個權(quán)限中的英文單詞FINE
說明是精確度高
的嗤朴,比如我們需要通過GPS來獲取權(quán)限的時候配椭,我們就需要申請這個權(quán)限。
一般來說我們的app這二個權(quán)限都會申請股缸,因為會需要GPS配合網(wǎng)絡(luò)一起來確定地理位置信息敦姻。
1.2 手機的定位開關(guān)
在確定我們的app本身已經(jīng)具有了定位權(quán)限后镰惦,我們需要知道本身的手機是否已經(jīng)打開了定位功能旺入。
public static boolean isLocServiceEnable(Context context) {
LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
boolean gps = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
boolean network = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
if (gps || network) {
return true;
}
return false;
}
我們可以看到上面我們提過一般來獲取定位是靠GPS和NetWork二種(為啥是一般呢茵瘾,因為還有一種 PASSIVE拗秘,后面會講到)雕旨。所以我們需要判定這二個功能是否可用。(如果用戶把定位功能給關(guān)了行瑞,那肯定二個都返回false奸腺。)
那這時候假如我們發(fā)現(xiàn)用戶把定位功能關(guān)了。我們肯定需要提示用戶血久,然后協(xié)助用戶跳到該設(shè)置界面突照,從而讓用戶把定位功能打開 (畢竟一般的普通用戶,可能還真的讓他去設(shè)置界面找氧吐,一時半會還真找不到讹蘑,畢竟安卓機型太多末盔,每個地方都不同 )座慰。
比如我們彈出一個彈框陨舱,提示用戶,按確定按鈕的時候跳轉(zhuǎn)到設(shè)置的定位界面:
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setMessage("尚未開啟位置定位服務");
builder.setPositiveButton("開啟", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
//啟動定位Activity
Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
//比如我們這里設(shè)定requestCode為 1
activity.startActivityForResult(intent , 1);
}
});
builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
我們可以看到通過隱式啟動Action為Settings.ACTION_LOCATION_SOURCE_SETTINGS
即可,但是這里記得用使用startActivityForResult
而不是startActivity
蛮粮,我看很多網(wǎng)上的的寫法是用startActivity
,單純跳轉(zhuǎn)過去是沒問題莺奔,但是我們需要知道返回的結(jié)果,萬一用戶跳轉(zhuǎn)過去后沒有打開呢变泄。
既然我們用了statActivityForResult
來啟動令哟,當我們返回回到自己的app界面的時候,在onActivityForResult
中需要來判斷妨蛹,本來因為習慣性思維役听,所以以為自動在onActivityForResult
的返回參數(shù)resultCode
可以用來判斷乐严,后來發(fā)現(xiàn)不管開啟不開啟艾扮,都是返回RESULT_CANCELED
,也就是0奖慌,畢竟在那個設(shè)置界面我們并沒有設(shè)定setResult(xxx)
雕欺;所以當判斷了requestCode
之后,我們需要重新判斷一次定位是否可用了蹦浦。
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 1) {
//我們通過上面提過的方法,再次去判斷是否gps和network的provider都無效撞蜂。
if (!isLocServiceEnable(MainActivity.this)) {
Toast.makeText(this, "未開啟定位功能盲镶,請手動選擇地址位置", Toast.LENGTH_LONG).show();
} else {
//去獲取具體的地理位置信息...
}
}
}
2 獲取地理位置
我們上面提到了我們想要獲取地理位置的時候,需要具備上面的基本權(quán)限蝌诡,然后才能正常使用我們的相關(guān)api去獲取信息溉贿。
主要是通過```LocationManager``這個類。
但是android.location包下的并不是谷歌推薦的:
翻譯過來就是:此API不是訪問Android位置的推薦方法浦旱。
Google位置服務API是Google Play服務的一部分宇色,是向您的應用添加位置感知功能的首選方式颁湖。 它提供了更簡單的API宣蠕,更高的精度,低功耗的地理圍欄等等甥捺。 如果您當前正在使用android.location API抢蚀,強烈建議您盡快切換到Google Location Services API。
而是推薦the Google Location Services API ,然后你懂得....emmm........
2.1 直接獲取地理信息
使用getLastKnownLocation
方法獲攘獭:
//獲取LocationManager的實例對象
locationManager = (LocationManager) activity.getSystemService(Context.LOCATION_SERVICE);
//獲取支持的provider列表
List<String> providers = locationManager.getProviders(true);
Location bestLocation = null;
//遍歷provider列表
for (String provider : providers) {
//通過getLastKnowLocation方法來獲取
Location l = locationManager.getLastKnownLocation(provider);
if (l == null) {
continue;
}
if (bestLocation == null || l.getAccuracy() < bestLocation.getAccuracy()) {
// Found best last known location: %s", l);
bestLocation = l;
}
}
這里需要注意的是皿曲,為啥通過循環(huán)provider來獲取,比如有些人會問吴侦,我開啟了GPS屋休,我想通過GPS來定位,我不是直接getLastKnowLocation(LocationManager.GPS_PROVIDER)
就可以了嗎备韧?理論上是沒問題的劫樟,但是大部分時候獲取到的都是null , 畢竟GPS本身定位時間也會很久,而且如果在室內(nèi)就更加GG了。
所以網(wǎng)上經(jīng)骋慊看到有提問:
當然解決方式也有很多听怕,有些人直接通過while循環(huán),比如一直請求:
while(xxx = null){
xxx = getLastKnowLocation(LocationManager.GPS_PROVIDER);
}
這還不算坑爹虑绵,我用了華為和小米手機尿瞭,小米手機使用這個GPS來獲取Location,一下子就獲取了翅睛。華為我寫了while循環(huán)声搁,等了很久很久,也還是一直是null捕发。(居然還跟不同牌子手機都有關(guān)系)
所以最終我是遍歷了provider來獲取最佳的地址來解決的疏旨,如果獲取不到GPS定位,也會有network輔助扎酷。
也可以參考相關(guān)的鏈接了解一下:Android 成功 使用GPS獲取當前地理位置(解決getLastKnownLocation 返回 null),不過貌似也沒有找到百分百直接獲取GPS定位獲取信息的方式檐涝。
2.2 監(jiān)視位置變化
使用requestLocationUpdates方法來獲取。
public void requestLocationUpdates(
String provider,
long minTime,
float minDistance,
LocationListener listener)
{
}
我們可以看到傳入provider,最小更新時間法挨,最小的更新距離谁榜,然后就是回調(diào)listener。
所以我們重點在于LocationListener
:
mLocationListener = new LocationListener() {
@Override
public void onLocationChanged(Location location) {
//比如判斷l(xiāng)ocation是否為null,然后根據(jù)Location來轉(zhuǎn)換成相關(guān)的地址位置信息凡纳。
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
@Override
public void onProviderEnabled(String provider) {
}
@Override
public void onProviderDisabled(String provider) {
//比如在provider失效了就取消監(jiān)聽
locationManager.removeUpdates(this);
}
};
切記窃植,在某個你需要的條件下,通過removeUpdates()去取消監(jiān)聽荐糜,比如你可能在onPause中去取消等巷怜。
我們在onLocationChanged
方法中獲取到了Location
對象,就可以去獲取相關(guān)信息了暴氏。
-
通過Location來獲取相關(guān)的經(jīng)緯度:
double latitude = location.getLatitude();
double longitude = location.getLongitude();
-
通過Geocoder來把經(jīng)緯度轉(zhuǎn)換成相應的Address集合:
Geocoder geocoder = new Geocoder(context);
List<Address> addresses = geocoder.getFromLocation(latitude, longitude, 1);
- 最后通過Address對象中的相關(guān)屬性延塑,拼接出自己想要的相關(guān)信息。
address.getCountryName() //國家
address.getPostalCode() //郵編
address.getCountryCode() //國家編碼
address.getAdminArea() //省份
address.getSubAdminArea() //二級省份
address.getThoroughfare() //道路
address.getSubLocality() //二級城市
.......
.......
結(jié)語:
emm.......大家輕噴即可答渔。页畦。。研儒。