基于位置的服务简介
基于位置的服务简称LBS,主要的工作原理是利用无线电通讯网络或者GPS等定位方式来确定出移动设备所在的位置。
基于位置的服务的核心就是要先确定出用户所在的位置。通常有两种计数方式可以实现:一种是通过GPS定位,一种是通过网络定位。
GPS定位的工作原理是集于手机内置的GPS硬件直接和卫星交互来获取当前的经纬度信息,这种定位方式精确度非常高,但缺点是只能在室外使用,室内基本无法接收到卫星的信号。
网络定位的工作原理是根据手机当前网络附近的三个基站进行测速,以此计算出手机和每个基站基站的距离,再通过三角定位确定出一个大致的位置,这种定位方式精确度一般,但优点是在室内室外都可以使用。
Android对这两种定位方式都提供了响应的API支持,但Google的网络服务在中国不可访问,从而导致网络定位方式的API失效。而GPS定位虽然不需要网络。但必须要在室外才能使用,因此在室外开发的时候很有可能遇到不管使用哪种定位方式都无法成功定位的情况。所以使用一些国内的第三方公司的SDK。目前国内在这一领域做的比较好的一个是百度一个是高德。学习一下百度在LBS方面提供的丰富多彩的功能。
申请API Key
-
要想在自己的应用程序中使用百度的LBS功能,首先必须申请一个API Key。你需要拥有一个百度帐号才能进行申请。
-
有了百度帐号之后,我们就可以申请成为一名百度开发者了,登录百度帐号,并打开http://developer.baidu.com/user/reg
-
接着访问http://lbsyun.baidu.com/apiconsole/key 这个地址
-
接下来去申请API Key了
应用名称随便填,应用类型选择是Android SDK,启动服务保持默认即可,如图:
那么这个发布版SHA1和开发便SHA1又是什么呢?
- 这是我们申请API Key所必须填写的一个字段,它是指打包程序时所用签名文件的SHA1指纹,可以通过AS查看到。
打开AS中的任意一个项目,
如图:
这里展示了一个Android Stdio项目中所有内置的Gradle Tasks,其中signingReport这个Task就可以用来查看签名文件。双击signingReport,如图:
下面就是我们所需要的SHA1指纹
注意:目前我们使用的是debug.keystore文件所生成的指纹,这时Android自动生成的一个用于测试的签名文件。而当你的应用程序发布时还需要创建一个正式的签名文件,如果要得到它的指纹,可以在cmd中输入如下命令:
然后输入正确的密码就可以了。
那么也就是说,现在得到的这个SHA1指纹实际上是一个开发版的SHA1指纹,不过因为暂时还没有一个发布版的SHA1指纹,因此这两个值都填成一样的就可以了。
最后还剩一下个包名选项,虽然现在应用程序还不存在,但可以先将包名与定下来,比如就叫做com.example.lbstest,这样所有内容就填写完成了,如图:
接下来点提交,应用就应该创建成功了,如图:
使用百度定位
现在新建一个LBSTest项目,包名应该就会自动被命名为com.example.lbstest。另外需要注意,建议真机运行。
准备LBS SDK
在开始编码之前,还需要先将百度的LBS开放平台的SDK准备好
下载地址是:
http://lbsyun.baidu.com/sdk/download
会使用到基础地图和定位功能这两个SDK,勾选上,然后点击开发包下载即可。
下载完成后对该压缩包进行解压,其中会有一个libs目录,这里面的内容就是我们所需要的一切了。如图:
libs目录下的内容又分为两部分,BaiduLBS_Android.jar这个文件是Java层要使用到的,其他子目录下的so文件是Native层要用到的。so文件是用C/C++语言进行编写,然后再用NDK编译出来的。当然这里我们并不需要去编写C/C++的代码,因为百度都已经做好了封装,但是我们需要将libs目录下的每一个文件都放置到正确的位置。
首先观察一下当前的项目结构,会发现app模块下有一个libs目录,这里用来存放所有的jar包,将BaiduLBS_Android.jar复制到这里,如图:
接下来展开src/main目录,
再创建一个名为jniLibs的目录,这里就是专门用来存放so文件的,然后把压缩包里的其他所有目录直接复制到这里,如图:
另外,虽然所有新创建的项目中,app/build.gradle文件都会默认配置以下这段说明:
这表示会将libs目录下所有以.jar结尾的文件添加到当前项目的引用中。
但是由于我们是直接将Jar包复制到libs目录下的,并没有修改gradle文件,因此不会弹出我们平常熟悉的Sync Now提示。
这时候必须手动点击一下Android Stdio顶部工具栏中的Sync按钮,不然项目将无法引用到Jar包提供的任何接口。
确定自己的经纬度
首先修改activity_main.xml中的代码,如下:
一个TextView控件,用于稍后显示当前位置的经纬度信息。
然后修改AndroidManifest.xml文件中的代码,如下:
文件改动很多。
首先添加了很多权限声明,每一个声明都是百度LBS SDK内部要用到的。
然后在标签的内部添加了一个标签,这个标签的android:name部分是固定的,必须填com.baidu.lbsapi.API_KEY, android:value部分则应该填入我们之前获取的API Key。
最后还需要再注册一个LBS SDK中的服务,不用对这个服务的名字感到疑惑,因为百度LBS SDK中的代码都是混淆过的。(???)
接下来修改MainActivity中的代码,如下:
在onCreate方法中,
- 我们首先创建了一个LoactionClient的实例,LoactionClient的构建函数接受一个Context参数,这里调用getApplicationContenxt方法来获取一个全局的Context参数并传入。
- 然后调用LoactionClient的registerLocationListener方法来注册一个定位监听器,当我们获取位置信息时,就会回调这个定位监听器。
接下来看这里运行时权限的用法,用于我们在AndroidManifest.xml中声明了很多权限,有四个权限,但是有两个都属于同一个权限组。
一次性申请三个权限:
- 首先创建一个空的List集合,然后以此判断这三个权限有没有被授权,如果没有就添加到List集合中,最后将List转换成数组,再调用ActivityCompat.requestPermission方法一次性申请。
除此之外,
onRequestPermissionResult方法中对权限申请结果的逻辑处理也和之前有所不同,这次我们通过一个循环将申请的每个权限都进行了判断,如果有一个权限被拒绝,那么就直接调用finish方法关闭当前程序,只有当所有权限都被用户同意了,才回到i奥永requestLoaction方法开始地理位置定位。
requestLoaction方法中的代码比较简答, 只是调用了一个LocationClient的start方法方法就能开始定位了。定位的结果会回调到前面注册的监听器中,也就是MyLocationListener。
观察MyLocationListener的onReceiveLocation方法中,这里我们通过BDLoaction的getLatitude方法来获取当前位置的维度,通过getLongitude获取当前位置的经度,通过getLocType方法获取当前的定位方式,最终将结果组装成一个字符串,显示到TextView上面。
不过,在默认情况下,调用LocationClient的start方法只会定位一次,如果我们正在快速移动中,怎样才能
实时更新当前的位置
呢?
为此百度LBS SDK提供了一系列的设置方法,来允许我们更改默认的行为,修改MainActivity中的代码,如下:
- 这里增加了一个initLocation方法,在initLocation方法中我们创建了一个LocationClientOption对象,然后调用它的setScanSpan方法来设置更新的间隔。
这里传入了5000,表示每五秒钟会更新一下当前的位置。
- 最后记得在活动销毁的时候要调用LocationClient的stop方法来停止定位,不然应用程序会持续在后台不停的进行定位。
选择定位模式
刚刚使用的是网络定位。那么如何才能切换到精确度更高的GPS定位呢?
首先GPS定位功能必须要由用户主动去启用才行,不然任何应用程序都无法使用GPS获取到手机当前的位置信息。
高精度模式表示允许使用GPS、无线网络、蓝牙、或者移动网络来进行定位,节电模式表示仅允许无线网络、蓝牙、或者移动网络来进行定位,而仅限设备模式表示仅允许使用GPS进行定位。
也就是说如果我们想使用GPS,这里必须要选择第一或者第三。
只有定位操作真正开始的时候才会影响手机的电量。
你也可以强制指定只使用GPS定位, 修改MainActivity中的代码,如下:
这里setLocationMode方法来将定位模式指定成传感器模式,也就是说只能使用GPS进行定位。
看得懂的位置信息
虽然获取到了设备的经纬度信息,但一般人看不懂,为了更加直观的阅读,学习如何看得懂的位置信息。
百度LBS SDK在这方面提供了非常好的支持,只需要一些简单的接口调用就可以得到当前位置的各种丰富的地址信息。
修改MainActivity中的代码,如下:
首先在initLocation方法中,调用了LocationClientOption的setIsNeedAddress方法,并传入true,这就表示我们需要获取当前位置的详细地址信息。
接下来在MyLocationListener的onReceiveLocation方法就可以获取到各种丰富的地址信息了,调用getCountry方法可以得到当前所在国家,以此类推方法。。
由于获取地址信息一定需要用到网络,因此即使我们将定位模式指定成了Device_Sensors,也会自动开启网络定位功能。
使用百度地图
在自己的应用程序里加入地图功能。
让地图显示出来
准备好LBS SDK(其中包含了地图功能)
直接在LBSTest项目基础上进行,修改activity_main.xml中的代码,如下:
布局文件中新放置了一个MapView控件,并让他填充满整个屏幕。这个MapView是由百度提供的自定义控件,所以在使用它的时候需要将完整的包名加上。另外,之前用于显示定位信息的TextView现在暂时用不到了,我们将他的visibility属性指定成gone,让他在界面上隐藏起来。
接下来修改MainAcitivity中的代码,如下:
- 这里代码首先调用了SDKInitializer的initialize方法来进行初始化操作,initialize方法接受一个Context参数,这里调用getApplicationContext方法来获取一个全局的Context参数并传入。
- 注意初始化操作一定要在setContentView方法之前调用,不然会出错。
- 接下来调用findviewbyid获取到MapView的实例。
- 另外需要重写onResume、onPause和onDestroy这三个方法以便于对实例进行管理。
移动到我的位置
上面获取到的是一张默认的地图,显示的是北京市中心的位置。
百度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方法当中。就可以将地图移动到指定的经纬度上了。
如图:
了解了这些知识之后,接下来再去实现将地图快速移动到自己位置的功能就变得非常简单了。首先用定位技术先获取到自己当前的经纬度,然后再按照上述方法来将地图移动到指定位置即可。
完善,修改活动中代码:
新加入了一个navigateTo方法。
这个方法首先将BDLocation对象中的地理位置取出并封装到LatLng对象中,然后调用MapStatusUpdateFactory的newLatLng方法并将LatLng对象传入,接着将返回的MapStatusUpdate对象作为参数传入到Baidumap的animateMapStatus方法中。
上述代码使用了一个isFirstLocate变量,作用是为了防止多次调用animateMapStatus方法,因为将地图移到我们当前的位置只需要在程序第一次定位的时候调用一次就可以了。
让“我”显示在地图上
通常情况下手机地图上应该会有一个小光标,用于显示设备当前所在的位置,并且如果设备正在移动的话,那么这个光标也会跟着一起移动。
现在对现有代码进行扩展。
百度LBS SDK当中提供了一个MyLocationData.Bulider类,这个类是用来封装设备当前所在位置的,我们只需要将经纬度信息传入到这个类的相应方法当中就可以了,如下:
- MyLocationData.Bulider类还提供了一个build方法,当我们把要封装的信息都设置完成之后,只需要调用一下它的bulid方法,就会生成一个MyLocationData实例,然后再将这个实例传入到BaiduMap的setMyLocationData方法当中,就可以让折别的位置显示在地图上了。
写法如下:
现在修改活动中代码,如下:
- 在navigateTo方法中,添加了MyLocationData的构建逻辑,将Location中包含的经纬度分别封装到了MyLocationData.Builder当中,最后把MyLocationData设置到BaiduMap的setMyLocationData方法当中。
- 注意这段逻辑必须写在isFirstLocate这个if条件的外面,因为让地图移动到我们当前位置下只需要第一次定位的时候执行,但是设备在地图上显示的位置却应该是随着设备移动而实时改变的。
- 根据百度地图的限制,如果我们想要使用这一功能,一定要事先调用BaiduMap的setMyLocationEnabled方法将此功能开启,否则设备的位置将无法在地图上显示,而在程序退出的时候,也要记得将此功能关掉。