Android开发基础11-基于位置的服务

本篇文章主要介绍以下几个知识点:

  • 百度定位。
  • 百度地图。

11.1 基于位置的服务简介

  基于位置的服务简称 LBS(Location Based Service),主要的工作原理是利用无线电通讯网络或 GPS 等定位方式来确定出移动设备所在的位置。

  LBS 所围绕的核心就是要确定出用户所在的位置。通常有两种技术:

  • GPS 定位:
     基于手机内置的 GPS 硬件直接和卫星交互来获取当前的经纬度信息,精确度高,但只能室外使用,室内基本无法接收到卫星的信号。

  • 网络定位:
     根据手机当前网络附近的三个基站进行测速,以此计算出手机和每个基站之间的距离,再通过三角定位确定一个大概位置,精确度一般,但室内外均可使用。

  本章主要学习百度在 LBS 方面提供的一些功能。

11.2 使用百度定位

  要想在自己的应用程序里使用百度的 LBS 功能,首先必须申请一个 API Key,有了 API Key 就可以进行后续的 LBS 开发工作了。

11.2.1 准备 LBS SDK

  在编码之前,先将百度 LBS 开放平台的 SDK 准备好,下载地址:http://lbsyun.baidu.com/sdk/download

  本章会用到基础地图和基础定位这两个 SDK,下载完后对该压缩包解压,libs 目录里就有我们所需要的一切了:

  下面把 libs 目录里的内容拷贝到我们的项目中:
 (1)把 BaiduLBS_Android.jar 拷贝到项目 app 模块中的 libs 目录:

 (2)展开 src/main 目录,右击该目录→New→Directory,创建一个名为 jniLibs 的目录,用来存放 so 包,然后把压缩包里的其他所以目录直接复制到这里:

  另外,记得点击顶部工具栏中的 Sync 按钮(下图中最左边的按钮)将 BaiduLBS_Android.jar 添加到当前项目的引用中。

  以上就把 LBS 的 SDK 都准备好了。

11.2.2 确定自己位置的经纬度

  首先在 AndroidManifest 中添加开发密钥、所需权限等信息:
 (1)在 application 中添加开发密钥

1
2
3
4
5
<application>  
<meta-data
android:name="com.baidu.lbsapi.API_KEY"
android:value="开发者 key" />
</application>

 (2)添加所需权限

1
2
3
4
5
6
7
8
9
10
11
<!-- 百度 LBS 相关权限 -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_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.WAKE_LOCK" />

 (3)再注册一个百度 LBS SDK 中的服务

1
2
3
4
5
<service
android:name="com.baidu.location.f"
android:enabled="true"
android:process=":remote">
</service>

  接下来在布局添加个 TextView 来显示当前位置的经纬度:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
public class LocationActivity extends AppCompatActivity {

private LocationClient mLocationClient;

private TextView tv_show_location;// 显示当前位置信息

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 构建 LocationClient 实例
mLocationClient = new LocationClient(getApplicationContext());
// 注册一个定位监听器
mLocationClient.registerLocationListener(new MyLocationListener());
setContentView(R.layout.activity_location);

tv_show_location = (TextView) findViewById(R.id.tv_show_location);

// 声明权限,将权限添加到list集合中再一次性申请
List<String> permissionList = new ArrayList<>();
if (ActivityCompat.checkSelfPermission(LocationActivity.this,
Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
permissionList.add(Manifest.permission.ACCESS_FINE_LOCATION);
}
if (ActivityCompat.checkSelfPermission(LocationActivity.this,
Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
permissionList.add(Manifest.permission.READ_PHONE_STATE);
}
if (ActivityCompat.checkSelfPermission(LocationActivity.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(LocationActivity.this,permissions,1);
}else {
requestLocation();
}
}

/**
* 开始地理位置定位
*/
private void requestLocation() {
mLocationClient.start();
}


@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
switch (requestCode){
case 1:
if (grantResults.length > 0 ){
for (int result : grantResults){
if (result != PackageManager.PERMISSION_GRANTED){
ToastUtils.showShort("必须同意所有权限才能使用本程序");
finish();
return;
}
}
requestLocation();
}else {
ToastUtils.showShort("发生未知错误");
finish();
}
break;

default:
break;
}
}

// 监听器
public class MyLocationListener implements BDLocationListener{

@Override
public void onReceiveLocation(BDLocation bdLocation) {
StringBuilder currentLocation = new StringBuilder();
currentLocation.append("纬度:").append(bdLocation.getLatitude()).append("\n");
currentLocation.append("经线:").append(bdLocation.getAltitude()).append("\n");
currentLocation.append("定位方式:");
if (bdLocation.getLocType() == BDLocation.TypeGpsLocation){
currentLocation.append("GPS");
} else if (bdLocation.getLocType() == BDLocation.TypeNetWorkLocation){
currentLocation.append("网络");
}
tv_show_location.setText(currentLocation);
}
}
}

