[Android] 基于位置的服务(LBS)与百度地图 SDK

基于位置的服务 (LBS)

基于位置的服务(Location Based Services,LBS),是利用各类型的定位技术来获取定位设备当前的所在位置,通过移动互联网向定位设备提供信息资源和基础服务。首先用户可利用定位技术确定自身的空间位置,随后用户便可通过移动互联网来获取与位置相关资源和信息。LBS服务中融合了移动通讯、互联网络、空间定位、位置信息、大数据等多种信息技术,利用移动互联网络服务平台进行数据更新和交互,使用户可以通过空间定位来获取相应的服务。

LBS系统架构

LBS系统架构包括:

  • Mobile Devices (Users) 移动设备(用户)
  • Positioning System 定位系统
  • Network Service Provider 网络服务提供商
  • Location Service Provider 定位服务提供商

1、移动设备(用户)
在LBS的体系结构中,用户通常具有定位功能的移动设备用来获得其地理位置信息,同时,用户可以通过基站或WiFi热点访问互联网来发起基于位置的服务查询请求。此外,在P2P分布式结构的隐私保护方案中,我们认为移动设备还有另一个无线网卡,可以通过各种传输协议或特定的Ad hoc网络协议(如AODV协议或LANMAR协议)自发组织成移动点对点网络,并相互交换信息。

2、定位系统
定位技术是指由移动设备及时确定该设备所在地理位置的技术,其结合了硬件(例如GPS芯片)和软件(例如从多个基站信号中确定位置的程序)的技术。使用基于位置服务是以精确的定位技术为前提的,也是此服务最重要的保障技术之一。目前,常用的定位技术包括四种:

(1)全球定位系统(Global Positioning System,GPS):使用卫星和移动设备通信时根据多个卫星与同一装置之间的通信延迟,使用三角测量方法获得移动对象的经纬度,精度可达5米以下。GPS定位方法是目前最精确的经纬度定位方法。但是,该方法的缺陷是无法实现室内定位。

(2)WiFi定位:建立WiFi接入点与其准确位置之间的对应关系并预先存储在数据库中。当移动对象连接到某个WiFi访问点时,用户的位置可以通过访问数据库中相对应的表检测较精确的经纬度,如Google WiFi定位。WiFi定位的精度在1到10米的范围内。
(3)IP地址定位:当移动设备访问互联网时会被分配一个IP地址,IP地址的分配是与地域有关的。通过使用现有的IP地址与区域之间的映射关系,可以将移动对象的位置定位到城市大小的区域。
(4)三角测量法:三角测量在三角学和几何学上是借由测量目标点与固定基准线的已知端点的角度,测量目标距离的方法。当移动设备在三个移动电话基站的信号范围内时,三角测量可以获得用户的经纬度。三角测量法和WiFi定位克服了GPS不能在室内进行定位的缺点。用户在发送位置服务请求时,需要通过移动设备的定位系统获取自己的精确位置坐标,然后将自己的位置信息和查询内容一起发送给LBS服务器。

3、网络服务提供商
网络服务提供商是移动用户和LBS服务提供商之间通信的网络载体。一般情况下,网络服务提供商不能保证信息传播的安全性。恶意攻击者可以监控网络传输的内容。

4、位置服务提供商
位置服务提供商接收移动用户的查询请求信息并给于该信息计算查询相应的结果,然后通过网络把查询结果发送到移动用户。现实中,LBS服务提供商主要以盈利为主,所以位置服务提供商很可能将移动用户的位置信息卖给第三方来获取利润,存在隐私泄露的风险。

介绍百度地图SDK

百度地图 Android SDK是一套基于Android 4.0及以上版本设备的应用程序接口。 您可以使用该套 SDK开发适用于Android系统移动设备的地图应用,通过调用地图SDK接口,您可以轻松访问百度地图服务和数据,构建功能丰富、交互性强的地图类应用程序。

重点功能简介

  • 地图展示与交互

  • 室内地图

  • 境外地图

  • 地图覆盖物

  • POI检索

  • 路线规划

  • 步行导航

  • 骑行导航

使用百度地图SDK

百度地图SDK下载地址:http://lbsyun.baidu.com/index.php?title=android-locsdk/geosdk-android-download

官方文档:

Android SDK | 百度地图API SDK (baidu.com)

注册百度开发者账号

首先需要注册百度账号,登陆百度账号,打开网址 http://developer.baidu.com/user/info 填写注册信息并提交,然后去自己的邮箱通过验证,就完成注册了。

申请AK密钥

接着访问 https://lbsyun.baidu.com/apiconsole/key 会看到下面这个界面

点击 创建应用 申请 API Key, 应用名称可以随便填,这里填 LBSTest,应用类型选择 Android SDK,启用服务保持沉默即可,如下图所示

