欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

说说在 Android 如何实现基于位置的服务(基于百度 API)

程序员文章站 2022-05-11 17:58:37
...

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

利用定位服务,可以开发出许多丰富多彩的功能。比如天气预报 APP,可以根据用户所在的位置自动选择所在城市。约会时,可以通过微信发出碰头地点,让朋友们能够尽快过来等等。

确定出用户所在的位置。通常有两种技术:

技术 说明 优点 不足
GPS 定位 它是基于手机内置的 GPS 硬件直接和卫星交互来获得当前的经纬度信息。 精确度高 只能室外使用,因为室内基本无法接收到卫星的信号。
网络定位 根据手机当前网络附近的三个基站进行测速,以此计算出手机和每个基站之间的距离,最后通过三角定位确定一个大概位置。 室内外均可使用。 精确度一般。

2 申请百度 API Key

首先必须先注册一个百度开发者账号(官网请点击),这个很简单,所以这里就不赘述了哦O(∩_∩)O哈哈~

注册并登陆成功后,打开百度地图开放平台创建新应用:

说说在 Android 如何实现基于位置的服务(基于百度 API)

  • 应用类型选择:Android SDK。
  • 启用服务中选择 Android 定位 SDK与 Android地图SDK,默认为全选。
  • 发布版与开发版 SHA1:使用 Android Studio 的 Gradle 插件生成的结果。
  • 包名:就是项目所在的包路径。

点击 Android Studio 的 Gradle 标签,执行 signingReport 任务。

说说在 Android 如何实现基于位置的服务(基于百度 API)

执行成功后,点击右下角的【Gradle Console】标签,可以看到生成的 SHA1 码:

说说在 Android 如何实现基于位置的服务(基于百度 API)


点击【提交】成功后,就会转到应用列表页,里面的访问应用(AK)就是我们申请到的 API Key 啦:

说说在 Android 如何实现基于位置的服务(基于百度 API)

3 实现定位服务

建议在手机上运行下面的代码,因为这样可以得到真实的位置数据,有助于我们更深刻地理解基于位置的服务。

3.1 下载并安装百度 API 包

打开百度地图开放平台,找到地图引擎入口:

说说在 Android 如何实现基于位置的服务(基于百度 API)

点击后,进入地图 SDK 说明页:

说说在 Android 如何实现基于位置的服务(基于百度 API)

点击产品下载-》自定义下载,在此选择相应的开发资源:

说说在 Android 如何实现基于位置的服务(基于百度 API)

这里我们选择【全量定位】与【基础地图】资源。

  • 全量定位:包含离线定位、室内高精度定位能力,同时提供更人性化的位置描述服务。
  • 基础地图:包括基础地图、卫星图、路况图和各种覆盖物,及与地图相关的操作、事件监听。

说说在 Android 如何实现基于位置的服务(基于百度 API)

这里的 BaiduLBS_Android.jar 就是百度提供的 API 包。所以我们把这个包放入 app 的 libs 目录下:

说说在 Android 如何实现基于位置的服务(基于百度 API)

注意:如果找不到 libs 目录,一个可能的原因是视图模式问题,视图模式(页签左上角)请选择【Project】。

接下来在 app → src → main 中,新建一个 jniLibs 文件夹,然后再把百度下载包中的其它文件夹都拷贝到这个新建的文件夹中:
说说在 Android 如何实现基于位置的服务(基于百度 API)

因为是手动引入包,所以需要点击 Android Studio 工具栏中的 Sync Project with Gradle Files,手动同步:

说说在 Android 如何实现基于位置的服务(基于百度 API)

同步成功后,就会发现刚才手工引入的 jar 包左侧多了一个朝右的箭头,这就表示这个包在项目中可以引用啦:

说说在 Android 如何实现基于位置的服务(基于百度 API)

3.2 显示当前位置经纬度

3.2.1 布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/position"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!" />
</LinearLayout>

这里,我们使用 TextView 来显示当前位置的经纬度。

3.2.2 AndroidManifest.xml

...
<!--百度 LBS API 的相关权限声明-->
<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" />

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">

    <meta-data
        android:name="com.baidu.lbsapi.API_KEY"
        android:value="aYOacSei5VfK2eC7GjcS8j7iPGxGjPal" />

    <activity android:name=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>

    <service
        android:name="com.baidu.location.f"
        android:enabled="true"
        android:process=":remote" />
</application>

在此,我们做了以下工作:
1. 添加百度 LBS API 需要用到的相关权限声明。
2. 在 <application> 标签内添加了 <meta-data> 标签,在此记录之前申请的百度 API Key。
3. 最后注册百度 LBS 服务。之所以服务名叫 f,是因为代码被混淆过。

3.2.3 活动类

public class MainActivity extends AppCompatActivity {

    public static final int REQUEST_CODE = 1;


    public LocationClient locationClient;

    private TextView position;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        locationClient = new LocationClient(getApplicationContext());
        //注册【定位监听器】
        locationClient.registerLocationListener(new LocationListener());

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

        /**
         * 一次性申请多个权限
         */
        List<String> permissions = new ArrayList<>();
        addPermission(permissions, Manifest.permission.ACCESS_FINE_LOCATION);
        addPermission(permissions, Manifest.permission.READ_PHONE_STATE);
        addPermission(permissions, Manifest.permission.WRITE_EXTERNAL_STORAGE);
        if (!permissions.isEmpty()) {
            ActivityCompat.requestPermissions(MainActivity.this, permissions.toArray(new String[permissions.size()]), REQUEST_CODE);
        } else {
            beginLocation();
        }
    }

    /**
     * 开始定位
     */
    private void beginLocation() {
        setLocationOptions();
        locationClient.start();
    }

    /**
     * 设置 LBS
     */
    private void setLocationOptions() {
        LocationClientOption option = new LocationClientOption();
        //每 10 s 更新一次当前位置
        option.setScanSpan(10000);
        locationClient.setLocOption(option);
    }

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

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        switch (requestCode) {
            case REQUEST_CODE:
                if (grantResults.length > 0) {
                    for (int result : grantResults) {
                        if (result != PackageManager.PERMISSION_GRANTED) {
                            Toast.makeText(this, "权限被拒绝啦", Toast.LENGTH_SHORT).show();
                            finish();
                            return;
                        }
                    }
                    beginLocation();
                } else {
                    Toast.makeText(this, "出现与权限相关的问题啦", Toast.LENGTH_SHORT).show();
                    finish();
                }
                break;
            default:
                break;
        }
    }

    /**
     * 添加权限
     *
     * @param permissions
     * @param neededPermission 需要的权限
     */
    private void addPermission(List<String> permissions, String neededPermission) {
        if (ContextCompat.checkSelfPermission(MainActivity.this, neededPermission) != PackageManager.PERMISSION_GRANTED) {
            permissions.add(neededPermission);
        }
    }

    /**
     * 定位监听器
     */
    private class LocationListener implements BDLocationListener {
        @Override
        public void onReceiveLocation(BDLocation location) {
            StringBuilder content = new StringBuilder();
            content.append("纬度:").append(location.getLatitude() + "\n");
            content.append("经度:").append(location.getLongitude() + "\n");
            content.append("定位方式:");
            if (location.getLocType() == BDLocation.TypeGpsLocation) {
                content.append("GPS");
            } else if (location.getLocType() == BDLocation.TypeNetWorkLocation) {
                content.append("网络");
            }
            position.setText(content);
        }
    }


}

在此,我们做了以下工作:
1. 在 onCreate 方法中,我们注册了定位监听器 locationClient,这样当系统获取到位置信息时,就会回调这个监听器。
2. 因为百度 LBS API 需要多个权限,所以我们利用 List<String> 实现一次性申请多个权限,因为 ActivityCompat.requestPermissions() 接受数组参数,而 List 转为数组很容易。
3. 在 beginLocation 方法中,我们首先设置 LBS 参数,让它每 10 s 更新一次当前位置;接着,启动定位监听器。
4. 系统获取位置信息时,回调到 LocationListener 的 onReceiveLocation 方法。BDLocation 对象存在几个常用的方法:getLatitude() 表示获取经度,getLongitude() 表示获取纬度。getLocType() 表示获取定位的方式。
5. 注意: 在 onDestroy() 方法中必须调用 locationClient.stop(); 停止定位,否则 locationClient 会默默地在后台不停地进行定位工作,这会对手机电量产生严重影响。

安装并运行 APP:

说说在 Android 如何实现基于位置的服务(基于百度 API)

3.3 设置定位模式

因为 GPS 定位需要手动开启,它一般在手机的设置 → 位置信息中。

如果是小米手机,那么它在 设置 → 更多设置 → 系统安全 → 位置信息中。

说说在 Android 如何实现基于位置的服务(基于百度 API)

可以看出,定位模式有三种,上诉截图中对每一种定位模式都做了详细介绍。

在 LBS SDK 也有三种定位模式与手机的定位模式相匹配:

定位模式 说明
Hight_Accuracy 高精确度(默认模式),会在 GPS 信号正常的情况下优先使用 GPS 定位,在无法接收 GPS 信号时用网络定位 。
Battery_Saving 节电,只会使用网络定位。
Device_Sensors 设备传感器,只会使用GPS定位。

注意:只有当定位操作真正开始的时候,才会影响到手机的电量。

也可以通过 LocationClientOption 的 setLocationMode 方法来指定定位模式:

