Android地理位置這篇就夠啦!

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

知識(shí)點(diǎn)

image.png

一、梳理

1钠导、誰提供的定位信息震嫉?

安卓的位置信息由“位置提供者”提供,在安卓中有三種位置提供者即:gps牡属、network票堵、passive。前兩者我們或許還可以理解但是這個(gè)passive是個(gè)啥東西逮栅?客官別急悴势,窗宇,,特纤,這就開始分析军俊!
1、gps:gps提供者就是使用手機(jī)內(nèi)置的gps硬件接收器接受精確的位置信息捧存。
2粪躬、network:就是使用網(wǎng)絡(luò)作為位置提供者,這里網(wǎng)絡(luò)可以為wifi昔穴、基站镰官。因?yàn)閣ifi、基站也是可以提供粗略的定位的吗货。
3朋魔、psssive:這個(gè)提供者有點(diǎn)特別。從這個(gè)單詞來看是“被動(dòng)的”意思卿操。其實(shí)這個(gè)就是個(gè)被動(dòng)接收者警检。怎么理解呢?你可以這樣理解:當(dāng)其他的app害淤、或者服務(wù)(service)請(qǐng)求到位置信息時(shí)扇雕,這個(gè)接收者可以接受到他們請(qǐng)求到的位置信息】悖看到這里我們就明白了镶奉。也可以知道使用這個(gè)提供者時(shí)獲取到的位置并不一定是正確的。

2崭放、安卓Location框架的架構(gòu)

如下圖(圖片來自網(wǎng)絡(luò)哨苛,如有侵權(quán)聯(lián)系作者刪除,,,,)開發(fā)者主要使用LocationManager來獲得位置信息。然而位置信息的獲得是個(gè)跨進(jìn)程通信的過程币砂。真正的位置信息的獲取建峭、位置提供者的查詢、位置的實(shí)時(shí)監(jiān)聽等都是由系統(tǒng)的LocationManagerService來負(fù)責(zé)接手的决摧。
1壹士、如果用戶是使用了gps定位方式太援,那么LocationManagerService就委托給GpsLocationProvider去獲得精確位置信息挠进。
2矾麻、如果用戶使用的network 定位方式,LocationManagerService就委托給第三方實(shí)現(xiàn)去獲得粗略的位置信息波岛。
ps:google的api中并未實(shí)現(xiàn)netWorkProvider的具體邏輯茅坛,這個(gè)功能交給了第三方服務(wù)去完成。國內(nèi)手機(jī)一般都集成了百度地圖则拷、或者高德地圖贡蓖。這兩個(gè)第三方的app中就有一個(gè)service祟剔,就是netWorkProvider的具體實(shí)現(xiàn)。這里又涉及到了 安卓系統(tǒng)位置服務(wù)于第三方app的位置服務(wù)跨進(jìn)程通信摩梧。

image.png

二物延、常用類

1、LocationManager

位置管理者仅父,應(yīng)用層(開發(fā)者)主要使用這個(gè)類和系統(tǒng)的位置服務(wù)進(jìn)行通信叛薯。

(1)獲取方式

 LocationManager locationManager =
       (LocationManager) getSystemService(LOCATION_SERVICE);

(2)常用方法或字段

// 常用的靜態(tài)常量
GPS_PROVIDER // gps 提供者
NETWORK_PROVIDER // network提供者
PASSIVE_PROVIDER // passive提供者(需要)

// 返回手機(jī)支持的所有提供者(上述三種:gps、network笙纤、passive)
public List<String> getAllProviders()  

//根據(jù)篩選條件返回最符合條件的位置提供者耗溜,一般篩選最符合條件能用的提供者。
public String getBestProvider(Criteria criteria, boolean enabledOnly)

// 一般參數(shù)為true省容,返回能用的提供者集合
List<String> getProviders(boolean enabledOnly)

// 獲得最近一次使用過的Location信息對(duì)象抖拴,最近沒有使用過時(shí)返回null。一般此方法不用腥椒。
public Location getLastKnownLocation(String provider);

// 注冊(cè)阿宅,需要位置的實(shí)時(shí)更新
//provider:位置提供者,表名位置數(shù)據(jù)由那種提供者提供笼蛛。
//minTime:最小時(shí)間間隔,位置刷新期間的洒放。多久刷新一次。
//minDistance:最小的位置滨砍,位置刷新期間往湿。超過這個(gè)位置也觸發(fā)刷新。
// LocationListener :位置監(jiān)聽
void requestLocationUpdates(String provider, long minTime, float minDistance,
            LocationListener listener)

