??地理定位服務對于我們的開發來說具有重要的作用,目前是市場上的很多App會選擇獲取你的地理位置信息,尤其是一些旅游類,生活類的信息服務App。例如:飛豬,美團,去哪兒旅行,餓了么等。萬維網的實現,我們所在的世界就小了,不管你在哪里,都是一個個二維或者三維的坐標。
??基于地理定位服務的核心就是確定用戶所在的位置。通常有兩種技術方式來實現:第一種是通過GPS定位,第二種是網絡定位。GPS定位的工作原理是手機內置的GPS硬件與衛星交互來獲取經緯度,這種定位方式精度準確,但是只能在室外進行,室內無法接收衛星信號。網絡定位的工作原理是根據手機當前網絡附近的三個基站來進行測速,計算手機之間的距離,然后通過三角定位得到大概的位置,這種定位方式一般,室內室外都可用。
??因為特殊的原因,Google雖然提供了相應的API,但是國內不可用。所以我們一般借助第三方定位SDK來進行定位:百度地圖,高德地圖。
1. 申請API Key
??在申請API Key之前,必須得注冊成為百度開發者的賬號,這里就按照它的提示去做就好了,不描述了。在申請完成之后,訪問這個http://lbsyun.baidu.com/apiconsole/key 地址。
因為這里沒有創建項目,所以空的。然后點擊創建應用。應用名稱,類型。然后按照自己的實際需要選擇服務。
下面這里有個SHA1,我們需要打開Android Studio 的項目,點擊右邊的Gradle->項目包名->Tasks->android->signingReport(雙擊)
雙擊signingReport后,就可以查看你的MD5和SHA1了,如果沒有出現,點一下這個/ab鍵。
這個就是我們需要的SHA1指紋,每個Android Studio 的指紋都不一樣,我們使用debug.keystore文件生成的指紋,這個是Android Studio自動生成的用于測試的簽名文件。如果你需要正式發布的時候需要創建一個簽名文件。要得到它的指紋可以這樣做: 在cmd中輸入:
keytool -list -v -keystore <簽名文件的路徑名>
使用Android Studio 生成簽名文件Build -> Generate signed APK文件。有的第一次可能會需要輸入操作系統的密碼,輸完了之后點擊OK就好了。進入這個界面
- 選擇創建jks文件,并填寫信息。填寫信息的時候不要忘記密碼了,最好將上下兩個密碼設置一樣的,免得忘記。
創建jsk文件
|
選擇jks文件的目錄
|
---|---|
創建jsk文件目錄
|
填寫信息
|
- 創建完成之后,會自動保存你的key 和 value ,然后可能會需要輸入設置的密碼。
創建完成之后
|
輸入之前設置的密碼
|
---|
可以打包apk了。
接下來,我們可以輸入之前的 keytool -list -v -keystore <簽名文件的路徑名>
-
最后回到百度地圖頁面,填上你想要的信息,點擊提交
提交信息 最后你就可以回到http://lbsyun.baidu.com/apiconsole/key,刷新就可以出現你創建的應用了。
這個udAn51qiTQY2XvxgDkBIR9ihq9hfKGj7就是你申請到的API Key。然后你就可以看是你的百度定位工作了。
2. 使用百度定位的準備
- 在你申請好了API Key的時候,現在可以下載LBS的SDK包,下載地址:http: //lbsyun.baidu.com/sdk/download。選擇基礎定位和基礎地圖就可以了。
-
在你選擇好了之后,下載壓縮包,解壓效果如下圖所示,然后將BaiduLBS_Android.jar包添加到Android Studio 中的libs中,然后右鍵單擊Add as Library,將其他的文件新建一個包放在src/main/jniLibs ,用于存放so文件。
解壓包
3. 確定經緯度
- 修改actiivty_main.xml,布局只有一個TextView控件,用于顯示當前位置的經緯度。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tv_postion"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
/>
</LinearLayout>
- 修改AndroidManifest.xml文件,添加權限,配置API Key。
主要是<meta-data>和<service>這兩個不能忽視。<meta-data>的name不用改,value改成你申請的API Key,service不用改動。
<meta-data
android:name="com.baidu.lbsapi.API_KEY"
android:value="AK" />
<service android:name="com.baidu.location.f"
android:enabled="true"
android:process=":remote">
完整代碼:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.demo.bddemo">
<!-- 這個權限用于進行網絡定位-->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"></uses-permission>
<!-- 這個權限用于訪問GPS定位-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"></uses-permission>
<!-- 用于訪問wifi網絡信息,wifi信息會用于進行網絡定位-->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"></uses-permission>
<!-- 獲取運營商信息,用于支持提供運營商信息相關的接口-->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission>
<!-- 這個權限用于獲取wifi的獲取權限,wifi信息會用來進行網絡定位-->
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"></uses-permission>
<!-- 用于讀取手機當前的狀態-->
<uses-permission android:name="android.permission.READ_PHONE_STATE"></uses-permission>
<!-- 寫入擴展存儲,向擴展卡寫入數據,用于寫入離線定位數據-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
<!-- 訪問網絡,網絡定位需要上網-->
<uses-permission android:name="android.permission.INTERNET" />
<!-- SD卡讀取權限,用戶寫入離線定位數據-->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"></uses-permission>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<meta-data
android:name="com.baidu.lbsapi.API_KEY"
android:value="udAn51qiTQY2XvxgDkBIR9ihq9hfKGj7" />
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name="com.baidu.location.f"
android:enabled="true"
android:process=":remote">
</service>
</application>
</manifest>
- 在MainActiivty文件中完成對地理定位的操作,因為Android 6.0開始,對權限要求的提高,所以這里給了個刪減版的。方便好看。首先我們創建一個LocationClient實例,然后調用registerLocationListener()方法注冊定位監聽器。獲得地理位置信息之后,會返回一個BDLocation 對象。根據這個對象來獲取定位的信息。
public class MainActivity extends AppCompatActivity {
private TextView tv_postion;
private LocationClient mLocationClient;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv_postion = (TextView) findViewById(R.id.tv_postion);
initLocation();
}
private void initLocation() {
mLocationClient = new LocationClient(getApplicationContext());
mLocationClient.registerLocationListener(new BDLocationListener() {
@Override
public void onReceiveLocation(BDLocation bdLocation) {
StringBuilder currentPosition = new StringBuilder();
currentPosition.append("維度:").append(bdLocation.getLatitude()).append("\n");
currentPosition.append("經度:").append(bdLocation.getLongitude()).append("\n");
currentPosition.append("定位方式:");
Log.e("tag","當前的定位方式="+bdLocation.getLocType());
if(bdLocation.getLocType() == BDLocation.TypeGpsLocation){
currentPosition.append("GPS");
}else if(bdLocation.getLocType() == BDLocation.TypeNetWorkLocation){
currentPosition.append("網絡");
}
tv_postion.setText(currentPosition);
}
});
}
mLocationClient.start();
}
添加權限版本的:
public class MainActivity extends AppCompatActivity {
private TextView tv_postion;
private LocationClient mLocationClient;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv_postion = (TextView) findViewById(R.id.tv_postion);
initLocation();
List<String> permissionList = new ArrayList<>();
if(ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_FINE_LOCATION)!= PackageManager.PERMISSION_GRANTED){
permissionList.add(Manifest.permission.ACCESS_FINE_LOCATION);
}
if(ContextCompat.checkSelfPermission(this,Manifest.permission.READ_PHONE_STATE)!=PackageManager.PERMISSION_GRANTED){
permissionList.add(Manifest.permission.READ_PHONE_STATE);
}
if(ContextCompat.checkSelfPermission(this,Manifest.permission.WRITE_EXTERNAL_STORAGE)!=PackageManager.PERMISSION_GRANTED){
permissionList.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
}
if(!permissionList.isEmpty()){
String[] permissions= permissionList.toArray(new String[permissionList.size()]);
ActivityCompat.requestPermissions(MainActivity.this,permissions,1);
}else{
requestLocation();
}
}
private void requestLocation() {
mLocationClient.start();
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode){
case 1:
if(grantResults.length>0){
for (int result: grantResults
) {
if(result !=PackageManager.PERMISSION_GRANTED){
Toast.makeText(this, "必須同意所有的權限才能使用本程序", Toast.LENGTH_SHORT).show();
finish();
return;
}
}
requestLocation();
}else{
Toast.makeText(this, "發生了錯誤", Toast.LENGTH_SHORT).show();
}
break;
}
}
private void initLocation() {
mLocationClient = new LocationClient(getApplicationContext());
mLocationClient.registerLocationListener(new BDLocationListener() {
@Override
public void onReceiveLocation(BDLocation bdLocation) {
StringBuilder currentPosition = new StringBuilder();
currentPosition.append("維度:").append(bdLocation.getLatitude()).append("\n");
currentPosition.append("經度:").append(bdLocation.getLongitude()).append("\n");
currentPosition.append("定位方式:");
Log.e("tag","當前的定位方式="+bdLocation.getLocType());
if(bdLocation.getLocType() == BDLocation.TypeGpsLocation){
currentPosition.append("GPS");
}else if(bdLocation.getLocType() == BDLocation.TypeNetWorkLocation){
currentPosition.append("網絡");
}
tv_postion.setText(currentPosition);
}
});
// mLocationClient.start();
}
獲取error code:
public int getLocType ( )
返回值:
61 : GPS定位結果,GPS定位成功。
62 : 無法獲取有效定位依據,定位失敗,請檢查運營商網絡或者wifi網絡是否正常開啟,嘗試重新請求定位。
63 : 網絡異常,沒有成功向服務器發起請求,請確認當前測試手機網絡是否通暢,嘗試重新請求定位。
65 : 定位緩存的結果。
66 : 離線定位結果。通過requestOfflineLocaiton調用時對應的返回結果。
67 : 離線定位失敗。通過requestOfflineLocaiton調用時對應的返回結果。
68 : 網絡連接失敗時,查找本地離線定位時對應的返回結果。
161: 網絡定位結果,網絡定位定位成功。
162: 請求串密文解析失敗。
167: 服務端定位失敗,請您檢查是否禁用獲取位置信息權限,嘗試重新請求定位。
502: key參數錯誤,請按照說明文檔重新申請KEY。
505: key不存在或者非法,請按照說明文檔重新申請KEY。
601: key服務被開發者自己禁用,請按照說明文檔重新申請KEY。
602: key mcode不匹配,您的ak配置過程中安全碼設置有問題,請確保:sha1正確,“;”分號是英文狀態;且包名是您當前運行應用的包名,請按照說明文檔重新申請KEY。
501~700:key驗證失敗,請按照說明文檔重新申請KEY。
建議使用真機測試,模擬器測試容易出問題。
模擬器測試結果
|
真機測試結果
|
---|
- 但是我們在實際中的位置不可能會一直沒有變化的,而在默認的情況下mLocationClient 的start()方法只會定位一次啊。我們需要的是實時更新當前的位置。所以修改代碼MainActivity.
public class MainActivity extends AppCompatActivity {
···
private void requestLocation() {
LocationClientOption option = new LocationClientOption();
option.setScanSpan(5000);
mLocationClient.setLocOption(option);
mLocationClient.start();
}
···
@Override
protected void onDestroy() {
super.onDestroy();
mLocationClient.stop();
}
}
增加LocationClientOption 對象,然后調用setScanSpan()的方法來更新,這里設置5000,每5秒更新一次。這樣當你的位置移動的時候,就可以實時更新經緯度了。
5.但是只給你一個經緯度,你哪里知道你在哪里呢,你需要的是具體的地址
public class MainActivity extends AppCompatActivity {
···
private void requestLocation() {
LocationClientOption option = new LocationClientOption();
option.setScanSpan(5000);
option.setIsNeedAddress(true);
mLocationClient.setLocOption(option);
mLocationClient.start();
}
···
private void initLocation() {
mLocationClient = new LocationClient(getApplicationContext());
mLocationClient.registerLocationListener(new BDLocationListener() {
@Override
public void onReceiveLocation(BDLocation bdLocation) {
StringBuilder currentPosition = new StringBuilder();
currentPosition.append("維度:").append(bdLocation.getLatitude()).append("\n");
currentPosition.append("經度:").append(bdLocation.getLongitude()).append("\n");
currentPosition.append("國家:").append(bdLocation.getCountry()).append("\n");
currentPosition.append("省:").append(bdLocation.getProvince()).append("\n");
currentPosition.append("市:").append(bdLocation.getCity()).append("\n");
currentPosition.append("區:").append(bdLocation.getDistrict()).append("\n");
currentPosition.append("街道:").append(bdLocation.getStreet()).append("\n");
currentPosition.append("定位方式:");
Log.e("tag","當前的定位方式="+bdLocation.getLocType());
if(bdLocation.getLocType() == BDLocation.TypeGpsLocation){
currentPosition.append("GPS");
}else if(bdLocation.getLocType() == BDLocation.TypeNetWorkLocation){
currentPosition.append("網絡");
}
tv_postion.setText(currentPosition);
}
});
// mLocationClient.start();
}
}
定位結果選擇的方法
//獲取定位結果
location.getTime(); //獲取定位時間
location.getLocationID(); //獲取定位唯一ID,v7.2版本新增,用于排查定位問題
location.getLocType(); //獲取定位類型
location.getLatitude(); //獲取緯度信息
location.getLongitude(); //獲取經度信息
location.getRadius(); //獲取定位精準度
location.getAddrStr(); //獲取地址信息
location.getCountry(); //獲取國家信息
location.getCountryCode(); //獲取國家碼
location.getCity(); //獲取城市信息
location.getCityCode(); //獲取城市碼
location.getDistrict(); //獲取區縣信息
location.getStreet(); //獲取街道信息
location.getStreetNumber(); //獲取街道碼(門牌號)
location.getLocationDescribe(); //獲取當前位置描述信息
location.getPoiList(); //獲取當前位置周邊POI信息
location.getBuildingID(); //室內精準定位下,獲取樓宇ID
location.getBuildingName(); //室內精準定位下,獲取樓宇名稱
location.getFloor(); //室內精準定位下,獲取當前位置所處的樓層信息
4. 使用百度Map
在使用百度地圖之后,我們需要做的基本的功能就是在地圖上定位到自己當前的位置。
- 修改activity_main.xml 文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<TextView
android:id="@+id/tv_postion"
android:layout_width="match_parent"
android:layout_height="150dp"
android:text="Hello World!"
android:visibility="gone"
/>
<com.baidu.mapapi.map.MapView
android:id="@+id/map_bd"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true"
/>
</LinearLayout>
- 修改MainActivity 文件,很簡單的,首先調用SDKInitializer.initialize(getApplicationContext());來進行初始化操作,記得一定要在setContentView()之前,不然會出錯。然后添加一個MapView,重寫onResume() ,onPauser()和onDestory()三個方法對MapView進行管理。
public class MainActivity extends AppCompatActivity {
private LocationClient mLocationClient;
private MapView mapView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SDKInitializer.initialize(getApplicationContext());
setContentView(R.layout.activity_main);
mapView = (MapView) findViewById(R.id.map_bd);
```
}
@Override
protected void onResume() {
super.onResume();
mapView.onResume();
}
@Override
protected void onPause() {
super.onPause();
mapView.onPause();
}
@Override
protected void onDestroy() {
super.onDestroy();
···
mapView.onDestroy();
}
}
但是這樣的地圖顯然不是我們想要的,我們需要的是可以直接定位到自己的當前的位置,并在地圖上顯示我的位置。
??修改代碼MainActivity文件,然后根據百度SDK中提供的BaiduMap類(地圖總控制器),調用MapView的getMap()方法來獲取BaiduMap的實例。得到了BaiduMap的實例之后,我們就可以對地圖進行各種各樣的操作。
??因為默認顯示的地圖的位置是北京,所以我們想移動到自己的當前的位置并縮放大小怎么辦?百度SDK中提供了LatLng 類來獲取經緯度。它的構造方法接收兩個參數,一個緯度,一個經度。再得到緯度和經度之后、我們想設置地圖的經緯度和地圖,需要用到MapStatus 類通過MapStatus.Builder的方法設置經緯度和地圖。最后將設置好的屬性裝載到BaiduMap里面
LatLng ll = new LatLng(bdLocation.getLatitude(),bdLocation.getLongitude());
MapStatus.Builder builder = new MapStatus.Builder();
builder.target(ll).zoom(18.0f);
baiduMap.animateMapStatus(MapStatusUpdateFactory.newMapStatus(builder.build()));
為了防止對此調用animateMapStatus這個變量,我們添加一個布爾值。
if(isFirstLocate){
LatLng ll = new LatLng(bdLocation.getLatitude(),bdLocation.getLongitude());
MapStatus.Builder builder = new MapStatus.Builder();
builder.target(ll).zoom(18.0f);
baiduMap.animateMapStatus(MapStatusUpdateFactory.newMapStatus(builder.build()));
isFirstLocate = false;
}
這只是完成了移動到當前的位置,還沒有顯示我在地圖的位置呢。百度SDK中有個MyLocationData的類,這個類封裝了設備當前的地理位置。調用它的Build()方法,生成一個實例。
MyLocationData.Builder locationBuilder = new MyLocationData.Builder();
locationBuilder.latitude(bdLocation.getLatitude());
locationBuilder.longitude(bdLocation.getLongitude());
MyLocationData locationData = locationBuilder.build();
baiduMap.setMyLocationData(locationData);
完整的代碼:
public class MainActivity extends AppCompatActivity {
private TextView tv_postion;
private LocationClient mLocationClient;
private MapView mapView;
private BaiduMap baiduMap;
private boolean isFirstLocate = true;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SDKInitializer.initialize(getApplicationContext());
setContentView(R.layout.activity_main);
tv_postion = (TextView) findViewById(R.id.tv_postion);
mapView = (MapView) findViewById(R.id.map_bd);
baiduMap = mapView.getMap();
baiduMap.setMyLocationEnabled(true);
initLocation();
initBaiDuMap();
List<String> permissionList = new ArrayList<>();
if(ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_FINE_LOCATION)!= PackageManager.PERMISSION_GRANTED){
permissionList.add(Manifest.permission.ACCESS_FINE_LOCATION);
}
if(ContextCompat.checkSelfPermission(this,Manifest.permission.READ_PHONE_STATE)!=PackageManager.PERMISSION_GRANTED){
permissionList.add(Manifest.permission.READ_PHONE_STATE);
}
if(ContextCompat.checkSelfPermission(this,Manifest.permission.WRITE_EXTERNAL_STORAGE)!=PackageManager.PERMISSION_GRANTED){
permissionList.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
}
if(!permissionList.isEmpty()){
String[] permissions= permissionList.toArray(new String[permissionList.size()]);
ActivityCompat.requestPermissions(MainActivity.this,permissions,1);
}else{
requestLocation();
}
}
private void initBaiDuMap() {
}
private void requestLocation() {
LocationClientOption option = new LocationClientOption();
option.setScanSpan(5000);
option.setIsNeedAddress(true);
mLocationClient.setLocOption(option);
mLocationClient.start();
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode){
case 1:
if(grantResults.length>0){
for (int result: grantResults
) {
if(result !=PackageManager.PERMISSION_GRANTED){
Toast.makeText(this, "必須同意所有的權限才能使用本程序", Toast.LENGTH_SHORT).show();
finish();
return;
}
}
requestLocation();
}else{
Toast.makeText(this, "發生了錯誤", Toast.LENGTH_SHORT).show();
}
break;
}
}
private void initLocation() {
mLocationClient = new LocationClient(getApplicationContext());
mLocationClient.registerLocationListener(new BDLocationListener() {
@Override
public void onReceiveLocation(BDLocation bdLocation) {
StringBuilder currentPosition = new StringBuilder();
currentPosition.append("維度:").append(bdLocation.getLatitude()).append("\n");
currentPosition.append("經度:").append(bdLocation.getLongitude()).append("\n");
currentPosition.append("國家:").append(bdLocation.getCountry()).append("\n");
currentPosition.append("省:").append(bdLocation.getProvince()).append("\n");
currentPosition.append("市:").append(bdLocation.getCity()).append("\n");
currentPosition.append("區:").append(bdLocation.getDistrict()).append("\n");
currentPosition.append("街道:").append(bdLocation.getStreet()).append("\n");
currentPosition.append("門牌號:").append(bdLocation.getStreetNumber()).append("\n");
currentPosition.append("定位方式:");
if(bdLocation.getLocType() == BDLocation.TypeGpsLocation ||bdLocation.getLocType() == BDLocation.TypeNetWorkLocation){
if(isFirstLocate){
LatLng ll = new LatLng(bdLocation.getLatitude(),bdLocation.getLongitude());
MapStatus.Builder builder = new MapStatus.Builder();
builder.target(ll).zoom(18.0f);
baiduMap.animateMapStatus(MapStatusUpdateFactory.newMapStatus(builder.build()));
isFirstLocate = false;
}
MyLocationData.Builder locationBuilder = new MyLocationData.Builder();
locationBuilder.latitude(bdLocation.getLatitude());
locationBuilder.longitude(bdLocation.getLongitude());
MyLocationData locationData = locationBuilder.build();
baiduMap.setMyLocationData(locationData);
}
// tv_postion.setText(currentPosition);
}
});
// mLocationClient.start();
}
@Override
protected void onResume() {
super.onResume();
mapView.onResume();
}
@Override
protected void onPause() {
super.onPause();
mapView.onPause();
}
@Override
protected void onDestroy() {
super.onDestroy();
mLocationClient.stop();
mapView.onDestroy();
baiduMap.setMyLocationEnabled(false);
}
}
這樣就可以完成想要的效果了,寫這一章的目的是為了方便講解一下百度地圖的基本用法,具體的內容還要看百度地圖的開發文檔。