***转载、引用请标明出处***
http://www.jianshu.com/p/29ccac3e1e42
本文出自[zhh_happig的简书博客](http://www.jianshu.com/u/d82bd37b1d29),谢谢


[Android百度地图(二):百度地图sdk显示位置点、图层绘制](http://www.jianshu.com/p/fdd1ba783495)
[Android百度地图(三):百度地图画运动轨迹及图层点击事件处理](http://www.jianshu.com/p/2ad4c2077dfd)
[Android百度地图(四):百度地图运动轨迹纠偏、去噪、绑路之百度鹰眼sdk服务](http://www.jianshu.com/p/3c3d9e92739d)
[Android百度地图(五):百度地图鹰眼sdk监控进出地理围栏(区域)](http://www.jianshu.com/p/db11ae2bad4f)
[Android百度地图(六):百度地图POI检索,行政区边界、公交、线路规划查询,地理编码介绍](http://www.jianshu.com/p/026fd4f96d4d)


本文主要针对百度定位sdk的api进行详细说明、注意事项,还会简单的谈一下定位的原理,目的是让读者真正了解定位,只有在完全了解的情况下,在开发中才会少走弯路,少踩坑。
##一  定位简介
目前定位大致分为三种:gps定位、wifi定位、基站定位


**gps定位**:卫星定位,利用手机gps硬件定位,实现简单,手机本地就能实现定位,不需要和服务端进行交互。
```java
//伪代码
LocationManager lm = (LocationManager) getSystemService(LOCATION_SERVICE);
lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 5, listener);
```
手机GPS状态说明
![以小米手机为例。如上图GPS标识为高亮状态,代表gps已经开启,可进行gps定位](http://upload-images.jianshu.io/upload_images/4153803-b6f639c0b5a42378.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
![如上图红框内灰暗标识,说明gps正在搜索卫星信号,虽然此时已经开始gps定位,但
是不会有gps位置返回,直到搜索到至少4颗卫星信号,才能定位成功;在室外开阔地
搜星的过程几分钟到十几分钟都有可能,如果gps长时间无法定位,尝试重启手机](http://upload-images.jianshu.io/upload_images/4153803-f7a41850f6788cfd.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
![如上图红框内gps标识变成高亮,说明gps已经搜索到卫星信号,开始了正常定位](http://upload-images.jianshu.io/upload_images/4153803-3ff26e0d33daf3c4.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
在桥梁下、大树下、建筑物内、隧道内,手机很多时候都无法接收到gps信号,就无法利用gps定位了。
在室内无法定位是gps定位的最大问题,此时就得利用WiFi定位了。


**wifi定位**:一说到wifi定位,很多人觉得奇怪,WiFi怎么能知道我的位置呢,wifi硬件会返回位置吗?其实这些WiFi都不能做到。wifi定位的原理是,我们在室外的时候,手机能接收到gps位置信息,也能扫描到WiFi,当手机把gps位置和WiFi传到后端服务器,WiFi和gps位置就建立了映射关系,当手机在室内无法接收到gps时,却能扫描WiFi,手机把WiFi传到服务器查询出对应的gps位置,然后进行计算可以得到位置结果(经纬度),其中映射和计算是很复杂的过程,感兴趣的同学可以查阅相关资料。wifi定位需要wifi开启,并且手机能上网。


**基站定位**:当手机无法扫描到WiFi时,只要装了sim卡,就能连接移动或联通等基站,即可用基站定位,定位原理和WiFi大致相同。基站定位需要装了sim卡,并且手机能上网。


**wifi定位与基站定位统称为网络定位**,当wifi关闭,或者扫描不到WiFi列表时,sdk只会把获取的基站信息发给服务端,进行基站定位;如果没有sim,wifi开启,则sdk会把扫描到的WiFi信息发给服务端,进行wifi定位;如果既有wifi又有基站,则sdk会把这两者信息都发给服务端,具体用哪一种定位,不太确定,但绝大多数情况下都是用wifi定位的。


在室内无gps时,百度定位就是利用的WiFi和基站定位的,在室外有gps时,百度是利用的android自带的LocationManager进行定位,当然室外也可以用WiFi基站定位。


gps定位精度很高,几米到十几米,但是耗电严重;
WiFi定位精度相对于gps差一点,但是也能到十几米、几十米,也有上百米的误差的,低耗电;
基站定位精度很差,一般都有几百米,上千米的误差。
这个误差其实和wifi、基站的信号覆盖半径有关,wifi覆盖半径大概100m左右,而基站的覆盖半径就到km级别了。


##二  百度定位sdk配置
**1.必须申请apikey**
```java
  android:name="com.baidu.lbsapi.API_KEY"
  android:value="P9XLjU5FvdtDhRUTumdg2xRTpmk*****" />
```
每个apikey对应一个唯一的应用,这个apikey的主要作用是统计每个应用每天的定位次数,虽然百度没有说明每天的访问次数,但是应该有一个访问次数。比如恶意攻击,设置访问次数可防止恶意攻击。


**2.导入jar、库文件**
BaiduLBS_Android.jar:提供定位sdk的api
liblocSDK7a.so:这个so文件的主要作用是对客户端发送给服务端的参数数据进行加密
开发包[下载地址](http://lbsyun.baidu.com/sdk/download?selected=location_all),选择基础定位即可。


**3.设置AndroidManifest.xml**
```java
  android:name="com.baidu.location.f"
  android:enabled="true"
  android:process=":remote" >
 
          android:name="com.baidu.location.service_v2.2">
   
 


 ```
周期性的定位请求是在这个服务中发起的,如果不声明这个Service,周期性请求无法正常工作,但可进行一次定位。


**4.声明使用权限**
```java


















```
**注:以上介绍和配置,高德、腾讯sdk也差不多是这样的,这些sdk不一样的就是下面的api不一样,但是定位原理流程都是大同小异的**


##三  定位api详解
1.初始化LocationClient类
此处需要注意:LocationClient类最好在主线程中声明,需要Context类型的参数。
Context需要时全进程有效的Context,推荐用getApplicationConext获取全进程有效的Context
```java
//伪代码
public LocationClient mLocationClient = null;;
public void onCreate()
{
  mLocationClient = new LocationClient(getApplicationContext());//声明LocationClient类
  mLocationClient.registerLocationListener(myListener);//注册监听函数
 }
```
2.配置定位SDK参数
设置定位参数包括:定位模式(高精度定位模式、低功耗定位模式和仅用设备定位模式),返回坐标类型,是否打开GPS,是否返回地址信息、位置语义化信息、POI信息等等。
LocationClientOption类,该类用来设置定位SDK的定位方式,例如:
```java
//伪代码
LocationClientOption mOption = new LocationClientOption();


/**
* 默认高精度,设置定位模式
* LocationMode.Hight_Accuracy 高精度定位模式:这种定位模式下,会同时使用
  网络定位(Wi-Fi和基站定位)和GPS定位,优先返回最高精度的定位结果;
  但是在室内gps无信号,只会返回网络定位结果;
  室外如果gps收不到信号,也只会返回网络定位结果。
* LocationMode.Battery_Saving 低功耗定位模式:这种定位模式下,不会使用GPS,只会使用网络定位。
* LocationMode.Device_Sensors 仅用设备定位模式:这种定位模式下,
  不需要连接网络,只使用GPS进行定位,这种模式下不支持室内环境的定位
*/
mOption.setLocationMode(LocationMode.Hight_Accuracy);


/**
* 默认是true,设置是否使用gps定位
* 如果设置为false,即使mOption.setLocationMode(LocationMode.Hight_Accuracy)也不会gps定位
*/
mOption.setOpenGps(true);


/**
* 默认gcj02,设置返回的定位结果坐标系,如果配合百度地图使用,建议设置为bd09ll;
* 目前国内主要有以下三种坐标系:
1. wgs84:目前广泛使用的GPS全球卫星定位系统使用的标准坐标系;
2. gcj02:经过国测局加密的坐标;
3. bd09:为百度坐标系,其中bd09ll表示百度经纬度坐标,bd09mc表示百度墨卡托米制坐标;
* 在国内获得的坐标系类型可以是:国测局坐标、百度墨卡托坐标 和 百度经纬度坐标。
  在海外地区,只能获得WGS84坐标。请在使用过程中注意选择坐标。
*/
mOption.setCoorType("bd09ll");


/**
* 默认0,即仅定位一次;设置间隔需大于等于1000ms,表示周期性定位
* 如果不在AndroidManifest.xml声明百度指定的Service,周期性请求无法正常工作
* 这里需要注意的是:如果是室外gps定位,不用访问服务器,设置的间隔是3秒,那么就是3秒返回一次位置
  如果是WiFi基站定位,需要访问服务器,这个时候每次网络请求时间差异很大,设置的间隔是3秒,
  只能大概保证3秒左右会返回就一次位置,有时某次定位可能会5秒才返回
*/
mOption.setScanSpan(3000);


/**
* 默认false,设置是否需要地址信息
* 返回省、市、区、街道等地址信息,这个api用处很大,
  很多新闻类app会根据定位返回的市区信息推送用户所在市的新闻
*/
mOption.setIsNeedAddress(true);


/**
* 默认false,设置是否需要位置语义化结果
* 可以在BDLocation.getLocationDescribe里得到,结果类似于“在北京天安门附近”
*/
mOption.setIsNeedLocationDescribe(true);


/**
* 默认false,设置是否需要设备方向传感器的方向结果
* 一般在室外gps定位时,返回的位置信息是带有方向的,但是有时候gps返回的位置也不带方向,
  这个时候可以获取设备方向传感器的方向
* wifi基站定位的位置信息是不带方向的,如果需要可以获取设备方向传感器的方向
*/
mOption.setNeedDeviceDirect(false);


/**
* 默认false,设置是否当gps有效时按照设定的周期频率输出GPS结果
* 室外gps有效时,周期性1秒返回一次位置信息,其实就是设置了
locationManager.requestLocationUpdates中的minTime参数为1000ms,1秒回调一个gps位置
* 如果设置了mOption.setScanSpan(3000),那minTime就是3000ms了,3秒回调一个gps位置
*/
mOption.setLocationNotify(false);


/**
* 默认true,定位SDK内部是一个SERVICE,并放到了独立进程,设置是否在stop的时候杀死这个进程,默认不杀死
* 如果你已经拿到了你要的位置信息,不需要再定位了,不杀死留着干嘛
*/
mOption.setIgnoreKillProcess(true);


/**
* 默认false,设置是否需要POI结果,可以在BDLocation.getPoiList里得到
* POI就是获取到的位置附近的一些商场、饭店、银行等信息
*/
mOption.setIsNeedLocationPoiList(true);


/**
* 默认false,设置是否收集CRASH信息,默认收集
*/
mOption.SetIgnoreCacheException(false);


/**
* 默认false,设置定位时是否需要海拔高度信息,默认不需要,除基础定位版本都可用
*/
mOption.setIsNeedAltitude(false);


mLocationClient.setLocOption(mOption);//设置定位参数
mLocationClient.start();//开始定位


```
3.实现BDLocationListener接口
BDLocation类,封装了定位SDK的定位结果,在BDLocationListener的onReceiveLocation方法中获取,onReceiveLocation方法当返回的是网络类型定位时是在**子线程**中执行了,如果有UI操作,请注意。
```java
//伪代码
private BDLocationListener myListener = new BDLocationListener() {


  @Override
  public void onReceiveLocation(BDLocation location) {
    //定位sdk获取位置后回调
    if (null != location && location.getLocType() != BDLocation.TypeServerError) {

      /**
      * location.getTime() 是指服务端出本次结果的时间,如果位置不发生变化,则时间不变
      */
      location.getTime();
    
      /**
 * 定位类型
 BDLocation.TypeGpsLocation----gps定位
 BDLocation.TypeNetWorkLocation----网络定位(wifi基站定位)
 以及其他定位失败信息
 */
 location.getLocType();

 /**
 * 对应的定位类型说明
 * 比如"NetWork location successful"之类的信息
 */
 location.getLocTypeDescription();

 /**
 * 纬度
 */
 location.getLatitude();

 /**
 * 经度
 */
 location.getLongitude();

 /**
 * 误差半径,代表你的真实位置在这个圆的覆盖范围内,
      * 半径越小代表定位精度越高,位置越真实
      * 在同一个地点,可能每次返回的经纬度都有微小的变化,
      * 是因为返回的位置点并不是你真实的位置,有误差造成的。
 */
 location.getRadius();

 location.getCountryCode();//国家码,null代表没有信息
 location.getCountry();//国家名称
 location.getCityCode();//城市编码
 location.getCity();//城市
 location.getDistrict();//区
 location.getStreet();//街道
 location.getAddrStr();//地址信息
 location.getLocationDescribe();//位置描述信息

 /**
 * 判断用户是在室内,还是在室外
 * 1:室内,0:室外,这个判断不一定是100%准确的
  */
 location.getUserIndoorState();

 /**
 * 获取方向
 */
 location.getDirection();

 if (location.getPoiList() != null && !location.getPoiList().isEmpty()) {
   for (int i = 0; i < location.getPoiList().size(); i++) {
     Poi poi = (Poi) location.getPoiList().get(i);
     poi.getName();//获取位置附近的一些商场、饭店、银行等信息
   }
 }

 if (location.getLocType() == BDLocation.TypeGpsLocation) {// GPS类型定位结果
   location.getSpeed();//速度 单位:km/h,注意:网络定位结果是没有速度的
   location.getSatelliteNumber();//卫星数目,gps定位成功最少需要4颗卫星
   location.getAltitude();//海拔高度 单位:米
   location.getGpsAccuracyStatus();//gps质量判断
 } else if (location.getLocType() == BDLocation.TypeNetWorkLocation) {//网络类型定位结果
   if (location.hasAltitude()) {//如果有海拔高度
     location.getAltitude();//单位:米
   }
   location.getOperators();//运营商信息
 } else if (location.getLocType() == BDLocation.TypeOffLineLocation) {// 离线定位结果
   //离线定位成功,离线定位结果也是有效的;
 } else if (location.getLocType() == BDLocation.TypeServerError) {
        //服务端网络定位失败,可以反馈IMEI号和大体定位时间到loc-bugs@baidu.com;
 } else if (location.getLocType() == BDLocation.TypeNetWorkException) {
   //网络不同导致定位失败,请检查网络是否通畅;
 } else if (location.getLocType() == BDLocation.TypeCriteriaException) {
   //无法获取有效定位依据导致定位失败,一般是由于手机的原因,处于飞行模式下一般会造成这种结果,可以试着重启手机;
 }
    }
  }


};
```
4.定位完成注意释放资源
```java
//伪代码
protected void onStop() {
  mLocationClient.unregisterListener(myLocationListener); //注销掉监听
  mLocationClient.stop(); //停止定位
}
```
仅一次定位:
mLocationClient.start()启动定位SDK,在BDLocationListener的onReceiveLocation方法中获取到位置后,
再 mLocationClient.stop()关闭定位SDK即可。
周期性定位:
设置周期间隔,单位ms,必须大于等于1000ms才进行周期性定位,小于1000只定位一次。
locationClientOption .setScanSpan(1000);
mLocationClient.start();
每次获取位置都会在BDLocationListener的onReceiveLocation方法中回调,
不再进行定位, mLocationClient.stop()关闭定位SDK。
##四  定位错误分析
首先,一定确定你的配置都完成了:
1.apikey申请正确,并在AndroidManifest.xml配置;
2.权限都申请了,注意6.0权限的动态申请;
3.AndroidManifest.xml中定位服务配置了;
4.jar包以及so包都导入了。
在Android Studio除了上述操作之外,还需要在build.gradle中配置SO文件的使用,如下所示
```java
android{
    ...
    sourceSets {
        main {
       jniLibs.srcDirs = ['libs']
        }
    }
}
```
![](http://upload-images.jianshu.io/upload_images/4153803-22887b001169ff93.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


另外,ABI:指应用基于哪种指令集来进行编译,ABI分armeabi、armeabi-v7a、mips、x86、x86_64等,它们都是表示cpu的类型,确定自己的机器是什么类型的cpu,导入相应的so文件。


如果所有配置正确,在a手机中正常,在b手机出错了,原因很有可能就是ab手机的cpu类型不同导致的。


查看cpu类型命令
1、adb shell  
2、cat  /proc/cpuinfo


详细cpu间so文件的兼容问题,请阅读相关资料


**如果确定所有配置正确,还有问题,则需要通过获取定位返回错误码分析错误原因**
bDLocation.getLocType ( );//获取错误码


返回值:
61 : GPS定位结果,GPS定位成功。
62 : 无法获取有效定位依据(wifi基站信息),定位失败,请检查运营商网络或者WiFi网络是否正常开启,尝试重新请求定位。
63 : 网络异常,没有成功向服务器发起请求,请确认当前测试手机网络是否通畅,尝试重新请求定位。
65 : 定位缓存的结果。
66 : 离线定位结果。通过requestOfflineLocaiton调用时对应的返回结果。
67 : 离线定位失败。通过requestOfflineLocaiton调用时对应的返回结果。
68 : 网络连接失败时,查找本地离线定位时对应的返回结果。
161: 网络定位结果,网络定位成功。
162: 请求串密文解析失败,一般是由于客户端SO文件加载失败造成,请严格参照开发指南或demo开发,放入对应SO文件。
167: 服务端定位失败,请您检查是否禁用获取位置信息权限,尝试重新请求定位。
502: AK参数错误,请按照说明文档重新申请AK。
505:AK不存在或者非法,请按照说明文档重新申请AK。
601: AK服务被开发者自己禁用,请按照说明文档重新申请AK。
602: key mcode不匹配,您的AK配置过程中安全码设置有问题,请确保:SHA1正确,“;”分号是英文状态;且包名是您当前运行应用的包名,请按照说明文档重新申请AK。
501~700:AK验证失败,请按照说明文档重新申请AK。


如果以上都不能解决你定位sdk使用中出现的问题,可以去[百度定位sdk开发社区](http://bbs.lbsyun.baidu.com/forum.php?mod=forumdisplay&fid=10)查阅或发帖询问,有百度工程师解答。


如果各位看官觉得文章不错,别忘了点个喜欢。
[源码下载地址 https://github.com/zhuhaoHappig/BaiduMapApi](https://github.com/zhuhaoHappig/BaiduMapApi)


以上文章内容,是本人工作中的总结,供大家参考,有误的地方还请指正。

更多相关文章

  1. Android(安卓)GPS学习笔记—LMS初始化
  2. android中gps的使用以及解析nmea0183协议
  3. Android输入法遮挡问题的解决思路
  4. android google map的使用
  5. android 基于百度地图api获取经纬度
  6. Android(安卓)2.3 StatusBar 分析(二)
  7. android 基于百度地图api获取经纬度
  8. Android(安卓)Studio下的build工具路径和设置位置
  9. Deepin Android(安卓)Studio 修改默认源 提高下载速度

随机推荐

  1. 【转】模拟器中运行编译好的Android
  2. Android(安卓)- Device Administration
  3. Android:自定义标题栏
  4. Android(安卓)Layout之四:Table Layout
  5. Android(安卓)Fragment动态创建时replace
  6. Android—WebView介绍
  7. Android(安卓)ListView理解,BaseAdapter
  8. FragmentPagerAdapter notifyDataSetChan
  9. android开发之读取xml文件
  10. ADB