// 解注冊(cè)
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)的地理坐標(biāo)。這一過程稱為地理編碼龄句』芈郏或者散罕,您可以將地理位置轉(zhuǎn)換為地址分歇。地址查詢功能也稱為逆向地理編碼。
image.png
maxResults:返回地址的數(shù)目取1~5之間
因?yàn)橥唤?jīng)緯度可能對(duì)應(yīng)多個(gè)地址
public List<Address> getFromLocation(double latitude, double longitude, int maxResults)

1欧漱、可見提供了三個(gè)方法职抡,常用的為第一個(gè)。根據(jù)經(jīng)緯度獲取Address集合误甚。
2缚甩、Geocoder 請(qǐng)求的是一個(gè)后臺(tái)服務(wù)谱净,但是該服務(wù)不包括在標(biāo)準(zhǔn)android framework中。因此如果當(dāng)前設(shè)備不包含location services擅威,則Geocoder返回的地址或者經(jīng)緯度為空壕探。當(dāng)然你可以使用 Geocoder#isPresent()方法來判斷當(dāng)前設(shè)備是否包含地理位置服務(wù)。由于國內(nèi)使用不了Google Services服務(wù)郊丛,因此一般的手機(jī)廠商都會(huì)在自己的手機(jī)內(nèi)內(nèi)置百度地圖服務(wù)李请,或者高德地圖服務(wù)來替代Google Services服務(wù)。

4厉熟、Address:詳細(xì)的地理位置

一般通過如下api我們即可獲得 XXX市XXX區(qū)XXX街區(qū)XXX小區(qū)信息导盅。

image.png

三、權(quán)限&api請(qǐng)求流程

1揍瑟、動(dòng)態(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)對(duì)危險(xiǎn)權(quán)限添加了動(dòng)態(tài)權(quán)限申請(qǐng)的功能。上述的位置權(quán)限在6.0或者以上的手機(jī)中需要?jiǎng)討B(tài)申請(qǐng)下绢片。
參考文章:安卓動(dòng)態(tài)權(quán)限

2滤馍、大致使用流程圖解
流程.png

四 、栗子及其Criteria類的補(bǔ)充

1底循、Criteria
String bestProvider = locationManager.getBestProvider(new Criteria(), true);

細(xì)心的你可能會(huì)留意到纪蜒,上述api簡介中,獲得provider時(shí)我們還會(huì)用到Criteria類此叠。其實(shí)這個(gè)類就是篩選合適的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):是否允許收費(fèi)倦炒。
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è)置方向的精準(zhǔn)度。參數(shù) Criteria.NO_REQUIREMENT, Criteria.ACCURACY_LOW, Criteria.ACCURACY_HIGH。分別表示:無喉誊,低邀摆,高。
public void setSpeedAccuracy(int accuracy):設(shè)置速度的精準(zhǔn)度伍茄。參數(shù)同上栋盹。
public void setHorizontalAccuracy(int accuracy):設(shè)置水平方向的精準(zhǔn)度。參數(shù)同上敷矫。
public void setVerticalAccuracy(int accuracy):設(shè)置垂直方向的精準(zhǔn)度贞盯。參數(shù)同上。

Criteria類該怎么使用沪饺?代碼示例如下:

        criteria.setAccuracy(Criteria.ACCURACY_FINE);//設(shè)置定位精準(zhǔn)度
        criteria.setAltitudeRequired(false);//是否要求海拔
        criteria.setBearingRequired(true);//是否要求方向
        criteria.setCostAllowed(true);//是否要求收費(fèi)
        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è)備可用的location provider,當(dāng)?shù)诙€(gè)參數(shù)為false時(shí)整葡,返回當(dāng)前設(shè)備所有provider中最符合條件的那個(gè)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)緯度坐標(biāo)
        // 1 獲取位置管理者對(duì)象
        LocationManager lm = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
        // 2 通過lm獲得經(jīng)緯調(diào)度坐標(biāo)
        // (參數(shù): provider(定位方式 提供者 通過 LocationManager靜態(tài)調(diào)用)遭居,
        // minTime(獲取經(jīng)緯度間隔的最小時(shí)間 時(shí)時(shí)刻刻獲得傳參數(shù)0)啼器,
        // minDistance(移動(dòng)的最小間距 時(shí)時(shí)刻刻傳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 {
                    // 返回集合對(duì)象泛型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("當(dāng)前位置"+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);
    }
}

貼圖如下

image.png

五俱萍、擴(kuò)展及其采坑

