第一行代码读书笔记 11-- 基于位置的服务

作者: 开心wonderful | 来源:发表于2017-01-23 22:57 被阅读632次

    本篇文章主要介绍以下几个知识点:

    • 百度定位。
    • 百度地图。
    图片来源于网络

    11.1 基于位置的服务简介

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

    LBS 所围绕的核心就是要确定出用户所在的位置。通常有两种技术:

    • GPS 定位
       基于手机内置的 GPS 硬件直接和卫星交互来获取当前的经纬度信息,精确度高,但只能室外使用,室内基本无法接收到卫星的信号。

    • 网络定位
       根据手机当前网络附近的三个基站进行测速,以此计算出手机和每个基站之间的距离,再通过三角定位确定一个大概位置,精确度一般,但室内外均可使用。

    本章主要学习百度在 LBS 方面提供的一些功能。

    11.2 使用百度定位

    要想在自己的应用程序里使用百度的 LBS 功能,首先必须申请一个 API Key,有了 API Key 就可以进行后续的 LBS 开发工作了。

    11.2.1 准备 LBS SDK

    在编码之前,先将百度 LBS 开放平台的 SDK 准备好,下载地址:http://lbsyun.baidu.com/sdk/download

    本章会用到基础地图和基础定位这两个 SDK,下载完后对该压缩包解压,libs 目录里就有我们所需要的一切了:

    压缩包libs目录下的内容

    下面把 libs 目录里的内容拷贝到我们的项目中:
     (1)把 BaiduLBS_Android.jar 拷贝到项目 app 模块中的 libs 目录:

    将 jar 包放置到 libs 目录中

    (2)展开 src/main 目录,右击该目录→New→Directory,创建一个名为 jniLibs 的目录,用来存放 so 包,然后把压缩包里的其他所以目录直接复制到这里:

    将 so 文件放置到 jniLibs 目录中

    另外,记得点击顶部工具栏中的 Sync 按钮(下图中最左边的按钮)将 BaiduLBS_Android.jar 添加到当前项目的引用中。

    AS 顶部工具栏

    以上就把 LBS 的 SDK 都准备好了。

    11.2.2 确定自己位置的经纬度

    首先在 AndroidManifest 中添加开发密钥、所需权限等信息:
     (1)在 application 中添加开发密钥

    <application>  
        <meta-data  
            android:name="com.baidu.lbsapi.API_KEY"  
            android:value="开发者 key" />  
    </application>
    

    (2)添加所需权限

    <!-- 百度 LBS 相关权限 -->
    <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" />
    

    (3)再注册一个百度 LBS SDK 中的服务

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

    接下来在布局添加个 TextView 来显示当前位置的经纬度:

    public class LocationActivity extends AppCompatActivity {
        
        private LocationClient mLocationClient;
        
        private TextView tv_show_location;// 显示当前位置信息
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            // 构建 LocationClient 实例
            mLocationClient = new LocationClient(getApplicationContext());
            // 注册一个定位监听器
            mLocationClient.registerLocationListener(new MyLocationListener());
            setContentView(R.layout.activity_location);
            
            tv_show_location = (TextView) findViewById(R.id.tv_show_location);
    
            // 声明权限,将权限添加到list集合中再一次性申请
            List<String> permissionList = new ArrayList<>();
            if (ActivityCompat.checkSelfPermission(LocationActivity.this,
                    Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                permissionList.add(Manifest.permission.ACCESS_FINE_LOCATION);
            }
            if (ActivityCompat.checkSelfPermission(LocationActivity.this,
                    Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
                permissionList.add(Manifest.permission.READ_PHONE_STATE);
            }
            if (ActivityCompat.checkSelfPermission(LocationActivity.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(LocationActivity.this,permissions,1);
            }else {
                requestLocation();
            }
        }
    
        /**
         * 开始地理位置定位
         */
        private void requestLocation() {
            mLocationClient.start();
        }
    
    
        @Override
        public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                               @NonNull int[] grantResults) {
            switch (requestCode){
                case 1:
                    if (grantResults.length > 0 ){
                        for (int result : grantResults){
                            if (result != PackageManager.PERMISSION_GRANTED){
                                ToastUtils.showShort("必须同意所有权限才能使用本程序");
                                finish();
                                return;
                            }
                        }
                        requestLocation();
                    }else {
                        ToastUtils.showShort("发生未知错误");
                        finish();
                    }
                    break;
    
                default:
                    break;
            }
        }
        
        // 监听器
        public class MyLocationListener implements BDLocationListener{
    
            @Override
            public void onReceiveLocation(BDLocation bdLocation) {
                StringBuilder currentLocation = new StringBuilder();
                currentLocation.append("纬度:").append(bdLocation.getLatitude()).append("\n");
                currentLocation.append("经线:").append(bdLocation.getAltitude()).append("\n");
                currentLocation.append("定位方式:");
                if (bdLocation.getLocType() == BDLocation.TypeGpsLocation){
                    currentLocation.append("GPS");
                } else if (bdLocation.getLocType() == BDLocation.TypeNetWorkLocation){
                    currentLocation.append("网络");
                }
                tv_show_location.setText(currentLocation);
            }
        }
    }
    

    运行程序,效果如下:

    地理位置定位的结果

    在默认情况下,调用 LocationClient 的 start() 的方法只会定位一次,若要实时更新当前的位置,还需添加如下代码:

    public class LocationActivity extends AppCompatActivity {
    
        . . .
    
       /**
         * 开始地理位置定位
         */
        private void requestLocation() {
            initLocation();
            mLocationClient.start();
        }
    
        private void initLocation() {
            // 创建LocationClientOption 对象
            LocationClientOption option = new LocationClientOption();
            option.setScanSpan(5000);  //5秒钟更新下当前位置
            mLocationClient.setLocOption(option);
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            mLocationClient.stop();//停止定位
        }
    }
    

      这样界面上的经纬度信息就会跟着位置变化一起变化。

    11.2.3 选择定位模式

      上一小节是使用网络定位的,那要如何使用 GPS 定位呢?

      GPS 定位功能必须由用户主动去启用才行,开启后可以在 initLocation() 方法中对百度 LBS SDK 的定位模式进行指定,共有3种模式:

    • Hight_Accuracy
       高精度模式(默认模式),会在GPS信号正常的情况下优先使用GPS定位,在无法接收GPS信号时用网络定位。

    • Battery_Saving
       节电模式,只会使用网络定位。

    • Device_Sensors
       传感器模式,只会使用GPS定位。

      当然,也可以调用 setLocationMode() 方法来强制指定只使用GPS定位,如下:

    option.setLocationMode(LocationClientOption.LocationMode.Device_Sensors);
    

      重新运行程序,效果如下:

    GPS定位的结果

    11.2.4 看得懂的位置信息

      经纬度一般人是看不懂,为了更加直观点,还需要进行一些简单的接口调用,如下:

    public class LocationActivity extends AppCompatActivity {
    
        . . .
        private void initLocation() {
            LocationClientOption option = new LocationClientOption();
            option.setScanSpan(5000);  
            //需要获取当前位置的详细信息
            option.setIsNeedAddress(true);
            mLocationClient.setLocOption(option);
        }
    
        // 监听器
        public class MyLocationListener implements BDLocationListener{
    
            @Override
            public void onReceiveLocation(BDLocation bdLocation) {
                StringBuilder currentLocation = new StringBuilder();
                currentLocation.append("纬度:").append(bdLocation.getLatitude()).append("\n");
                currentLocation.append("经线:").append(bdLocation.getAltitude()).append("\n");
                currentLocation.append("国家:").append(bdLocation.getCountry()).append("\n");
                currentLocation.append("省:").append(bdLocation.getProvince()).append("\n");
                currentLocation.append("市:").append(bdLocation.getCity()).append("\n");
                currentLocation.append("区:").append(bdLocation.getDistrict()).append("\n");
                currentLocation.append("街道:").append(bdLocation.getStreet()).append("\n");
                currentLocation.append("定位方式:");
                if (bdLocation.getLocType() == BDLocation.TypeGpsLocation){
                    currentLocation.append("GPS");
                } else if (bdLocation.getLocType() == BDLocation.TypeNetWorkLocation){
                    currentLocation.append("网络");
                }
                tv_show_location.setText(currentLocation);
            }
        }
     }
    

      重新运行程序,效果如下:

    当前位置的详细地址信息

    11.3 使用百度地图

    11.3.1 让地图显示出来

      要让地图显示出来,需要用到百度提供的自定义控件 MapView,在布局中添加如下:

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/activity_map"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context="com.wonderful.myfirstcode.chapter11.MapActivity">
        
        <!-- 显示地图控件 -->
        <com.baidu.mapapi.map.MapView
            android:id="@+id/map_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:clickable="true"/>
    
    </RelativeLayout>
    

      接下来,编写活动中的代码如下:

    public class MapActivity extends AppCompatActivity {
    
        private MapView mapView;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            // 初始化操作,在 setContentView() 方法前调用
            SDKInitializer.initialize(getApplicationContext()); 
            setContentView(R.layout.activity_map);
    
            mapView = (MapView) findViewById(R.id.map_view);
        }
    
        @Override
        protected void onResume() {
            super.onResume();
            mapView.onResume();
        }
    
        @Override
        protected void onPause() {
            super.onPause();
            mapView.onPause();
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            mapView.onDestroy();
        }
    }
    

      上述代码,需要重写 onResume()、onPause()、onDestroy() 这3个方法,保证资源能够及时释放。

      运行效果如下:

    让百度地图显示出来

    11.3.4 移动到我的位置

      百度 LBS SDK 的 API 中提供了一个** BaiduMap** 类,是地图的总控制器,有了它就能对地图进行各种各样的操作了。获取其实例如下:

    BaiduMap baiduMap = mapView.getMap();
    

      百度地图将缩放级别的取值范围限定在3到19之间,也可取小数点位,值越大地图显示信息越精细,如把缩放级别设置成12.5,可以这样写:

    MapStatusUpdate update = MapStatusUpdateFactory.zoomTo(12.5f);
    baiduMap.animateMapStatus(update);
    

      若要让地图移动到某个经纬度上,可以借助 LatLng 类,如将地图移动到北纬39.915°、东经116.404°,可以这样写:

    LatLng ll = new LatLng(39.915,116.404);
    MapStatusUpdate update = MapStatusUpdateFactory.newLatLng(ll);
    baiduMap.animateMapStatus(update);
    

      接下来实现下 “移动到我的位置” 这个功能,修改活动中代码如下:

    public class MapActivity extends AppCompatActivity {
    
        private LocationClient mLocationClient;
    
        private MapView mapView;
    
        private BaiduMap baiduMap;
    
        // 避免多次调用animateMapStatus() 方法
        private boolean isFirstLocate = true;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            mLocationClient = new LocationClient(getApplicationContext());
            mLocationClient.registerLocationListener(new MyLocationListener());
            SDKInitializer.initialize(getApplicationContext());
            setContentView(R.layout.activity_map);
    
            mapView = (MapView) findViewById(R.id.map_view);
            baiduMap = mapView.getMap();
            . . .
        }
    
       /**
         * 把地图移动到当前位置
         * @param location
         */
        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;
            }
        }
    
        // 监听器
        public class MyLocationListener implements BDLocationListener{
    
            @Override
            public void onReceiveLocation(BDLocation bdLocation) {
                if (bdLocation.getLocType() == BDLocation.TypeGpsLocation ||
                        bdLocation.getLocType() == BDLocation.TypeNetWorkLocation){
                    navigateTo(bdLocation);
                }
            }
        }
    
        . . .
    }
    

    11.3.3 让“我”显示在地图上

      百度 LBS SDK 当中提供了一个 MyLocationData.Builder 类,这个类是用来封装设备当前所在位置的,只需将经纬度信息传入到它相应的方法就可以,如下:

    MyLocationData.Builder locationBuilder = new MyLocationData.Builder();
    locationBuilder.latitude(39.915);
    locationBuilder.longitude(116.404);
    

      设置完要封装的信息后调用 MyLocationData.Builder 类中的 build() 方法,就会生成一个 MyLocationData 实例,把这个实例传入到 BaiduMap 的 setMyLocationData() 方法中,就可以让设备当前位置显示在地图上了,如下:

    MyLocationData locationData = locationBuilder.build();
    baiduMap.setMyLocationData(locationData);
    

      下面贴上完整的代码:

    public class MapActivity extends AppCompatActivity {
    
        private LocationClient mLocationClient;
    
        private MapView mapView;
    
        private BaiduMap baiduMap;
    
        private boolean isFirstLocate = true;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            mLocationClient = new LocationClient(getApplicationContext());
            mLocationClient.registerLocationListener(new MyLocationListener());
            SDKInitializer.initialize(getApplicationContext());
            setContentView(R.layout.activity_map);
    
            mapView = (MapView) findViewById(R.id.map_view);
            baiduMap = mapView.getMap();
            baiduMap.setMyLocationEnabled(true);
    
            // 声明权限,将权限添加到list集合中再一次性申请
            List<String> permissionList = new ArrayList<>();
            if (ActivityCompat.checkSelfPermission(MapActivity.this,
                    Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                permissionList.add(Manifest.permission.ACCESS_FINE_LOCATION);
            }
            if (ActivityCompat.checkSelfPermission(MapActivity.this,
                    Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
                permissionList.add(Manifest.permission.READ_PHONE_STATE);
            }
            if (ActivityCompat.checkSelfPermission(MapActivity.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(MapActivity.this,permissions,1);
            }else {
                requestLocation();
            }
        }
    
        /**
         * 开始地理位置定位
         */
        private void requestLocation() {
            initLocation();
            mLocationClient.start();
        }
    
        private void initLocation() {
            LocationClientOption option = new LocationClientOption();
            option.setScanSpan(5000);  //5秒钟更新下当前位置
            option.setIsNeedAddress(true);
            mLocationClient.setLocOption(option);
        }
    
        @Override
        public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                               @NonNull int[] grantResults) {
            switch (requestCode){
                case 1:
                    if (grantResults.length > 0 ){
                        for (int result : grantResults){
                            if (result != PackageManager.PERMISSION_GRANTED){
                                ToastUtils.showShort("必须同意所有权限才能使用本程序");
                                finish();
                                return;
                            }
                        }
                        requestLocation();
                    }else {
                        ToastUtils.showShort("发生未知错误");
                        finish();
                    }
                    break;
    
                default:
                    break;
            }
        }
    
        /**
         * 把地图移动到当前位置
         * @param location
         */
        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);
        }
    
        // 监听器
        public class MyLocationListener implements BDLocationListener{
    
            @Override
            public void onReceiveLocation(BDLocation bdLocation) {
                if (bdLocation.getLocType() == BDLocation.TypeGpsLocation ||
                        bdLocation.getLocType() == BDLocation.TypeNetWorkLocation){
                    navigateTo(bdLocation);
                }
            }
        }
    
        @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);
        }
    }
    

      关于百度 LBS SDK 的用法入门就介绍到这,更多用法参考官方网站:http://lbsyun.baidu.com/ 。建议根据官网开发指南来进行学习。

    相关文章

      网友评论

      • 小东方不败:为什么我的程序总是不能运行
      • 77f996dd4d69:在Fragment中怎么用
      • 努力生活的西鱼:你有没有遇到过定位后显示特别慢的问题,还有闪退的问题?
        开心wonderful:定位慢可能跟网络有关系吧,我测试时还好,定位方式是网络。至于闪退倒没什么碰到,之前有用过高德地图,碰到闪退是因为API Key没配置好,貌似每个应用都要申请一个对应的API Key

      本文标题:第一行代码读书笔记 11-- 基于位置的服务

      本文链接:https://www.haomeiwen.com/subject/qqcabttx.html