option.setLocationMode(LocationClientOption.LocationMode.Device_Sensors);

3.4 获取地址信息

通过 LocationClientOption 的 setIsNeedAddress 方法,可以开启地址信息获取功能:

option.setIsNeedAddress(true);

然后就可以在 BDLocation 中获取到地址信息啦:

content.append("所在国家:").append(location.getCountry() + "\n");
content.append("所在省份:").append(location.getProvince() + "\n");
content.append("所在市:").append(location.getCity() + "\n");
content.append("所在区:").append(location.getDistrict() + "\n");
content.append("所在街道:").append(location.getStreet() + "\n");

说说在 Android 如何实现基于位置的服务(基于百度 API)

注意:获取地址必须使用网络哦O(∩_∩)O~

4 使用地图

4.1 展示地图

现在让我们在 APP 中展示地图吧O(∩_∩)O~

修改布局文件,加入地图视图控件:

<com.baidu.mapapi.map.MapView
    android:id="@+id/mapView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:clickable="true">
</com.baidu.mapapi.map.MapView>

之前的 TextView 可以使用 android:visibility="gone" 将其隐藏。

修改活动类:

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

    /**
     * 初始化地图
     */
    SDKInitializer.initialize(getApplicationContext());
    setContentView(R.layout.activity_main);

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


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

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

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

在该类中,我们新增了这些逻辑:
1. 调用 SDKInitializer.initialize() 方法,执行初始化操作。注意:一定要在 setContentView(R.layout.activity_main); 之前调用,否则会抛出 you have not supplyed the global app context info from SDKInitializer.initialize(Context) function 错误。
2. 重写 onResume()、onPause()、onDestroy() 方法,释放资源。

说说在 Android 如何实现基于位置的服务(基于百度 API)

4.2 移动地图中心

百度 LBS SDK 的 API 中提供了一个BaiduMap 类,使用以下方法实例化后,就可以对地图进行各种各样的操作啦:

BaiduMap baiduMap = mapView.getMap();

修改活动类:

...
private BaiduMap baiduMap;

//是否为第一次定位
private boolean isFirstLocated = true;
...

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ...
    baiduMap = mapView.getMap();
}

private class LocationListener implements BDLocationListener {
    @Override
    public void onReceiveLocation(BDLocation location) {

        if (location.getLocType() == BDLocation.TypeGpsLocation || location.getLocType() == BDLocation.TypeNetWorkLocation) {
            if (isFirstLocated) {
                LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude());

                //设置经纬度
                baiduMap.animateMapStatus(MapStatusUpdateFactory.newLatLng(latLng));

                //设置缩放级别(在 3 ~ 19 之间)
                baiduMap.animateMapStatus(MapStatusUpdateFactory.zoomTo(16f));
                isFirstLocated = false;
            }
        }
    }
}

可以看出,通过 LatLng latLng = new LatLng(39.9, 116.3); 方法,就可以让地图移动任何地址啦O(∩_∩)O~

运行程序:

说说在 Android 如何实现基于位置的服务(基于百度 API)

4.3 标注位置

地图上可以添加一个标注,用于显示当前位置。

百度 LBS SDK 提供了一个 MyLocationData.Builder 类,它封装了设备当前所在的位置,我们只需将经纬度信息传入即可。

修改活动类:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ...
    baiduMap.setMyLocationEnabled(true);
}
...

private class LocationListener implements BDLocationListener {
        @Override
        public void onReceiveLocation(BDLocation location) {
        ...
    if (location.getLocType() == BDLocation.TypeGpsLocation || location.getLocType() == BDLocation.TypeNetWorkLocation) {
    if (isFirstLocated) {
        LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude());


        //设置经纬度
        baiduMap.animateMapStatus(MapStatusUpdateFactory.newLatLng(latLng));

        //设置缩放级别(在 3 ~ 19 之间)
        baiduMap.animateMapStatus(MapStatusUpdateFactory.zoomTo(16f));
        isFirstLocated = false;
    }

    //添加标注
    MyLocationData.Builder builder = new MyLocationData.Builder();
    builder.latitude(location.getLatitude());
    builder.longitude(location.getLongitude());
    baiduMap.setMyLocationData(builder.build());
  }
}

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

做了这些工作:

1.在 onCreate 方法中,开启标注。
2.在 LocationListener 的 onReceiveLocation 方法中添加标注。
3.在 onDestroy 方法中,关闭标注。

因为一般只需移动一次地图的中心位置,所以我们在代码中加了 isFirstLocated 变量进行判断。

再次运行:

说说在 Android 如何实现基于位置的服务(基于百度 API)

看见地图中心的蓝点了吗O(∩_∩)O哈哈~

相关标签: Android