1. 基于位置的服务
基于位置的服务(Location Based Service, LBS)广泛运用于移动设备,成为移动互联网应用区别于普通互联网应用的重要特色之一。
定位技术是LBS的基础技术,用以获取设备当前所在的地理位置(通常以经纬度来表示)。
在定位技术的基础上,提供LBS服务的厂商还需要采集和提供巨量的服务数据,如地理信息、社会服务位置等等。用户通过定位,即可从服务提供商那里
2. 地理信息开放平台
许多厂商提供地图、位置等相关的开放平台编程接口。通过申请这些开放平台的SDK嵌入到应用项目中,普通开发者也可以设计、实现自己的LBS应用。
3. 申请地图开放平台服务
申请开放平台服务我们并不陌生。在前面的课程里我们已经通过“和风天气”开放平台的服务为iWeather应用获取到了天气数据。地图开放平台服务的申请大同小异,首先注册账号,然后注册需要定位服务的App,最后获取APP Key和SDK(开发包)。
以高德地图开放平台为例(https://lbs.amap.com),注册完毕后信息如下:
从图中可以看到,iWeather应用已经注册成功,App Key如下:
c81a94b41cc152bac3a8ae23a9413717
同时,我们可以从高德平台提供的文档中找到定位功能相关开发包的导入方法:
表中最后一项即为定位功能对应的开发包,只是根据Android Studio最新版本的要求,格式上略有不同,写作:
implementation 'com.amap.api:location:latest.integration'
3. 定位服务开发包导入
在开始编写程序,需要对我们的项目做一些准备工作:
- 嵌入AppKey:没有这个Key,高德将不会发送定位数据
- 导入开发包
- 声明所需要的权限
- 导入高德定位服务组件
com.amap.api.location.APSService
3.1 嵌入App Key
进入AndroidManifest.xml
文件,在<application> ... </application>范围内部增加一条表示Key的<meta-data>标签如下:
<meta-data android:name="com.amap.api.v2.apikey"
android:value="c81a94b41cc152bac3a8ae23a9413717"/>
注意这个"amap",它是高德地图的英文名字。
3.2 导入开发包
进入app/build.gradle
,找到我们之前导入其它模块的dependencies { ... }
区域,在其中添加导入语句:
implementation 'com.amap.api:location:latest.integration'
3.3 声明权限
用户设备所在位置、定位的精确程度等等都需要用户赋予权限,否则系统将禁止使用。在AndroidManifest.xml
中已有的权限声明位置增加新的权限声明:
<!--用于进行网络定位-->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"></uses-permission>
<!--用于访问GPS定位-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"></uses-permission>
<!--用于获取运营商信息,用于支持提供运营商信息相关的接口-->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission>
<!--用于访问wifi网络信息,wifi信息会用于进行网络定位-->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"></uses-permission>
<!--用于获取wifi的获取权限,wifi信息会用来进行网络定位-->
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"></uses-permission>
<!--用于访问网络,网络定位需要上网-->
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
<!--用于读取手机当前的状态-->
<uses-permission android:name="android.permission.READ_PHONE_STATE"></uses-permission>
<!--用于写入缓存数据到扩展存储卡-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
<!--用于申请调用A-GPS模块-->
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS"></uses-permission>
<!--用于申请获取蓝牙信息进行室内定位-->
<uses-permission android:name="android.permission.BLUETOOTH"></uses-permission>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"></uses-permission>
运行一下程序查看效果。目前只是对程序做出了一定的配置,没有编写实际的代码,因此这里运行只是保证前期工作没有引入错误。
3.4 导入定位服务组件
高德的定位服务通过一个Android Service组件来完成发起定位、位置获取以及结束定位等工作。同Activity类似,Service组件也需要在AndroidManifest.xml
文件中声明之后才能使用。
进入AndroidManifest.xml
文件,在<application> ... </application>
范围内部增加对com.amap.api.location.APSService
的声明:
<service android:name="com.amap.api.location.APSService" />
4. 实现位置的获取
现在编写代码,调用高德的定位接口来获取当前设备所在的经纬度。
定位程序编写一般都遵循如下的步骤:
- 启动定位服务:通过
AMapLocationClient
对象配置定位参数,然后启动定位。定位服务将按照设定的时间间隔持续的报告当前位置。 - 监听位置变化:通过定义
AMapLocationListener
监听器对象并注册到client
。每当定位服务报告位置信息,监听器即被通知到。通过编写监听器代码,就可使实现对定位数据的应用了。
4.1 定义位置客户端和监听器对象
首先在MainActivity类中定义一个AMapLocationClient
类型的成员mLocationClient
:
然后定义位置监听器成员mLocationListener
:
4.2 启动定位
启动定位服务代码较多,因此这里专门为这个过程定义一个名为initLocation()
的私有方法并在onCreate()
末尾调用它:
然后为initLocation()
方法编写代码。
-
第一步:初始化
mLocationClient
成员并设置监听器:
第二步:设置定位参数
开发者可以对定位服务的一些选项进行定制。具体的是创建一个AMapLocationClientOption
类的对象,然后为它设置若干参数,最后将其关联到定位客户端对象mLocationClient
。
AMapLocationClientOption
的意思就是“高德定位客户端选项”
我们根据需求做如下设置:
a. 选择使用高精度定位。
b. 每10秒钟报告一次位置
c. 允许模拟定位(模拟器调试无法真正的获取定位数据)
具体操作如下:
- 启动定位服务
最终,通过定位客户端对象mLocationClient
启动定位。
这里有个技巧性的做法,在启动定位之前先强制停止一次,以防意外。
4.3 监听定位数据
前面设置了每10秒报告一次定位数据。我们将来要通过这个定位数据去获取对应位置的天气。我们先暂时简单的通过日志来展示得到的定位数据。
在监听器对象mLocationListener
的onLocationChanged()
方法中编写代码。该方法的参数aMapLocation
即封装了每次报告的定位数据:
- 读取经度和纬度:
-
输出日志:
运行程序,并在屏幕下方视图中切换至“Logcat”标签页,设置筛选标记为“iWeather”的日志如图:
可以看到获取到的经纬度都是0,说明定位失败。我们还需要配置一下运行环境。
4.4 配置运行环境
由于虚拟设备本身不具备真正的定位部件,所以我们需要设置模拟定位,使高德定位服务能够获取到一个我们指定的位置。另外,我们要在手机模拟器上设置应用程序权限,使系统允许iWeather获取定位。
- 开启定位权限
退回手机桌面,在App列表中选择“Settings”,然后依次选择“Apps & notifications” -> “爱天气” -> “Permissions”,将看到如下的App权限设置页面:
将其中的几个选项全部打开:
- 设置模拟定位
点击模拟器面板最下方的“...”按钮:
进入设置对话框,选择左侧选项列表第一项,在右侧出现的定位设置面板中,将“Latitude”(纬度)和“Longitude”(经度)两项设置成如下数值(北京后海附近):
Latitude: 39.93497349279733
Longitude: 116.3912593266754
如图:
点击“Send”按钮,关闭对话框。
退出并再次运行iWeather,查看日志。如果不出意外,应当可以在日志中看到刚才设置的经纬度了:
2019-06-04 14:52:28.130 7612-7612/com.dmtech.iw D/iWeather: 当前位置:0.0, 0.0
2019-06-04 14:52:34.268 7612-7612/com.dmtech.iw D/iWeather: 当前位置:116.39750217013889, 39.936375054253475
2019-06-04 14:52:34.392 7612-7612/com.dmtech.iw D/iWeather: 当前位置:116.39750217013889, 39.936375054253475
2019-06-04 14:52:36.199 7612-9496/com.dmtech.iw D/iWeather: Request: https://free-api.heweather.net/s6/weather?location=CN101010800&key=d2ae781d61744d65a2ef2156eef2cb64
2019-06-04 14:52:36.250 7612-7612/com.dmtech.iw D/iWeather: 当前位置:116.39750217013889, 39.936375054253475
2019-06-04 14:52:36.421 7612-9496/com.dmtech.iw D/iWeather: Request: https://free-api.heweather.net/s6/weather?location=CN101131012&key=d2ae781d61744d65a2ef2156eef2cb64
2019-06-04 14:52:36.538 7612-9496/com.dmtech.iw D/iWeather: Request: https://free-api.heweather.net/s6/weather?location=CN101310304&key=d2ae781d61744d65a2ef2156eef2cb64
2019-06-04 14:52:36.661 7612-9496/com.dmtech.iw D/iWeather: Request: https://free-api.heweather.net/s6/weather?location=US3290097&key=d2ae781d61744d65a2ef2156eef2cb64
2019-06-04 14:52:36.774 7612-9496/com.dmtech.iw D/iWeather: Request: https://free-api.heweather.net/s6/weather?location=AU2147714&key=d2ae781d61744d65a2ef2156eef2cb64
4.5 增加实时位置的天气
只要把定位服务提供的位置信息加入到我们的天气队列中,即可从天气服务平台获取该位置的天气。
- 在位置列表数组
LOCATION_IDS
最前面添加一个位置,令其固定的表示实时定位的到的位置。默认设置为下图的值(天安门):
- 根据实时位置改写位置列表第一项
修改位置监听器对象mLocationListener
的代码,用当前得到的经纬度值拼接成新的字符串,替代位置列表数组LOCATION_IDS
的第一项。然后,重新执行获取天气数据的异步任务:
4.6 使用更具体的地址信息
我们在获取天气数据的同时能够得到对应的地址信息并显示在主页面的标题栏。但是天气平台提供的地址(区级)不如高德地图给出的定位具体(街道)。我们来尝试在显示实时位置天气页面时用后者替代前者,即当显示天气数据中第0个的时候,采用高德定位得到的地址信息。
首先增加一个AMapLocation
类的成员mLocation
:
修改位置监听器对象mLocationListener
的代码,将mLocation
设置为当前报告的位置:
找到实现设置标题栏地址信息的函数setTitleAddress()
:
对绿线标出的这一行代码进行改写,使其进行判断:
- 如果当前页面顺序为0,则使用定位得到的地址
- 否则使用天气数据中的地址
代码如下:
运行程序,等待一段时间,即可看到如下的效果: