一, 基于位置的服务简介
LBS: 基于位置的服务. 随着移动互联网的兴起, 这个技术在最近的几年里十分火爆. 其实它本身并不是什么时髦的技术, 主要的工作原理就是利用无线电通讯网络或 GPS 等定位方式来确定出移动设备所在的位置, 而这种技术早在很多年前就已经出现了.
那么为什么 LBS 技术直到最近几年才开始流行呢? 这主要是因为, 在过去移动设备的功能及其有限, 即使定位到了设备所在的位置, 也就仅仅只是定位到了而已, 我们并不能在位置的基础上进行一些其他的操作. 而现在就大大不同了, 有了 Android 系统作为载体, 我们可以利用定位出的位置进行许多丰富多彩的操作. 比如说天气预报程序可以根据用户所在的位置自动选择城市, 发微博的时候我们可以向朋友们晒一下自己在哪里, 不认识路的时候随时打开地图就可以查询路线等等.
在学习之前, 还有一些是需要知道的. 首先要清楚, 基于位置的服务所围绕的核心就是要先确定出用户所在的位置. 通常有两种技术方式可以实现: 一种是通过 GPS 定位, 一种是通过网络定位. GPS 定位的工作原理是基于手机内置的 GPS 硬件直接和卫星交互来获取当前的经纬度信息, 这种定位方式精确度非常高, 但缺点是只能在室外使用, 室内基本无法接收到卫星的信号. 网络定位的工作原理是根据手机当前网络附近的三个基站进行测速, 以此计算出手机和每个基站之间的距离, 在通过三角定位确定一个大概的位置, 这种定位方式精确度一般, 但优点是在室内外都可以使用.
Android 对这两种定位方式都提供了相应的 API 支持, 但是由于一些特殊原因, Google 的网络服务在中国不可访问, 从而导致网络定位方式的 API 失效. 而 GPS 定位虽然不需要网络, 但是必须在室外才可以使用, 因此你在室内开发的时候很有可能遇到不管使用哪种定位方式都无法成功定位的情况.
基于以上原因, 在这里就不讲解 Android 原生定位 API 的用法了, 而是使用一些国内第三方公司的 SDK. 目前国内在这一领域做的比较好的一个是百度, 一个是高德, 下面就来看一下百度在 LBS 方面提供的丰富多彩的功能.
二, 申请 API Key
要想在自己的应用程序里使用百度的 LBS 功能, 首先必须申请一个 API Key. 你得拥有一个百度账号才能进行申请, 如果没有就需要先去注册一个. 有了百度账号之后, 我们就可以申请成为一名百度开发者了, 进入百度云登录网址: https://login.bce.baidu.com/?account=, 登录后然后访问: http://lbsyun.baidu.com/apiconsole/key 这个网址:
由于是刚刚注册的账号, 所以目前的应用列表是空的, 接下来点击创建应用就可以去申请 API Key 了, 应用名称可以随便填, 应用类型选择 Android SDK, 启用服务保持默认即可:
那么发布版本 SHA1 和开发版 SHA1 又是什么东西呢? 这是我们申请 API Key 所必须填写的一个字段, 它指的是打包程序时所用签名文件的 SHA1 指纹, 可以通过 Android Studio 查看到. 打开 Android Studio 中的任意一个项目, 点击右侧工具栏的: Gradle ---> 项目名 ---> :App ---> Tasks ---> Android, 如下所示:
这里展示了一个 Android Studio 项目中所有内置的 Gradle Tasks, 其中 signingReport 这个 Task 就可以用来查看签名文件信息. 双击 signingReport, 结果如图所示:
如图所示就是我们所需的 SHA1 指纹了, 当然这个指纹每个人是不同的, 这是我的. 另外需要注意的是: 目前我们使用的是 debug.keystore 文件所生成的指纹, 这是 Android 自动生成的一个用于测试签名文件. 而当你的应用程序发布时还需要创建一个正式的签名文件, 如果要得到它的指纹, 可以在 cmd 中输入如下命令:
Keytool -list -v -keystore <签名文件路径>
然后输入正确的密码就可以了, 创建签名文件的方法网上有资源可以学到, 这里就不多说了.
那么也就是说, 现在得到的这个 SHA1 指纹实际上是一个开发版的 SHA1 指纹, 不过因为暂时我们还没有一个发布版的 SHA1 指纹, 因此这两个值都填成一样的就可以了. 最后还剩一个包名选择, 虽然目前我们的应用程序还不存在, 但可以先将包名预定下来, 比如叫: com.workspace.hh.lbstest, 这样所有的内容就填写完整了, 如下所示:
接下来点击提交, 应用就创建成功了, 如下所示:
其中访问应用那一列就是申请到的 API Key, 有了它就可以进行后续的 LBS 开发工作了.
三, 使用百度定位
新建一个 LBSTest 项目, 包名为 com.workspace.hh.lbstest, 另外需要注意的是, 下面中所写的代码建议都在手机上运行, 虽然模拟器中也提供了模拟地理位置的功能, 但在手机上可以得到真实的位置数据, 感受会更加深刻.
3.1, 准备 LBS SDK
在开始编码之前, 我们还需要先从百度 LBS 开发平台的 SDK 准备好, 下载地址是: http://lbsyun.baidu.com/index.php?title=sdk/download&action#selected=location_all, 将全量定位和基础地图两个 SDK 都勾选上, 点击 "开发包" 下载按钮即可:
下载完成后对该压缩包解压, 其中会有一个 libs 目录, 这里面的内容就是我们所需要的一切:
libs 目录下的内容又分为两部分, BaiduLBS_Android.jar 这个文件是 Java 层要使用到的其他子目录下的 so 文件是 Native 层要用到的. so 文件是用 C/C++ 语言进行编写的, 然后再用 NDK 编译出来的. 当然这里我们并不需要去编写 C/C++ 的代码, 因为百度都已经做好了封装, 但是我们需要将 libs 目录下的每一个文件都放置到正确的位置.
首先观察一下当前的项目结构, 你会发现 App 模块下面有一个 libs 目录, 这里就是用来存放所有的 Jar 包的, 我们将 BaiduLBS_Android.jar 复制到这里:
接下来展开 src/main 目录, 右击该目录 -> New -> Directory, 再创建一个名为 jniLibs 的目录, 这里就是专门用来存放 so 文件的, 然后把压缩包里的其他所有目录直接复制到这里:
另外, 虽然所有新创建的项目中, App/build.gradle 文件都会默认配置以下这段声明:
这表示会将 libs 目录下所有以 (.jar) 结尾的文件添加到当前项目的引用中, 但是由于我们是直接将 Jar 包复制到 libs 目录下的, 并没有修改 gradle 文件, 因此不会弹出我们平时熟悉的 Sync Now 提示, 这个时候必须手动点击一下 Android Studio 顶部工具栏中的 Sync 按钮, 如图所示, 不然项目将无法引用到 Jar 包中提供的任何接口:
点击 Sync 按钮之后, libs 目录下的 jar 文件就会多出一个向右的箭头, 这就表示项目已经能引用到这些 Jar 包了, 如图所示:
好了, 这样我们就把 LBS 的 SDK 都准备好了, 接下来开始编码.
3.2, 确定自己位置的经纬度
第一步: 首先在布局文件中添加一个 TextView 控件, 用于稍后显示当前位置的经纬度信息
第二步: 修改 AndroidManifest.xml 文件中的代码, 如下所示:
AndroidManifest.xml 文件中, 首先添加了很多行权限声明, 每一个权限都是百度 LBS SDK 内部要用到的. 然后在 < application > 标签的内部添加了一个 < meta-data > 标签, 这个标签的 Android:name 部分是固定的, 必须填: com.baidu.lbsapi.API_KEY,Android:value 部分则应该填入上面我们申请到的 API Key, 最后, 还需要再注册一个 LBS SDK 中的服务, 不用对这个服务的名字感到疑惑, 因为百度 LBSSDK 中的代码都是混淆过的.
第三步: 修改 MainActivity 中的代码:
代码分析:
可以看到, 在 onCreate()方法中, 我们首先创建了一个 LocationClient 的实例, LocationClient 的构建函数接收一个 Context 参数, 这里调用 getApplicationContext()方法来获取一个全局的 Context 参数并传入. 然后调用 LocationClient 的 registerLocationListener()方法来注册一个定位监听器, 当获取到位置信息的时候, 就会回调这个定位监听器.
接下来看一下这里运行时权限的用法, 由于我们在 AndroidManifest.xml 文件中声明了很多权限, 参考一下危险权限表格可以发现, 其中 ACCESS_COARSE_LOCATION,ACCESS_FINE_LOCATION,READ_PHONE_STATE,WRITE_EXTERNAL_STORAGE 这 4 个权限是需要进行运行时权限处理的, 不过由于 ACCESS_COARSE_LOCATION 和 ACCESS_FINE_LOCATION 都属于同一个权限组, 因此两者只要申请其一就可以了. 那么怎么样才能在运行时一次性申请 3 个权限呢? 这里我们使用了一种新的用法, 首先创建了一个空的 List 集合, 然后依次判断 3 个权限有没有被授权, 如果没有被授权就添加到 List 集合中, 最后将 List 转换成数组, 再调用 ActivityCompat.requestPermissions()方法一次性申请.
除此之外, onRequestPermissionResult()方法中对权限申请结果的逻辑处理也和之前有所不同, 这次我们通过一个循环将申请的每个权限都进行了判断, 如果有任何一个权限被拒绝, 那么直接调用 finish()方法关闭当前程序, 只有当所有权限都被用户同意了, 才会调用 requestLocation()方法开始地理位置定位.
requestLocation()方法中的代码比较简单, 只是调用了一下 LocationClient 的 start()方法就能开始定位了. 定位的结果会回调到我们前面注册的监听器当中, 也就是 MyLocationListener. 观察一下 MyLocationListener 的 onReceiveLocation()方法中, 在这里我们通过 BDLocation 的 getLatitude()方法获取当前位置的纬度, 通过 getLongitude()方法获取当前位置的经度, 通过 getLocType()方法获取当前的定位方式, 最终将结果组装成一个字符串, 显示到 TextView 上面.
第四步: 运行程序, 打开程序首先就会弹出运行时权限的申请对话框, 注意看对话框的底部, 提示我们一共有 3 项权限申请, 当前是第一项, 授权了第一项后就会显示第二项, 这里我们完全点击允许, 然后就会立刻开始定位了, 结果如下, 可以看到, 设备当前的经纬度信息已经成功定位出来了.
不过在默认情况下, 调用 LocationClient 的 start()方法只会定位一次, 如果我们正在快速移动中, 怎样才能实时更新当前的位置呢? 为此, 百度 LBS SDK 提供了一系列的设置方法, 来允许我们更改默认的行为, 修改 MainActivity 中的代码, 如下所示:
这里增加了一个 initLocation()方法, 在 initLocation()方法中我们创建了一个 LocationClientOption 对象, 然后调用它的 setScanSpan()方法来设置更新的间隔, 这里传入了 5000, 表示每 5 秒钟会更新一下当前的位置.
最后要记得, 在活动被销毁的时候一定要调用 LocationClient 的 stop()方法来停止定位, 不然程序会持续在后台不停的进行定位, 从而严重消耗手机的电量.
现在重新运行一下程序, 然后拿着手机随处移动, 你会发现界面上的经纬度信息也会跟着一起变化.
3.3, 选择定位模式
前面说到, Android 中主要有两种定位方式: 一种是通过 GPS 定位, 一种是通过网络定位. 而从上面一小节中的例子中应该可以看出, 我们一直是使用的网络定位, 那么如何才能切换到精确度更高的 GPS 定位呢?
首先, GPS 定位功能必须要由用户主动去启用才行, 不然任何应用程序都无法使用 GPS 获取到手机当前的位置信息. 进入手机的设置 -> 位置信息. 我们可以通过顶部的开关来控制定位功能是开启还是关闭, 另外, 点击 "模式" 可以选择具体的定位模式.
其中, 高精度模式表示允许使用 GPS, 无线网络, 蓝牙或移动网络来进行定位, 节电模式表示仅允许使用无线网络, 蓝牙或移动网络来进行定位, 而仅限设备模式表示仅允许使用 GPS 来进行定位. 也就是说, 如果我们想要使用 GPS 定位功能, 这里必须要选择高精度模式, 或者仅限设备模式.
当然并不需要担心一旦启用 GPS 定位功能后, 手机的电量就会直线下滑, 这只是表明你已经同意让应用程序来对你的手机进行 GPS 定位了, 但只有当定位操作真正开始的时候, 才会影响到手机的电量.
开启了 GPS 定位功能之后, 再回来看一下代码. 我们可以在 initLocation()方法中对百度 LBS SDK 的定位模式进行指定, 一共有 3 种模式可选: Hight_Accuracy,Battery_Saving,Device_Sensors.Hight_Accuracy 表示高精确度模式, 会在 GPS 信号正常的情况下优先使用 GPS 定位, 在无法接收 GPS 信号的时候使用网络定位. Battery_Saving 表示节电模式, 只会使用网络进行定位. Device_Sensors 表示传感器模式, 只会使用 GPS 进行定位. 其中, Hight_Accuracy 是默认的模式, 也就是说, 我们即使不修改任何代码, 只要拿着手机走到室外去, 让手机可以接收到 GPS 信号, 就会自动切换到 GPS 定位模式了.
当然我们也可以强制指定只使用 GPS 进行定位, 修改 MainActivity 中的代码:
这里调用了 setLocationMode()方法来将定位模式指定成传感器模式, 也就是说只能使用 GPS 进行定位, 重新运行一下程序, 然后拿着你的手机走到室外去, 会看到定位方式那里变成了 GPS.
3.4, 看得懂的位置信息
刚才我们虽然成功获取到了设备当前位置的经纬度信息, 但是这种经纬度一般人是根本看不懂的, 为了更加直观地阅读, 我们还需要学习一下如何获取看得懂的位置信息.
幸运的是, 百度 LBS SDK 在这方面提供了非常好的支持, 我们只需要进行一些简单的接口调用就能得到当前位置各种丰富的地址信息, 下面就来看看吧:
第一步: 修改 MainActivity 中的代码:
代码分析:
首先在 initLocation()方法中, 我们调用了 LocationClientOption 的 setIsNeedAddress()方法, 并传入 true, 这就表示我们需要获取当前位置详细的地址信息.
接下来在 MyLocationListener 的 onReceiveLocation()方法就可以获取到各种丰富的地址信息了, 调用 getCountry()方法可以得到当前所在国家, 调用 getProvince()方法可以得到当前所在省份, 以此类推, 另外还有一点需要注意, 由于获取地址信息一定需要用到网络, 因此即使我们将定位模式指定成了 Device_Sensors, 也会自动开启网络定位功能.
现在重新运行一下程序, 如图所示, 可以看到, 手机当前位置的地址信息已经成功显示出来了. 如果你带着手机移动了较远的距离. 界面上显示的位置也会跟着一起变化的.
四, 使用百度地图
现在手机地图的应用真的可以算得上是非常广泛了, 和 PC 上的地图相比, 手机地图能够随时随地进行查看, 并且轻松构建出行路线, 使用起来明显更加地方便. 其实我们在自己的应用程序里也是可以加入地图功能的, 比如优步中使用的就是百度地图.
4.1, 让地图显示出来
前面我们已经把 LBS SDK 全部准备好了, 其中包括地图功能, 因此这里就不用再去下载百度地图的 SDK 了. 我们直接在 LBSTest 项目的基础上进行开发:
第一步: 修改 activity_main.xml 中的代码
在布局文件中新放置了一个 MapView 控件, 并让它填充满整个屏幕, 这个 MapView 是由百度提供的自定义控件, 所以在使用它的时候需要将完整的包名加上. 另外, 之前用于显示定位信息的 TextView 现在暂时用不到了, 将它的 visibility 属性指定为 gone, 让它在界面上隐藏起来.
第二步: 修改 MainActivity 中的代码
可以看到, 这里的代码非常简单. 首先需要调用 SDKInitiallize()方法来进行初始化操作, initialize()方法接收一个 Context 参数, 这里我们调用 getApplicationContext()方法来获取一个全局的 Context 参数并传入. 注意初始化操作一定要在 setContentView()方法前调用, 不然的话就会出错. 接下来我们调用 findViewById()方法获取到了 MapView 的实例, 这个实例在后面的功能当中还会用到.
另外还需要重写 onResume(),onPause(),onDestroy()这 3 个方法, 在这里对 MapView 进行管理, 以保证资源能够及时地得到释放.
第三步: 运行程序, 百度地图就显示出来了.
4.2, 移动到我的位置
地图是成功的显示出来了, 但是这是一张默认的地图, 显示的是北京市中心的位置, 而我们希望是看到更加精细的地图信息, 比如说自己所在的位置的周边环境. 显然, 通过缩放和移动的方式来慢慢找到自己的位置是一种很愚蠢的做法.
百度 LBS SDK 的 API 中提供了一个 BaiduMap 类, 它是地图的总控制器, 调用 MapView 的 getMap()方法就能获取到 BaiduMap 的实例, 如下所示:
有了 BaiduMap 后, 我们就能对地图进行各种各样的操作了, 比如设置地图的缩放级别以及将地图移动到某一个经纬度上.
百度地图将缩放级别的取值范围限定在 3 到 19 之间, 其中小数点位的值也是可以取的, 值越大, 地图显示的信息就越精细. 比如我们想要将缩放级别设置成 12.5, 就可以这样写:
其中 MapStatusUpdateFactory 的 zoomTo()方法接收一个 float 型的参数, 就是用于设置缩放级别的, 这里我们传入 12.5f.zoomTo()方法返回一个 MapStatusUpdate 对象, 我们把这个对象传入 BaiduMap 的 animateMapStatus()方法当中即可完成缩放功能.
那么怎么样才能让地图移动到到某一个经纬度上呢? 这就需要借助 LatLng 类了. 其实 LatLng 并没有什么太多的用法, 主要就是用于存放经纬度值的, 它的构造方法接收两个参数, 第一个参数是纬度值, 第二个参数是经度值. 之后调用 MapStatusUpdateFactory 对象的 newLatLng()方法将 LatLng 对象传入, newLatLng()方法返回的也是一个 MapStatusUpdate 对象, 我们再把这个对象传入 BaiduMap 的 animateMapStatus()方法当中, 就可以将地图移动到指定的经纬度上了, 写法如下:
上述代码就实现了将地图移动到北纬 39.915 度, 东经 116.404 度这个位置的功能.
了解了这些知识之后, 接下来再去实现将地图快速移动到自己的位置的功能就变得非常简单了, 首先我们可以利用前面所学的定位技术来获取到自己当前位置的经纬度, 之后再按照上述的方法来将地图移动到指定的位置就可以了.
那么下面我们就来继续完善 LBSTest 项目, 加入 "移动到我的位置" 这个功能:
第一步: 修改 MainActivity 中的代码:
这里并没有新增多少代码, 主要是加入了一个 navigateTo()方法, 这个方法中的代码也很好理解, 先是将 BDLocation 对象中的地理位置信息取出并封装到 LatLng 对象中, 然后调用 MapStatusUpdateFactory 的 newLatLng()方法并将 LatLng 对象传入, 接着将返回的 MapStatusUpdate 对象作为参数传入到 BaiduMap 的 animateMapStatus()方法当中, 和上面介绍的用法是一模一样的. 并且这里为了让地图信息可以显示的更加丰富一些, 我们将缩放级别设置成了 16. 另外还有一点需要注意, 上述代码当中我们使用了一个 isFirstLocate 变量, 这个变量的作用就是为了防止多次调用 animateMapStatus()方法, 因为将地图移动到我们当前的位置只需要在程序第一次定位的时候调用一次就可以了.
写好了 navigateTo()方法后, 剩下的事情就简单了, 当定位到设备当前位置的时候, 我们在 onReceiveLocation()方法中直接把 BDLocation 对象传给 navigateTo()方法, 这样就能够让地图移动到设备所在的位置了.
第二步: 运行程序, 如下所示:
4.3, 让 "我" 显示在地图上
现在我们已经可以让地图显示我们周边的环境了, 但是在平时使用手机地图时应该会注意到, 通常情况下手机地图上应该都会有一个小光标, 用于显示设备当前所在的位置, 并且如果设备正在移动的话, 那么这个光标也会跟着一起移动, 那么我们现在就继续对现有代码进行扩展, 让 "我" 显示在地图上.
百度 LBS SDK 当中提供了一个 MyLocationData.Builder 类, 这个类是用来封装设备当前所在位置的, 我们只需要将经纬度信息传入到这个类的相应方法当中就可以了, 如下所示:
MyLocationData.Builder 还提供了一个 build()方法, 当我们把要封装的信息都设置完成后, 只需要调用它的 build()方法, 就会生成一个 MyLocationData 的实例, 然后再将这个实例传入到 BaiduMap 的 setMyLocationData()方法当中, 就可以让设备当前的位置显示在地图上了, 写法如下:
大体思路就是这样子, 下面我们开始来实现一下, 修改 MainActivity 中的代码, 如下所示:
可以看到, 在 navigateTo()方法中, 我们添加了 MyLocationData 的构建逻辑, 将 Location 中包含的经度和纬度分别封装到了 MyLocationData.Builder 当中, 最后把 MyLocationData 设置到了 BaiduMap 的 setMyLocationData()方法当中, 注意这段逻辑必须写在 isFirstLocate 这个 if 条件语句的外面, 因为让地图移动到我们当前的位置只需要在第一次定位的时候执行, 但是设备咋地图上显示的位置却应该是随着设备的移动而实时改变的.
另外, 根据百度地图的限制, 如果我们想要使用这一功能, 一定要事先调用 BaiduMap 的 setMyLocationEnabled()方法将此功能开启, 否则设备的位置将无法在地图上显示. 而在程序退出的时候, 也要记得将此功能给关闭掉.
接下来就运行程序, 用户就可以清晰的看出自己当前的位置在哪里了.
关于百度 LBS SDK 的用法就介绍这么多, 算是成功入门了. 如果想要更加深入的研究百度 LBS 的各种用法, 可以到官网上面参考开发指南, 地址: http://lbsyun.baidu.com/index.php?title=androidsdk/guide/create-project/androidstudio. 另外, 百度 LBS SDK 的版本未来随时都有可能更新, 所以根据官网的开发指南来进行学习也是非常重要的, 因为官方文档永远都是最新的.
来源: http://www.bubuko.com/infodetail-2930864.html