关于 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
83public 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;
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();
}
public IBinder onBind(Intent intent) {
//返回与 Activity 的交互通道
return binder;
}
/**
* 在代理中返回真正地城市名信息
*/
public class MyBinder extends Binder {
public String getCity() {
return city;
}
}
public class MyLocationListener implements BDLocationListener {
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);
}
}
}
}
public void onConnectHotSpotMessage(String s, int i) {
}
}
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
46public 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() {
public void onServiceConnected(ComponentName name, IBinder service) {
//获取服务的代理对象
binder = (MyService.MyBinder) service;
}
public void onServiceDisconnected(ComponentName name) {
}
};
/**
* 接受到消息后将城市名显示出来
*/
private Handler handler=new Handler(){
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: 浦东新区