笔记:Android 位置服务

基于位置服务简称 LBS,主要工作原理通过无线电通讯网络或者 GPS 等定位方式来确定移动设备所在的位置,
GPS 定位是通过手机内置的 GPS 硬件和卫星交互获得当前经纬度信息,网络定位是根据手机当前网络附近的三个基站进行测速,以此计算出手机和每个基站之间的距离,在通过三角定位出一个大概的位置

打开网页:http://lbsyun.baidu.com/apiconsole/key
应用管理>>我的应用>>创建应用
在这里插入图片描述
输入应用名称,选择应用类型为:Android SDK,输入 SHA1
在这里插入图片描述
SHA1 是打包程序时所用的签名文件的 SHA1 指纹,通过 Android Studio 右侧工具栏的 Gradle>>项目名>>Tasks>>android,双击 signingReport 就可以查看签名文件信息,点击提交就创建了一个百度的应用
在这里插入图片描述
其中访问应用(AK)就是所需要的

准备sdk

在这里插入图片描述
在这里插入图片描述
选好服务后点击 开发包 下载即可
解压后有一个lib目录,该目录下又分为两部分,BaiduLBS_Android.jar 文件是 Java 层使用,其他文件夹下都是 .os 文件,是 Native 层用到的,将 BaiduLBS_Android.jar 文件放到项目名>>app>>libs 文件下,在 项目名>>app>>src>>main 文件夹下建立 jniLibs文件夹,并将其他的文件放到这个文件夹下
在这里插入图片描述
注意在 gradle 中声明 libs 文件夹下的 jar包,一般 app/build.gradle 文件都会默认配置以下这段声明

dependencies {
    implementation fileTree(dir: "libs", include: ["*.jar"])
    ......
}

这表示会将 libs 目录下的所有以 jar 为后缀的文件添加到当前项目的引用中,由于没有修改该文件,所以没有弹出 Sync Now 的提示,需要手动同步,不然项目任然无法引用 jar包 中的任何接口

确定自己的位置

添加权限,这些都是百度 LBS SDK内部用到的权限

	<!-- 这个权限用于进行网络定位-->
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
    <!-- 这个权限用于访问GPS定位-->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    <!-- 用于访问wifi网络信息,wifi信息会用于进行网络定位-->
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
    <!-- 获取运营商信息,用于支持提供运营商信息相关的接口-->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <!-- 这个权限用于获取wifi的获取权限,wifi信息会用来进行网络定位-->
    <uses-permission android:name="android.permission.CHANGE_WIFI_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.READ_LOGS"/>
    <!-- 允许访问振动设备 -->
    <uses-permission android:name="android.permission.VIBRATE"/>
    <!-- 允许使用PowerManager的 WakeLocks保持进程在休眠时从屏幕消失 -->
    <uses-permission android:name="android.permission.WAKE_LOCK"/>
    <!-- 允许程序读取或写入系统设置 -->
    <uses-permission android:name="android.permission.WRITE_SETTINGS"/>
    <!-- android 9.0上使用前台服务,需要添加权限 -->
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <!-- 用于读取手机当前的状态 -->
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <!-- 读取缓存数据 -->
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <!-- 获取模拟定位信息 -->
    <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"/>

除此之外还需要在<application>标签中注册服务,并添加一个<meta-data>,其中 android:name 部分是固定的,android:value 部分填入申请的 API Key

		<!-- 声明service组件 -->
        <service
            android:name="com.baidu.location.f"
            android:enabled="true"
            android:process=":remote"/>
        <!-- AK鉴权 -->
        <meta-data
            android:name="com.baidu.lbsapi.API_KEY"
            android:value="请输入百度开放平台申请的Android端API KEY" /> <!-- http://lbsyun.baidu.com/apiconsole/key -->
	public LocationClient locationClient;
    private TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_show_location);
        textView = (TextView)findViewById(R.id.position_textview);
        getPermission();
    }
    
	@Override
    protected void onDestroy() {
        super.onDestroy();
        locationClient.stop();
    }

