利用百度地图在后台服务中定位

关于 LBS 带来的好处就不多说了,我们大家都从中享受了很大的便利,现在基于 LBS 的应用也层出不穷。我也是因为业务需求才接触到百度地图 SDK 的接入,尝试了下在前台 Activity 中获取定位按照 API 文档可以很轻松的实现,但是如果需要再后台进行定位就没那么简单了。尝试了很过次,参考了网络上的很多文章,最后终于找到了可行的方案。

注册百度开放平台开发者

要使用百度地图就必须成为百度开放平台的用户,关于怎么注册就不细说了,因为实在太简单了。注册地址是 http://developer.baidu.com/user/reg .

下载百度地图 SDK

因为我这里只需要用到基础定位功能,便仅下载了包含基础定位的 SDK ,这样占用的存储空间也最小。下载完成后解压,得到一个 jar 文件和 5 个文件夹。将 AS 文件目录切到 Project 模式下,将 jar 文件拷贝到 app/libs 路径下,邮件点击拷贝过来的 jar 文件,选择 Add as lib 。这个时候 AS 会自动开始 进行 Gradle 同步。然后在 src/main/ 路径下新建文件夹 jniLibs 将压缩包中剩余的所有文件夹拷贝到新建好的 jniLibs 文件夹中。

在开发者中心创建应用

http://lbsyun.baidu.com/apiconsole/key/create 中创建自己的应用,应用类型选择 Android SDK,启用服务默认勾选所有,其中发布版 SHA1 可以在 CMD 中切换到 C:/Users/username/.android/ 路径下执行 keytool -list -v -keystore debug.keystore 来得到,这也是获取默认的 debug 签名的 SHA1 认证码的方式。如果存在其他的 debug keystore 文件,只要将最后的文件改成自己的路径+文件即可。然后包名就是 Android APP 的包名。点击提交成功后会得到这个应用的 API_KEY。

在设置包名的时候要注意一点,有时候实际的包名与你在 AS 中创建的 Project 的包名不一致,这可能会导致百度的 AK 认证失败,调试的时候错误显示 NetWork location failed because baidu location service check the key is unlegal, please check the key in AndroidManifest.xml !。比如我在 AS 中创建的应用的包名是 com.alphagao.coolerweather ,但实际上的包名是 com.alphagao.coolerweather.android.qihoo ,开始因为这个问题导致多次测试失败。查看实际的包名可以通过在 AS 中的 Android Monitor 窗口获得,如图:

在 Manifest 文件中设置 API_KEY

Application 标记下添加 meta-data

1
2
3
<meta-data
android:name="com.baidu.lbsapi.API_KEY"
android:value="6nImDiRH8Fmv4RnrgLkxjqpimXTUmirP" />

其中 name 是固定的写法名称,而 value 就是上一步中获得 AK 码。

由于 SDK 在使用的时候有自己依赖的远程服务,所以还需要添加以下内容:

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

这些内容也都是固定写法,可以看到 service 的 name 是已经经过混淆了的。

当然不要忘记为 APP 添加相应的权限,以下都是百度地图 SDK 需要使用的权限:

1
2
3
4
5
6
7
8
9
10
<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"/>

以上权限中包含一些敏感权限,在 Android 6.0 (23) 及以上需要做权限请求方面的适配,由于我这里是 5.0 的系统,为了简便,就省略了权限请求相关的内容,如果是你,可别忘记了。

到这里准备工作基本就完成了,下面就是正式的代码部分了。

后台服务

根据查询,百度地图的定位必须在主线程中才能够正常运行。而 service 也是属于主线程,所以按理来说是完全可行的。

按照在 Activity 中的流程,首先必须初始化定位客户端,设置定位结果监听接口,设置定位选项并开启定位,然后在监听接口中就可以获取定位结果了。

至于查询出的结果如何与前台进行交互,可以有多重方式,比如 service 查询到以后就把结果存在 SharedPreferences 或者数据库中,也可以通过 handler 将消息发送到前台进程,还可以通过 Activity 与 Service 绑定进行数据交互等。这里我就用 Handler 来发送消息了。

MyService.java

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
public class MyService extends Service {
private static final String TAG = "MyService";
//查询到的城市信息
private String city = "";
//设置为静态成员方便在 Activity 中引用
public static Handler handler;
//Activity 与 Service 交互的通道
private MyBinder binder = new MyBinder();
//定位客户端
private LocationClient client;

@Override
public void onCreate() {
super.onCreate();
getLocation();
}

/**
* 初始化定位选项
*/
private void getLocation() {
client = new LocationClient(getApplicationContext());
//注册监听接口
client.registerLocationListener(new MyLocationListener());
LocationClientOption option = new LocationClientOption();
//每 5 秒更新一次定位
option.setScanSpan(5 * 1000);
//开启描述性信息,不开启不会返回城市名等信息
option.setIsNeedAddress(true);
client.setLocOption(option);
//开始定位
client.start();
}

@Override
public IBinder onBind(Intent intent) {
//返回与 Activity 的交互通道
return binder;
}

/**
* 在代理中返回真正地城市名信息
*/
public class MyBinder extends Binder {
public String getCity() {
return city;
}
}

public class MyLocationListener implements BDLocationListener {
@Override
public void onReceiveLocation(BDLocation bdLocation) {
if (bdLocation == null) {
Toast.makeText(MyService.this, "定位失败", Toast.LENGTH_SHORT).show();
return;
}
if (bdLocation.getLocType() == BDLocation.TypeNetWorkLocation
|| bdLocation.getLocType() == BDLocation.TypeGpsLocation) {
//也可以使用 bdLocation.getCity()
if (bdLocation.getDistrict() != null) {
Log.d(TAG, "onReceiveLocation: " + bdLocation.getDistrict());
if (handler != null) {
city = bdLocation.getDistrict();
//向实例化 Handler 的线程发送消息
handler.sendEmptyMessage(0);
}
}
}
}

@Override
public void onConnectHotSpotMessage(String s, int i) {

}
}

@Override
public void onDestroy() {
super.onDestroy();
//服务销毁停止定位
client.stop();
}
}

MainActivity.java

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
public class MainActivity extends AppCompatActivity {

private MyService.MyBinder binder;
private TextView textView;

protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.service_loc);
location();
}

/**
* 绑定定位服务
*/
private void location() {
Intent intent = new Intent(this, MyService.class);
bindService(intent, connection, BIND_AUTO_CREATE);
//传入本地的 Handler
MyService.handler = handler;
}
private ServiceConnection connection=new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//获取服务的代理对象
binder = (MyService.MyBinder) service;
}

@Override
public void onServiceDisconnected(ComponentName name) {

}
};
/**
* 接受到消息后将城市名显示出来
*/
private Handler handler=new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (binder != null) {
textView.setText(binder.getCity());
}
}
};
}

运行 APP ,结果 :

运行日志:

com.alphagao.lbs_service D/baidu_location_client: baidu location connected ...
com.alphagao.lbs_service D/MyService: onReceiveLocation: 浦东新区
com.alphagao.lbs_service D/MyService: onReceiveLocation: 浦东新区
com.alphagao.lbs_service D/MyService: onReceiveLocation: 浦东新区

源码可以参考 https://github.com/AlphaGao1993/CoolerWeather .