基于位置服务简称 LBS,主要工作原理通过无线电通讯网络或者 GPS 等定位方式来确定移动设备所在的位置,
GPS 定位是通过手机内置的 GPS 硬件和卫星交互获得当前经纬度信息,网络定位是根据手机当前网络附近的三个基站进行测速,以此计算出手机和每个基站之间的距离,在通过三角定位出一个大概的位置
打开网页:http://lbsyun.baidu.com/apiconsole/key
应用管理>>我的应用>>创建应用
输入应用名称,选择应用类型为:Android SDK,输入 SHA1
SHA1 是打包程序时所用的签名文件的 SHA1 指纹,通过 Android Studio 右侧工具栏的 Gradle>>项目名>>Tasks>>android,双击 signingReport 就可以查看签名文件信息,点击提交就创建了一个百度的应用
其中访问应用(AK)就是所需要的
准备sdk
选好服务后点击 开发包 下载即可
解压后有一个lib目录,该目录下又分为两部分,BaiduLBS_Android.jar 文件是 Java 层使用,其他文件夹下都是 .os 文件,是 Native 层用到的,将 BaiduLBS_Android.jar 文件放到项目名>>app>>libs
文件下,在 项目名>>app>>src>>main
文件夹下建立 jniLibs文件夹,并将其他的文件放到这个文件夹下
注意在 gradle 中声明 libs 文件夹下的 jar包,一般 app/build.gradle 文件都会默认配置以下这段声明
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
......
}
这表示会将 libs 目录下的所有以 jar 为后缀的文件添加到当前项目的引用中,由于没有修改该文件,所以没有弹出 Sync Now 的提示,需要手动同步,不然项目任然无法引用 jar包 中的任何接口
确定自己的位置
添加权限,这些都是百度 LBS SDK内部用到的权限
<!-- 这个权限用于进行网络定位-->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<!-- 这个权限用于访问GPS定位-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<!-- 用于访问wifi网络信息,wifi信息会用于进行网络定位-->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<!-- 获取运营商信息,用于支持提供运营商信息相关的接口-->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<!-- 这个权限用于获取wifi的获取权限,wifi信息会用来进行网络定位-->
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
<!-- 写入扩展存储,向扩展卡写入数据,用于写入离线定位数据-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<!-- 访问网络,网络定位需要上网 -->
<uses-permission android:name="android.permission.INTERNET"/>
<!-- 允许挂载和反挂载文件系统可移动存储 -->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<!-- 允许程序读取底层系统日志文件 -->
<uses-permission android:name="android.permission.READ_LOGS"/>
<!-- 允许访问振动设备 -->
<uses-permission android:name="android.permission.VIBRATE"/>
<!-- 允许使用PowerManager的 WakeLocks保持进程在休眠时从屏幕消失 -->
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<!-- 允许程序读取或写入系统设置 -->
<uses-permission android:name="android.permission.WRITE_SETTINGS"/>
<!-- android 9.0上使用前台服务,需要添加权限 -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<!-- 用于读取手机当前的状态 -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<!-- 读取缓存数据 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!-- 获取模拟定位信息 -->
<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"/>
除此之外还需要在<application>标签中注册服务,并添加一个<meta-data>,其中 android:name 部分是固定的,android:value 部分填入申请的 API Key
<!-- 声明service组件 -->
<service
android:name="com.baidu.location.f"
android:enabled="true"
android:process=":remote"/>
<!-- AK鉴权 -->
<meta-data
android:name="com.baidu.lbsapi.API_KEY"
android:value="请输入百度开放平台申请的Android端API KEY" /> <!-- http://lbsyun.baidu.com/apiconsole/key -->
public LocationClient locationClient;
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_show_location);
textView = (TextView)findViewById(R.id.position_textview);
getPermission();
}
@Override
protected void onDestroy() {
super.onDestroy();
locationClient.stop();
}
TextView 用于显示自己位置,getPermission() 方法用于申请必要的定位权限
private void getPermission(){
List<String> permissionList = new ArrayList<>();
if (ContextCompat.checkSelfPermission(ShowLocationActivity.this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED){
permissionList.add(Manifest.permission.ACCESS_FINE_LOCATION);
}
//READ_PHONE_STATE
if (ContextCompat.checkSelfPermission(ShowLocationActivity.this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED){
permissionList.add(Manifest.permission.ACCESS_COARSE_LOCATION);
}
if (ContextCompat.checkSelfPermission(ShowLocationActivity.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(ShowLocationActivity.this, permissions, 1);
}else {
requestLocation();
}
}
private void requestLocation(){
initLocationOption();
locationClient.start();
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode){
case 1:
if (grantResults.length > 0){
for (int result:grantResults){
if (result != PackageManager.PERMISSION_GRANTED){
Toast.makeText(ShowLocationActivity.this,"缺少必要运行权限", Toast.LENGTH_SHORT).show();
finish();
return;
}
}
requestLocation();
}else {
Toast.makeText(ShowLocationActivity.this,"发生为之错误", Toast.LENGTH_SHORT).show();
finish();
return;
}
break;
default:
break;
}
}
getPermission() 方法用于进行运行时权限的申请,由于 ACCESS_COARSE_LOCATION 和 ACCESS_FINE_LOCATION 都属于同一个权限组,因此两个只要申请其中一个就行了,这里将两个都进行了申请,但用户授权的时候只会受一次
首先创建了一个 List 集合用于存放需要申请的权限,最后如果有需要申请的权限,就调用requestPermissions() 方法一次申请,申请后会回掉 onRequestPermissionsResult() 方法,判断授权结果如果授权成功就调用 requestLocation() 方法,否则退出
private void requestLocation(){
initLocationOption();
locationClient.start();
}
requestLocation() 中 initLocationOption() 方法用于初始化百度sdk的定位参数配置,locationClient.start() 启动开始定位,定位的结果会回掉在 initLocationOption() 方法中注册的监听器中
private void initLocationOption(){
//定位服务的客户端。宿主程序在客户端声明此类,并调用,目前只支持在主线程中启动
locationClient = new LocationClient(getApplicationContext());
//声明LocationClient类实例并配置定位参数
LocationClientOption locationClientOption = new LocationClientOption();
//注册监听函数
locationClient.registerLocationListener(new MyLocationListener());
//可选,默认高精度,设置定位模式,高精度,低功耗,仅设备
locationClientOption.setLocationMode(LocationClientOption.LocationMode.Hight_Accuracy);
//可选,默认gcj02,设置返回的定位结果坐标系,如果配合百度地图使用,建议设置为bd09ll;
locationClientOption.setCoorType("gcj02");
//可选,默认0,即仅定位一次,设置发起连续定位请求的间隔需要大于等于1000ms才是有效的
locationClientOption.setScanSpan(0);
//可选,设置是否需要地址信息,默认不需要
locationClientOption.setIsNeedAddress(true);
//可选,设置是否需要地址描述
locationClientOption.setIsNeedLocationDescribe(true);
//可选,设置是否需要设备方向结果
locationClientOption.setNeedDeviceDirect(false);
//需将配置好的LocationClientOption对象,通过setLocOption方法传递给LocationClient对象使用
locationClient.setLocOption(locationClientOption);
}
在 initLocationOption() 方法中先创建了一个 LocationClient 的实例,LocationClient 的构建函数接受一个 Context 参数,这里调用 getApplicationContext() 获得一个全局的 Context 参数,然后调用 registerLocationListener() 方法来注册一个定位的监听器,当获取到位置信息的时候,就会回掉这个监听器,创建了一个 locationClientOption 对象,该对象可以配置定位的参数,最后调用 LocationClient 对象的 setLocOption() 方法将定位的参数设置进去
public class MyLocationListener extends BDAbstractLocationListener {
@Override
public void onReceiveLocation(BDLocation location) {
//此处的BDLocation为定位结果信息类,通过它的各种get方法可获取定位相关的全部结果
//以下只列举部分获取经纬度相关(常用)的结果信息
//更多结果信息获取说明,请参照类参考中BDLocation类中的说明
StringBuilder currentPosition = new StringBuilder();
//获取纬度信息
double latitude = location.getLatitude();
currentPosition.append("纬度:").append(latitude).append("\n");
//获取经度信息
double longitude = location.getLongitude();
currentPosition.append("经度:").append(longitude).append("\n");
//获取定位精度,默认值为0.0f
float radius = location.getRadius();
//获取经纬度坐标类型,以LocationClientOption中设置过的坐标类型为准
String coorType = location.getCoorType();
//获取定位类型、定位错误返回码,具体信息可参照类参考中BDLocation类中的说明
int errorCode = location.getLocType();
currentPosition.append("定位类型:");
if (errorCode == BDLocation.TypeGpsLocation){
currentPosition.append("GPS");
}else if (errorCode == BDLocation.TypeNetWorkLocation){
currentPosition.append("网络");
}
// 将定位信息设置到界面上
textView.setText(currentPosition);
}
}
MyLocationListener 继承于 BDAbstractLocationListener 类,要实现父类的 onReceiveLocation() 方法,当定位成功后,会回掉该方法,定位的结果保存在 location 中,然后就是获得相应的数据,组装成一个字符串,显示到 TextView 上面。
选择定位模式
GPS定位 的精确度更高,但 GPS 定位功能必须要用户主动去启用
一共有三种模式可选:Hight_Accuracy、Battery_Saving 和 Device_Sensors
- Hight_Accuracy:表示高精度确定模式,会在 GPS 信号正常下优先使用 GPS 定位,无法使用的时候使用网络
- Battery_Saving:表示省电模式,只会使用网络进行定位
- Device_Sensors表示传感器模式,只会使用 GPS 进行定位
使用 LocationClientOption 类的 setLocationMode() 方法进行设置
位置详细信息
使用 LocationClientOption 类的 setIsNeedAddress() 方法设置成 true 就可以拿到地址的详细信息,在 onReceiveLocation() 回掉方法中:
String country = location.getCountry();
currentPosition.append("国家:").append(country).append("\n");
String province = location.getProvince();
currentPosition.append("省:").append(province).append("\n");
String city = location.getCity();
currentPosition.append("市:").append(city).append("\n");
String district = location.getDistrict();
currentPosition.append("区:").append(district).append("\n");
String street = location.getStreet();
currentPosition.append("街道:").append(street).append("\n");
textView.setText(currentPosition);
使用百度地图
布局文件中放置一个 MapView 空间,这个是百度提供的自定义控件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_height="match_parent"
android:layout_width="match_parent">
<com.baidu.mapapi.map.MapView
android:id="@+id/position_bmapView"
android:layout_width="match_parent"
android:layout_height="1000px"
/>
<TextView
android:id="@+id/position_textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SDKInitializer.initialize(getApplicationContext());
setContentView(R.layout.activity_show_location);
textView = (TextView)findViewById(R.id.position_textview);
mapView = (MapView)findViewById(R.id.position_bmapView);
getPermission();
}
@Override
protected void onResume() {
super.onResume();
mapView.onResume();
}
@Override
protected void onPause() {
super.onPause();
mapView.onPause();
}
@Override
protected void onDestroy() {
super.onDestroy();
mapView.onDestroy();
}
首先调用 SDKInitializer 的 initialize() 方法来进行初始化,initialize() 方法接收一个 Context 参数,注意初始化操作一定要放在setContentView() 方法前调用,不然会报错,另外重写 onResume()、onPause()、onDestory() 这三个方法对 MapView 进行管理,以保证资源能够及时的释放
但现在显示的是地图默认的定位位置(北京市),还需要将上面获得到移动设备当前位置的经纬度设置到地图中显示
移动到我的位置
百度 LBS SDK 的 API 中提供了一个 BaiduMap 类,它是地图的总控制器,调用 MapView的 getMap() 方法就能获取到 BaiduMap 的实例
有了 BaiduMap 之后就能对地图进行各种操作,百度地图将缩放级别的取值范围限定在 3 到 19 之间,小数点位的值也是可以取的,值越大地图显示的信息越详细
private BaiduMap baiduMap;
private boolean isFirstLocate = true;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SDKInitializer.initialize(getApplicationContext());
setContentView(R.layout.activity_show_location);
textView = (TextView)findViewById(R.id.position_textview);
mapView = (MapView)findViewById(R.id.position_bmapView);
baiduMap = mapView.getMap();
getPermission();
}
isFirstLocate 用于防止多次调用 animateMapStatus() 定位到自己的位置,由于设置了 locationClientOption.setScanSpan(1000) 连续定位请求的间隔为1秒,否则会每隔 1 就定位自己的位置
private void navigateTo(double latitude, double longitude){
if(isFirstLocate){
LatLng latLng = new LatLng(latitude, longitude);
MapStatusUpdate update = MapStatusUpdateFactory.newLatLng(latLng);
baiduMap.animateMapStatus(update);
update = MapStatusUpdateFactory.zoomBy(14f);
baiduMap.animateMapStatus(update);
isFirstLocate = false;
}
}
加入了一个 navigateTo() 方法用于定位当前位置
LatLng 类的构造方法接收两个参数,一个是维度值,一个是经度值,之后调用 MapStatusUpdateFactory 的 newLatLng() 方法将 LatLng 对象传入,newLatLng() 返回一个MapStatusUpdate 对象,将这个对象传入 BaiduMap 的 animateMapStatus() 方法就可以将地图移动到指定位置的功能
MapStatusUpdateFactory 的 zoomTo() 方法接收一个 float 参数,用于设置缩放的级别,zoomTo() 返回一个 MapStatusUpdate 对象,将这个对象传入 BaiduMap 的 animateMapStatus() 方法就可以完成缩放功能
在 onReceiveLocation() 方法中将维度经度传入 navigateTo() 方法,这样就能让地图移动到设备所在的位置了
这里注意要将获取经纬度的坐标系转换成 bd09ll 格式,百度使用的是这个格式,不然定位会有偏差,使用 LocationClientOption 的 locationClientOption.setCoorType(“bd09ll”) 方法设置就行
让我显示在地图上
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SDKInitializer.initialize(getApplicationContext());
setContentView(R.layout.activity_show_location);
textView = (TextView)findViewById(R.id.position_textview);
mapView = (MapView)findViewById(R.id.position_bmapView);
baiduMap = mapView.getMap();
baiduMap.setMyLocationEnabled(true);
getPermission();
}
@Override
protected void onDestroy() {
super.onDestroy();
locationClient.stop();
mapView.onDestroy();
baiduMap.setMyLocationEnabled(false);
}
private void navigateTo(double latitude, double longitude){
if(isFirstLocate){
......
}
MyLocationData.Builder builder = new MyLocationData.Builder();
builder.latitude(latitude);
builder.longitude(longitude);
baiduMap.setMyLocationData(builder.build());
}
百度 LBS SDK 中提供了一个 MyLocationData.Builder() 类,这个类用来封装设备当前所在位置,只需要将经纬度信息传入这个方法当中就行,将封装的信息设置完成后,调用 build() 方法就会生成一个 MyLocationData 的实例,然后再将这个实例传入 BaiduMap 的 setMyLocationData() 方法中就可以让设备当前的位置显示到地图上了
注意如果想要使用这一功能,一定要先调用 BaiduMap 的 setMyLocationEnabled() 方法将此功能开启,在程序退出的时候也要将此功能关闭释放资源