TextView 用于显示自己位置,getPermission() 方法用于申请必要的定位权限

	private void getPermission(){
        List<String> permissionList = new ArrayList<>();
        if (ContextCompat.checkSelfPermission(ShowLocationActivity.this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED){
            permissionList.add(Manifest.permission.ACCESS_FINE_LOCATION);
        }
        //READ_PHONE_STATE
        if (ContextCompat.checkSelfPermission(ShowLocationActivity.this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED){
            permissionList.add(Manifest.permission.ACCESS_COARSE_LOCATION);
        }
        if (ContextCompat.checkSelfPermission(ShowLocationActivity.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(ShowLocationActivity.this, permissions, 1);
        }else {
            requestLocation();
        }
    }
    private void requestLocation(){
        initLocationOption();
        locationClient.start();
    }
	@Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull 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(ShowLocationActivity.this,"缺少必要运行权限", Toast.LENGTH_SHORT).show();
                            finish();
                            return;
                        }
                    }
                    requestLocation();
                }else {
                    Toast.makeText(ShowLocationActivity.this,"发生为之错误", Toast.LENGTH_SHORT).show();
                    finish();
                    return;
                }
                break;
            default:
                break;

        }
    }

getPermission() 方法用于进行运行时权限的申请,由于 ACCESS_COARSE_LOCATION 和 ACCESS_FINE_LOCATION 都属于同一个权限组,因此两个只要申请其中一个就行了,这里将两个都进行了申请,但用户授权的时候只会受一次
首先创建了一个 List 集合用于存放需要申请的权限,最后如果有需要申请的权限,就调用requestPermissions() 方法一次申请,申请后会回掉 onRequestPermissionsResult() 方法,判断授权结果如果授权成功就调用 requestLocation() 方法,否则退出

	private void requestLocation(){
        initLocationOption();
        locationClient.start();
    }

requestLocation() 中 initLocationOption() 方法用于初始化百度sdk的定位参数配置,locationClient.start() 启动开始定位,定位的结果会回掉在 initLocationOption() 方法中注册的监听器中

	private void initLocationOption(){
        //定位服务的客户端。宿主程序在客户端声明此类,并调用,目前只支持在主线程中启动
        locationClient = new LocationClient(getApplicationContext());
        //声明LocationClient类实例并配置定位参数
        LocationClientOption locationClientOption = new LocationClientOption();
        //注册监听函数
        locationClient.registerLocationListener(new MyLocationListener());

        //可选,默认高精度,设置定位模式,高精度,低功耗,仅设备
        locationClientOption.setLocationMode(LocationClientOption.LocationMode.Hight_Accuracy);
        //可选,默认gcj02,设置返回的定位结果坐标系,如果配合百度地图使用,建议设置为bd09ll;
        locationClientOption.setCoorType("gcj02");
        //可选,默认0,即仅定位一次,设置发起连续定位请求的间隔需要大于等于1000ms才是有效的
        locationClientOption.setScanSpan(0);
        //可选,设置是否需要地址信息,默认不需要
        locationClientOption.setIsNeedAddress(true);
        //可选,设置是否需要地址描述
        locationClientOption.setIsNeedLocationDescribe(true);
        //可选,设置是否需要设备方向结果
        locationClientOption.setNeedDeviceDirect(false);
        //需将配置好的LocationClientOption对象,通过setLocOption方法传递给LocationClient对象使用
        locationClient.setLocOption(locationClientOption);
    }