  运行程序,效果如下:

  在默认情况下,调用 LocationClient 的 start() 的方法只会定位一次,若要实时更新当前的位置,还需添加如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class LocationActivity extends AppCompatActivity {

. . .

/**
* 开始地理位置定位
*/
private void requestLocation() {
initLocation();
mLocationClient.start();
}

private void initLocation() {
// 创建LocationClientOption 对象
LocationClientOption option = new LocationClientOption();
option.setScanSpan(5000); //5秒钟更新下当前位置
mLocationClient.setLocOption(option);
}

@Override
protected void onDestroy() {
super.onDestroy();
mLocationClient.stop();//停止定位
}
}

  这样界面上的经纬度信息就会跟着位置变化一起变化。

11.2.3 选择定位模式

  上一小节是使用网络定位的,那要如何使用 GPS 定位呢?

  GPS 定位功能必须由用户主动去启用才行,开启后可以在 initLocation() 方法中对百度 LBS SDK 的定位模式进行指定,共有3种模式:

  • Hight_Accuracy
     高精度模式(默认模式),会在GPS信号正常的情况下优先使用GPS定位,在无法接收GPS信号时用网络定位。

  • Battery_Saving
     节电模式,只会使用网络定位。

  • Device_Sensors
     传感器模式,只会使用GPS定位。

  当然,也可以调用 setLocationMode() 方法来强制指定只使用GPS定位,如下:

1
option.setLocationMode(LocationClientOption.LocationMode.Device_Sensors);

  重新运行程序,效果如下:

11.2.4 看得懂的位置信息

  经纬度一般人是看不懂,为了更加直观点,还需要进行一些简单的接口调用,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class LocationActivity extends AppCompatActivity {

. . .
private void initLocation() {
LocationClientOption option = new LocationClientOption();
option.setScanSpan(5000);
//需要获取当前位置的详细信息
option.setIsNeedAddress(true);
mLocationClient.setLocOption(option);
}

// 监听器
public class MyLocationListener implements BDLocationListener{

@Override
public void onReceiveLocation(BDLocation bdLocation) {
StringBuilder currentLocation = new StringBuilder();
currentLocation.append("纬度:").append(bdLocation.getLatitude()).append("\n");
currentLocation.append("经线:").append(bdLocation.getAltitude()).append("\n");
currentLocation.append("国家:").append(bdLocation.getCountry()).append("\n");
currentLocation.append("省:").append(bdLocation.getProvince()).append("\n");
currentLocation.append("市:").append(bdLocation.getCity()).append("\n");
currentLocation.append("区:").append(bdLocation.getDistrict()).append("\n");
currentLocation.append("街道:").append(bdLocation.getStreet()).append("\n");
currentLocation.append("定位方式:");
if (bdLocation.getLocType() == BDLocation.TypeGpsLocation){
currentLocation.append("GPS");
} else if (bdLocation.getLocType() == BDLocation.TypeNetWorkLocation){
currentLocation.append("网络");
}
tv_show_location.setText(currentLocation);
}
}
}