这里的发布版 SHA1 是打包程序时所用的签名文件的 SHA1 指纹,可以通过 Android 查看,新建一个 Android Studio 项目,如图(开发版 SHA1 我们等下再说)

Finish创建成功就可以了。等待Gradle同步完成, 直到运行图标的绿色三角形出现.

点击 :app --> Task --> android -->

如果Gradle Task List没有显示, 则调整以下设置:

同步完成后, 可以查到Gradle的Task List:

点击 :app --> Task --> android, 双击 signingReport,我们即可得到 发布版SHA1 指纹:

> Task :app:signingReport

Variant: debug
Config: debug
Store: C:\Users\ryudo\AppData\Local\Android\.android\debug.keystore
Alias: AndroidDebugKey
MD5: 46:1A:6B:B8:90:15:7B:AD:BB:07:D2:E5:24:2C:81:30
SHA1: E4:E6:EE:BE:7C:3D:AC:4F:9E:C1:06:2E:63:A2:FF:81:EA:77:B7:F6
SHA-256: BE:F7:7C:81:97:17:6E:99:24:98:0C:7A:61:57:92:AB:AE:6E:A1:64:F6:E5:6E:00:C0:13:BE:87:7E:3E:E7:0B
Valid until: 2052��8��7��������
----------
Variant: release
Config: null
Store: null
Alias: null
----------

Variant: debugAndroidTest
Config: debug
Store: C:\Users\ryudo\AppData\Local\Android\.android\debug.keystore
Alias: AndroidDebugKey
MD5: 46:1A:6B:B8:90:15:7B:AD:BB:07:D2:E5:24:2C:81:30
SHA1: E4:E6:EE:BE:7C:3D:AC:4F:9E:C1:06:2E:63:A2:FF:81:EA:77:B7:F6
SHA-256: BE:F7:7C:81:97:17:6E:99:24:98:0C:7A:61:57:92:AB:AE:6E:A1:64:F6:E5:6E:00:C0:13:BE:87:7E:3E:E7:0B
Valid until: 2052��8��7��������
----------

BUILD SUCCESSFUL in 1s
1 actionable task: 1 executed

Build Analyzer results available
19:55:00: Task execution finished 'signingReport'.

把 SHA1: 后面的字符复制到创建百度地图应用界面的对应位置,

开发版的 SHA1 我们需要创建一个正式的签名文件,点击 Android Studio 顶部工具栏中的 Build --> Generate Signed APK,弹出如下窗口

选择APK:

NEXT:

点击 Create new… 弹出如下窗口

在Key Store path处选择签名文件保存的路径,并填写签名文件名称,并点击 OK

填写密码和名字

点击 OK,会回到如下界面,然后直接点击右上角关闭这个窗口

然后按 win + r 输入 cmd 输入如下命令,

keytool -list -v -keystore <签名文件路径>

比如我创建的签名文件路径为 C:\Users\ryudo\Android.jks\LBSTest.jks ,则完整命令为:

keytool -list -v -keystore C:\Users\ryudo\Android.jks\LBSTest.jks

输入之前提供的密码, 输出如上所示. 从中找到SHAI证书指纹:

SHA1: 93:AA:38:B8:9F:11:72:65:97:AE:04:D9:09:F0:C0:03:21:69:7F:53

把 SHA1:后的字符复制到创建百度地图应用界面的对应位置。
包名这里我们填 com.example.lbstest (包名在我们创建 Android project 的界面中已经设置了)

点击提交, 应用的创建成功了.

image-20221128202257464

上图中WFxwnrDH4vCzwzxxxxxxxxxxxxxxxxxxxx 就是我们申请到的 API Key, 可以点击复制到剪切板。

使用百度定位

建议在手机上运行调试

下载 LBS SDK

在开始编码之前,我们需要下载百度 LBS 开放平台的 SDK,下载地址为 http://lbsyun.baidu.com/index.php?title=sdk/download&action#selected=mapsdk_basicmap,mapsdk_searchfunction,mapsdk_lbscloudsearch,mapsdk_calculationtool,mapsdk_radar

选择 基础定位 和 基础地图 然后点击 开发包 下载按钮

在使用之前,您需要先申请密钥,且密钥和应用证书和包名绑定。

添加依赖库

解压下载的文件,在下载文件中 libs 目录下的内容分为两部分,如图,BaiduLBS_Android.jar 是 Java 层要使用的,其他子目录下的 so 文件时 Native 层要用到的。so 文件是用 C/C++ 语言编写的,然后再用 NDK 编译出来的。 我们这里不需要编写 C/C++ 的代码,因为百度都已经做好了封装, 但是我们需要将 libs 目录下的每一个文件都放到正确的位置。

观察一下当前的项目结构, app 模块下有一个 libs 目录,这里就是用来存放所有 Jar 包的,我们将 BaiduLBS_Android.jar 复制到这个目录下,如下图所示(需要切换为Project视图)