在 initLocationOption() 方法中先创建了一个 LocationClient 的实例,LocationClient 的构建函数接受一个 Context 参数,这里调用 getApplicationContext() 获得一个全局的 Context 参数,然后调用 registerLocationListener() 方法来注册一个定位的监听器,当获取到位置信息的时候,就会回掉这个监听器,创建了一个 locationClientOption 对象,该对象可以配置定位的参数,最后调用 LocationClient 对象的 setLocOption() 方法将定位的参数设置进去

	public class MyLocationListener extends BDAbstractLocationListener {
        @Override
        public void onReceiveLocation(BDLocation location) {
            //此处的BDLocation为定位结果信息类,通过它的各种get方法可获取定位相关的全部结果
            //以下只列举部分获取经纬度相关(常用)的结果信息
            //更多结果信息获取说明,请参照类参考中BDLocation类中的说明
            StringBuilder currentPosition = new StringBuilder();
            //获取纬度信息
            double latitude = location.getLatitude();
            currentPosition.append("纬度:").append(latitude).append("\n");
            //获取经度信息
            double longitude = location.getLongitude();
            currentPosition.append("经度:").append(longitude).append("\n");
            //获取定位精度,默认值为0.0f
            float radius = location.getRadius();
            //获取经纬度坐标类型,以LocationClientOption中设置过的坐标类型为准
            String coorType = location.getCoorType();
            //获取定位类型、定位错误返回码,具体信息可参照类参考中BDLocation类中的说明
            int errorCode = location.getLocType();
            currentPosition.append("定位类型:");
            if (errorCode == BDLocation.TypeGpsLocation){
                currentPosition.append("GPS");
            }else if (errorCode == BDLocation.TypeNetWorkLocation){
                currentPosition.append("网络");
            }
            // 将定位信息设置到界面上
            textView.setText(currentPosition);
        }
    }

MyLocationListener 继承于 BDAbstractLocationListener 类,要实现父类的 onReceiveLocation() 方法,当定位成功后,会回掉该方法,定位的结果保存在 location 中,然后就是获得相应的数据,组装成一个字符串,显示到 TextView 上面。

选择定位模式

GPS定位 的精确度更高,但 GPS 定位功能必须要用户主动去启用

一共有三种模式可选:Hight_Accuracy、Battery_Saving 和 Device_Sensors

  • Hight_Accuracy:表示高精度确定模式,会在 GPS 信号正常下优先使用 GPS 定位,无法使用的时候使用网络
  • Battery_Saving:表示省电模式,只会使用网络进行定位
  • Device_Sensors表示传感器模式,只会使用 GPS 进行定位

使用 LocationClientOption 类的 setLocationMode() 方法进行设置

位置详细信息

使用 LocationClientOption 类的 setIsNeedAddress() 方法设置成 true 就可以拿到地址的详细信息,在 onReceiveLocation() 回掉方法中:

			String country = location.getCountry();
            currentPosition.append("国家:").append(country).append("\n");

            String province = location.getProvince();
            currentPosition.append("省:").append(province).append("\n");

            String city = location.getCity();
            currentPosition.append("市:").append(city).append("\n");

            String district = location.getDistrict();
            currentPosition.append("区:").append(district).append("\n");

            String street = location.getStreet();
            currentPosition.append("街道:").append(street).append("\n");
            textView.setText(currentPosition);

在这里插入图片描述

使用百度地图

布局文件中放置一个 MapView 空间,这个是百度提供的自定义控件

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_height="match_parent"
    android:layout_width="match_parent">

    <com.baidu.mapapi.map.MapView
        android:id="@+id/position_bmapView"
        android:layout_width="match_parent"
        android:layout_height="1000px"
        />
    <TextView
        android:id="@+id/position_textview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

</LinearLayout>
	@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        SDKInitializer.initialize(getApplicationContext());
        setContentView(R.layout.activity_show_location);
        textView = (TextView)findViewById(R.id.position_textview);
        mapView = (MapView)findViewById(R.id.position_bmapView);
        getPermission();

    }

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

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

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

首先调用 SDKInitializer 的 initialize() 方法来进行初始化,initialize() 方法接收一个 Context 参数,注意初始化操作一定要放在setContentView() 方法前调用,不然会报错,另外重写 onResume()、onPause()、onDestory() 这三个方法对 MapView 进行管理,以保证资源能够及时的释放
但现在显示的是地图默认的定位位置(北京市),还需要将上面获得到移动设备当前位置的经纬度设置到地图中显示