  重新运行程序,效果如下:

11.3 使用百度地图

11.3.1 让地图显示出来

  要让地图显示出来,需要用到百度提供的自定义控件 MapView,在布局中添加如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_map"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.wonderful.myfirstcode.chapter11.MapActivity">

<!-- 显示地图控件 -->
<com.baidu.mapapi.map.MapView
android:id="@+id/map_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true"/>

</RelativeLayout>

  接下来,编写活动中的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class MapActivity extends AppCompatActivity {

private MapView mapView;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 初始化操作,在 setContentView() 方法前调用
SDKInitializer.initialize(getApplicationContext());
setContentView(R.layout.activity_map);

mapView = (MapView) findViewById(R.id.map_view);
}

@Override
protected void onResume() {
super.onResume();
mapView.onResume();
}

@Override
protected void onPause() {
super.onPause();
mapView.onPause();
}

@Override
protected void onDestroy() {
super.onDestroy();
mapView.onDestroy();
}
}

  上述代码,需要重写 onResume()、onPause()、onDestroy() 这3个方法,保证资源能够及时释放。

  运行效果如下:

11.3.4 移动到我的位置

  百度 LBS SDK 的 API 中提供了一个 BaiduMap 类,是地图的总控制器,有了它就能对地图进行各种各样的操作了。获取其实例如下:

1
BaiduMap baiduMap = mapView.getMap();

  百度地图将缩放级别的取值范围限定在3到19之间,也可取小数点位,值越大地图显示信息越精细,如把缩放级别设置成12.5,可以这样写:

1
2
MapStatusUpdate update = MapStatusUpdateFactory.zoomTo(12.5f);
baiduMap.animateMapStatus(update);

  若要让地图移动到某个经纬度上,可以借助 LatLng 类,如将地图移动到北纬39.915°、东经116.404°,可以这样写:

1
2
3
LatLng ll = new LatLng(39.915,116.404);
MapStatusUpdate update = MapStatusUpdateFactory.newLatLng(ll);
baiduMap.animateMapStatus(update);

  接下来实现下 “移动到我的位置” 这个功能,修改活动中代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
public class MapActivity extends AppCompatActivity {

private LocationClient mLocationClient;

private MapView mapView;

private BaiduMap baiduMap;

// 避免多次调用animateMapStatus() 方法
private boolean isFirstLocate = true;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mLocationClient = new LocationClient(getApplicationContext());
mLocationClient.registerLocationListener(new MyLocationListener());
SDKInitializer.initialize(getApplicationContext());
setContentView(R.layout.activity_map);

mapView = (MapView) findViewById(R.id.map_view);
baiduMap = mapView.getMap();
. . .
}

/**
* 把地图移动到当前位置
* @param location
*/
private void navigateTo(BDLocation location){
if (isFirstLocate){
LatLng ll = new LatLng(location.getLatitude(),location.getLongitude());
MapStatusUpdate update = MapStatusUpdateFactory.newLatLng(ll);
baiduMap.animateMapStatus(update);
update = MapStatusUpdateFactory.zoomTo(16f);
baiduMap.animateMapStatus(update);
isFirstLocate = false;
}
}

// 监听器
public class MyLocationListener implements BDLocationListener{

@Override
public void onReceiveLocation(BDLocation bdLocation) {
if (bdLocation.getLocType() == BDLocation.TypeGpsLocation ||
bdLocation.getLocType() == BDLocation.TypeNetWorkLocation){
navigateTo(bdLocation);
}
}
}

. . .
}

11.3.3 让“我”显示在地图上

  百度 LBS SDK 当中提供了一个 MyLocationData.Builder 类,这个类是用来封装设备当前所在位置的,只需将经纬度信息传入到它相应的方法就可以,如下:

1
2
3
MyLocationData.Builder locationBuilder = new MyLocationData.Builder();
locationBuilder.latitude(39.915);
locationBuilder.longitude(116.404);

  设置完要封装的信息后调用 MyLocationData.Builder 类中的 build() 方法,就会生成一个 MyLocationData 实例,把这个实例传入到 BaiduMap 的 setMyLocationData() 方法中,就可以让设备当前位置显示在地图上了,如下:

1
2
MyLocationData locationData = locationBuilder.build();
baiduMap.setMyLocationData(locationData);

  下面贴上完整的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
public class MapActivity extends AppCompatActivity {

private LocationClient mLocationClient;

private MapView mapView;

private BaiduMap baiduMap;

private boolean isFirstLocate = true;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mLocationClient = new LocationClient(getApplicationContext());
mLocationClient.registerLocationListener(new MyLocationListener());
SDKInitializer.initialize(getApplicationContext());
setContentView(R.layout.activity_map);

mapView = (MapView) findViewById(R.id.map_view);
baiduMap = mapView.getMap();
baiduMap.setMyLocationEnabled(true);

// 声明权限,将权限添加到list集合中再一次性申请
List<String> permissionList = new ArrayList<>();
if (ActivityCompat.checkSelfPermission(MapActivity.this,
Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
permissionList.add(Manifest.permission.ACCESS_FINE_LOCATION);
}
if (ActivityCompat.checkSelfPermission(MapActivity.this,
Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
permissionList.add(Manifest.permission.READ_PHONE_STATE);
}
if (ActivityCompat.checkSelfPermission(MapActivity.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(MapActivity.this,permissions,1);
}else {
requestLocation();
}
}

/**
* 开始地理位置定位
*/
private void requestLocation() {
initLocation();
mLocationClient.start();
}

private void initLocation() {
LocationClientOption option = new LocationClientOption();
option.setScanSpan(5000); //5秒钟更新下当前位置
option.setIsNeedAddress(true);
mLocationClient.setLocOption(option);
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
switch (requestCode){
case 1:
if (grantResults.length > 0 ){
for (int result : grantResults){
if (result != PackageManager.PERMISSION_GRANTED){
ToastUtils.showShort("必须同意所有权限才能使用本程序");
finish();
return;
}
}
requestLocation();
}else {
ToastUtils.showShort("发生未知错误");
finish();
}
break;

default:
break;
}
}

/**
* 把地图移动到当前位置
* @param location
*/
private void navigateTo(BDLocation location){
if (isFirstLocate){
LatLng ll = new LatLng(location.getLatitude(),location.getLongitude());
MapStatusUpdate update = MapStatusUpdateFactory.newLatLng(ll);
baiduMap.animateMapStatus(update);
update = MapStatusUpdateFactory.zoomTo(16f);
baiduMap.animateMapStatus(update);
isFirstLocate = false;
}

MyLocationData.Builder locationBuilder = new MyLocationData.Builder();
locationBuilder.latitude(location.getLatitude());
locationBuilder.longitude(location.getLongitude());
MyLocationData locationData = locationBuilder.build();
baiduMap.setMyLocationData(locationData);
}

// 监听器
public class MyLocationListener implements BDLocationListener{

@Override
public void onReceiveLocation(BDLocation bdLocation) {
if (bdLocation.getLocType() == BDLocation.TypeGpsLocation ||
bdLocation.getLocType() == BDLocation.TypeNetWorkLocation){
navigateTo(bdLocation);
}
}
}

@Override
protected void onResume() {
super.onResume();
mapView.onResume();
}

@Override
protected void onPause() {
super.onPause();
mapView.onPause();
}

@Override
protected void onDestroy() {
super.onDestroy();
mLocationClient.stop();
mapView.onDestroy();
baiduMap.setMyLocationEnabled(false);
}
}

  关于百度 LBS SDK 的用法入门就介绍到这,更多用法参考官方网站:http://lbsyun.baidu.com/ 。建议根据官网开发指南来进行学习。

Jason Xu wechat
欢迎您扫一扫上面的微信公众号,订阅我的博客!
坚持原创技术分享,您的支持将鼓励我继续创作!