1端壳、擴(kuò)展

安卓手機(jī)從屏幕上端下滑,拉出功能欄枪蘑,點(diǎn)擊GPS圖標(biāo)開啟GPS损谦,這時(shí)你的手機(jī)設(shè)置->位置信息模式 會(huì)選中“高精確度”定位方式,當(dāng)你點(diǎn)擊GPS圖標(biāo)關(guān)閉時(shí)岳颇,會(huì)使用默認(rèn)的“低耗電量”(如下圖)

Screenshot_2020-03-26-17-51-43-750_com.android.se.jpg
2照捡、采坑network 提供者下onLocationChange不回調(diào),拿不到Location對(duì)象话侧。

1栗精、場景:
安卓TV開發(fā)的同學(xué)或許會(huì)碰到這種情況,TV真機(jī)上onLocationChange沒有回調(diào)瞻鹏。拿不到Location對(duì)象悲立。
2、解釋:
其實(shí)TV不支持gps硬件功能新博,如果想采用定位那只有使用network提供者了薪夕,但是通過上述的架構(gòu)圖我們也可以知道。提供者為network時(shí)叭披,位置的獲取需要第三方服務(wù)寥殖。由于TV上一般都沒有地圖類app所以第三方就是空實(shí)現(xiàn)了玩讳。location對(duì)象就拿不到了涩蜘。
3嚼贡、驗(yàn)證依據(jù):

  • 安卓系統(tǒng)開機(jī)時(shí)可以打印系統(tǒng)log(使用adb logcat 命令)
  • LocationManagerService的初始化在安卓系統(tǒng)類的SystemServer的startOtherService方法中完成。這個(gè)操作在安卓系統(tǒng)啟動(dòng)時(shí)就進(jìn)行同诫。

4粤策、進(jìn)行驗(yàn)證:重啟TV打印系統(tǒng)log(如下圖找到你指定輸出的log.txt文件),結(jié)果搜索LocationManagerService關(guān)鍵字發(fā)現(xiàn)“no network location provider found”误窖,默認(rèn)使用的gms(google map service)也沒找到叮盘。
5、一種解決方案:在tv上集成三方sdk比如高德地圖霹俺。

image.png

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末丙唧,一起剝皮案震驚了整個(gè)濱河市愈魏,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌想际,老刑警劉巖培漏,帶你破解...
    沈念sama閱讀 212,454評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異胡本,居然都是意外死亡牌柄,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門侧甫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來珊佣,“玉大人,你說我怎么就攤上這事披粟〔嗜樱” “怎么了?”我有些...
    開封第一講書人閱讀 157,921評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵僻爽,是天一觀的道長虫碉。 經(jīng)常有香客問我,道長胸梆,這世上最難降的妖魔是什么敦捧? 我笑而不...
    開封第一講書人閱讀 56,648評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮碰镜,結(jié)果婚禮上兢卵,老公的妹妹穿的比我還像新娘。我一直安慰自己绪颖,他們只是感情好秽荤,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般窃款。 火紅的嫁衣襯著肌膚如雪课兄。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,950評(píng)論 1 291
  • 那天晨继,我揣著相機(jī)與錄音烟阐,去河邊找鬼。 笑死紊扬,一個(gè)胖子當(dāng)著我的面吹牛蜒茄,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播餐屎,決...
    沈念sama閱讀 39,090評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼檀葛,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了腹缩?” 一聲冷哼從身側(cè)響起屿聋,我...
    開封第一講書人閱讀 37,817評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎庆聘,沒想到半個(gè)月后胜臊,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,275評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡伙判,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評(píng)論 2 327
  • 正文 我和宋清朗相戀三年象对,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片宴抚。...
    茶點(diǎn)故事閱讀 38,724評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡勒魔,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出菇曲,到底是詐尸還是另有隱情冠绢,我是刑警寧澤,帶...
    沈念sama閱讀 34,409評(píng)論 4 333
  • 正文 年R本政府宣布常潮,位于F島的核電站弟胀,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏喊式。R本人自食惡果不足惜孵户,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評(píng)論 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望岔留。 院中可真熱鬧夏哭,春花似錦、人聲如沸献联。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至进胯,卻和暖如春用爪,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背龄减。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評(píng)論 1 266
  • 我被黑心中介騙來泰國打工项钮, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留班眯,地道東北人希停。 一個(gè)月前我還...
    沈念sama閱讀 46,503評(píng)論 2 361
  • 正文 我出身青樓,卻偏偏與公主長得像署隘,于是被迫代替她去往敵國和親宠能。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容