移动到我的位置

百度 LBS SDK 的 API 中提供了一个 BaiduMap 类,它是地图的总控制器,调用 MapView的 getMap() 方法就能获取到 BaiduMap 的实例

有了 BaiduMap 之后就能对地图进行各种操作,百度地图将缩放级别的取值范围限定在 3 到 19 之间,小数点位的值也是可以取的,值越大地图显示的信息越详细

	private BaiduMap baiduMap;
    private boolean isFirstLocate = true;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        SDKInitializer.initialize(getApplicationContext());
        setContentView(R.layout.activity_show_location);
        textView = (TextView)findViewById(R.id.position_textview);
        mapView = (MapView)findViewById(R.id.position_bmapView);
        baiduMap = mapView.getMap();
        getPermission();
    }

isFirstLocate 用于防止多次调用 animateMapStatus() 定位到自己的位置,由于设置了 locationClientOption.setScanSpan(1000) 连续定位请求的间隔为1秒,否则会每隔 1 就定位自己的位置

	private void navigateTo(double latitude, double longitude){
        if(isFirstLocate){
            LatLng latLng = new LatLng(latitude, longitude);
            MapStatusUpdate update = MapStatusUpdateFactory.newLatLng(latLng);
            baiduMap.animateMapStatus(update);
            update = MapStatusUpdateFactory.zoomBy(14f);
            baiduMap.animateMapStatus(update);
            isFirstLocate = false;
        }
    }

加入了一个 navigateTo() 方法用于定位当前位置

LatLng 类的构造方法接收两个参数,一个是维度值,一个是经度值,之后调用 MapStatusUpdateFactory 的 newLatLng() 方法将 LatLng 对象传入,newLatLng() 返回一个MapStatusUpdate 对象,将这个对象传入 BaiduMap 的 animateMapStatus() 方法就可以将地图移动到指定位置的功能

MapStatusUpdateFactory 的 zoomTo() 方法接收一个 float 参数,用于设置缩放的级别,zoomTo() 返回一个 MapStatusUpdate 对象,将这个对象传入 BaiduMap 的 animateMapStatus() 方法就可以完成缩放功能

在 onReceiveLocation() 方法中将维度经度传入 navigateTo() 方法,这样就能让地图移动到设备所在的位置了

这里注意要将获取经纬度的坐标系转换成 bd09ll 格式,百度使用的是这个格式,不然定位会有偏差,使用 LocationClientOption 的 locationClientOption.setCoorType(“bd09ll”) 方法设置就行

让我显示在地图上
	@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        SDKInitializer.initialize(getApplicationContext());
        setContentView(R.layout.activity_show_location);
        textView = (TextView)findViewById(R.id.position_textview);
        mapView = (MapView)findViewById(R.id.position_bmapView);
        baiduMap = mapView.getMap();
        baiduMap.setMyLocationEnabled(true);
        getPermission();

    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        locationClient.stop();
        mapView.onDestroy();
        baiduMap.setMyLocationEnabled(false);
    }
	private void navigateTo(double latitude, double longitude){
        if(isFirstLocate){
            ......
        }
        MyLocationData.Builder builder = new MyLocationData.Builder();
        builder.latitude(latitude);
        builder.longitude(longitude);
        baiduMap.setMyLocationData(builder.build());
    }

百度 LBS SDK 中提供了一个 MyLocationData.Builder() 类,这个类用来封装设备当前所在位置,只需要将经纬度信息传入这个方法当中就行,将封装的信息设置完成后,调用 build() 方法就会生成一个 MyLocationData 的实例,然后再将这个实例传入 BaiduMap 的 setMyLocationData() 方法中就可以让设备当前的位置显示到地图上了

注意如果想要使用这一功能,一定要先调用 BaiduMap 的 setMyLocationEnabled() 方法将此功能开启,在程序退出的时候也要将此功能关闭释放资源

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值