接下来,展开 src/main 目录,鼠标右键点击该目录–>New–>Directory, 创建一个名为 jniLibs 的目录,这个目录是专门用来存放 so 文件的,然后把压缩包里的其他所有目录直接复制到这里,如图

为了让项目引用到 Jar 包中提供的接口,我们需要把BaiduLBS_Android.jar作为库添加到模块:

点击 Sync 之后,libs 目录下的 jar 文件就会多出一个向右的箭头,表示项目已经能引用到这些 jar 包
这样我们就把 LBS 的 SDK 都准备好了,接下来就可以开始编码了

编码

MainActivity.java 代码如下:

package com.example.lbstest;

import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import com.baidu.location.BDLocation;
import com.baidu.location.BDLocationListener;
import com.baidu.location.LocationClient;
import com.baidu.location.LocationClientOption;
import com.baidu.mapapi.CoordType;
import com.baidu.mapapi.SDKInitializer;
import com.baidu.mapapi.common.BaiduMapSDKException;
import com.baidu.mapapi.map.BaiduMap;
import com.baidu.mapapi.map.MapStatusUpdate;
import com.baidu.mapapi.map.MapStatusUpdateFactory;
import com.baidu.mapapi.map.MapView;
import com.baidu.mapapi.map.MyLocationData;
import com.baidu.mapapi.model.LatLng;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    public LocationClient mLocationClient;

    private MapView mapView;
    private BaiduMap baiduMap;
    private boolean isFirstLocate = true;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // 是否同意隐私政策,默认为false
        SDKInitializer.setAgreePrivacy(getApplicationContext(), true);
        try {
            // 在使用 SDK 各组间之前初始化 context 信息,传入 ApplicationContext
            SDKInitializer.initialize(getApplicationContext());

            // 自4.3.0起,百度地图SDK所有接口均支持百度坐标和国测局坐标,用此方法设置您使用的坐标类型.
            // 包括BD09LL和GCJ02两种坐标,默认是BD09LL坐标。
            SDKInitializer.setCoordType(CoordType.BD09LL);
            // 百度地图接口,最新颁布了“隐私合规接口”。就是在百度地图获得定位时,要用户同意定位。
            LocationClient.setAgreePrivacy(true);
            try {
                mLocationClient = new LocationClient(getApplicationContext());
                mLocationClient.registerLocationListener(new MyLocationListener());

                setContentView(R.layout.activity_main);
                mapView = (MapView) findViewById(R.id.bmapView);
                baiduMap = mapView.getMap();
                baiduMap.setMyLocationEnabled(true);//用于显示我的位置

                // 动态申请权限
                List permissionList = new ArrayList<>();
                if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.
                        permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                    permissionList.add(Manifest.permission.ACCESS_FINE_LOCATION);
                }
                if (ContextCompat.checkSelfPermission(MainActivity.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(MainActivity.this, permissions, 1);
                } else {
                    requestLocation();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        } catch (BaiduMapSDKException e) {
            e.printStackTrace();
        }

    }

    //用于首次定位让地图移动到当前位置
    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);
    }

    private void requestLocation() {
        initLocation();
        mLocationClient.start(); // start方法开始定位
    }

    private void initLocation() {
        LocationClientOption option = new LocationClientOption();
        option.setScanSpan(5000); // 5秒更新一次位置信息
        option.setIsNeedAddress(true); // 启用详细位置
        option.setLocationMode(LocationClientOption.LocationMode.Device_Sensors);//使用GPS定位
        mLocationClient.setLocOption(option);
    }

    @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);
    }

    /**
     * 动态权限检查
     * @param requestCode
     * @param permissions
     * @param grantResults
     */
    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions,
                                           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(this, "必须同意所有权限才能使用本程序",
                                    Toast.LENGTH_SHORT).show();
                            finish();
                            return;
                        }
                    }
                    requestLocation();
                } else {
                    Toast.makeText(this, "发生未知错误", Toast.LENGTH_SHORT).show();
                    finish();
                }
                break;
            default:
        }
    }

    public class MyLocationListener implements BDLocationListener {

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

activity_main.xml 布局文件代码如下:




    //地图控件使其占满整个屏幕
    

权限文件 AndroidManifest.xml 代码如下:




    
    
    
    
    
    
    
    
    
    
    
    
    

    
        
        
        
            
                

                
            
        
        
        
        
    

至此,Android 百度地图初步开发完成

使用真机调试运行

为了正确显示定位效果, 建议使用真机运行和调试地图程序.

首先将真机(安卓或者鸿蒙均可)连接到开发机上

根据手机机型上网查询打开USB调试模式的方法.

我是系统更新-->开发人员选项-->USB调试

可以通过ADB命令检查是否连接上手机设备

此时AndroidStudio的运行设备也会自动切换到物理机

切换Gradle任务为app, 点击运行:

手机显示:

官方示例代码和API文档参考

img 示例代码
img API文档参考

Views: 787