美文网首页Location
Android地理位置这篇就够啦!

Android地理位置这篇就够啦!

作者: dev晴天 | 来源:发表于2017-09-25 11:14 被阅读0次

    Android 系统提供了地理位置服务相关的API,方便了开发者去获得当前地理位置。用户可以通过GPS接收器接受地理位置。也可以通过network(wifi或者基站)来获得当前的定位。

    知识点

    image.png

    一、梳理

    1、谁提供的定位信息?

    安卓的位置信息由“位置提供者”提供,在安卓中有三种位置提供者即:gps、network、passive。前两者我们或许还可以理解但是这个passive是个啥东西?客官别急,,,,这就开始分析!
    1、gps:gps提供者就是使用手机内置的gps硬件接收器接受精确的位置信息。
    2、network:就是使用网络作为位置提供者,这里网络可以为wifi、基站。因为wifi、基站也是可以提供粗略的定位的。
    3、psssive:这个提供者有点特别。从这个单词来看是“被动的”意思。其实这个就是个被动接收者。怎么理解呢?你可以这样理解:当其他的app、或者服务(service)请求到位置信息时,这个接收者可以接受到他们请求到的位置信息。看到这里我们就明白了。也可以知道使用这个提供者时获取到的位置并不一定是正确的。

    2、安卓Location框架的架构

    如下图(图片来自网络,如有侵权联系作者删除,,,,)开发者主要使用LocationManager来获得位置信息。然而位置信息的获得是个跨进程通信的过程。真正的位置信息的获取、位置提供者的查询、位置的实时监听等都是由系统的LocationManagerService来负责接手的。
    1、如果用户是使用了gps定位方式,那么LocationManagerService就委托给GpsLocationProvider去获得精确位置信息。
    2、如果用户使用的network 定位方式,LocationManagerService就委托给第三方实现去获得粗略的位置信息。
    ps:google的api中并未实现netWorkProvider的具体逻辑,这个功能交给了第三方服务去完成。国内手机一般都集成了百度地图、或者高德地图。这两个第三方的app中就有一个service,就是netWorkProvider的具体实现。这里又涉及到了 安卓系统位置服务于第三方app的位置服务跨进程通信。

    image.png

    二、常用类

    1、LocationManager

    位置管理者,应用层(开发者)主要使用这个类和系统的位置服务进行通信。

    (1)获取方式

     LocationManager locationManager =
           (LocationManager) getSystemService(LOCATION_SERVICE);
    

    (2)常用方法或字段

    // 常用的静态常量
    GPS_PROVIDER // gps 提供者
    NETWORK_PROVIDER // network提供者
    PASSIVE_PROVIDER // passive提供者(需要)
    
    // 返回手机支持的所有提供者(上述三种:gps、network、passive)
    public List<String> getAllProviders()  
    
    //根据筛选条件返回最符合条件的位置提供者,一般筛选最符合条件能用的提供者。
    public String getBestProvider(Criteria criteria, boolean enabledOnly)
    
    // 一般参数为true,返回能用的提供者集合
    List<String> getProviders(boolean enabledOnly)
    
    // 获得最近一次使用过的Location信息对象,最近没有使用过时返回null。一般此方法不用。
    public Location getLastKnownLocation(String provider);
    
    // 注册,需要位置的实时更新
    //provider:位置提供者,表名位置数据由那种提供者提供。
    //minTime:最小时间间隔,位置刷新期间的。多久刷新一次。
    //minDistance:最小的位置,位置刷新期间。超过这个位置也触发刷新。
    // LocationListener :位置监听
    void requestLocationUpdates(String provider, long minTime, float minDistance,
                LocationListener listener)
    
    // 解注册
    public void removeUpdates(LocationListener listener)
    
    
    2、Location:

    位置类,内部封装了经纬度、海拔之类的位置信息。

                    location.getLongitude();//经度
                    location.getLatitude();// 纬度
                    location.getProvider();//位置提供者
    
    3、Geocoder:将地址转换为相应的地理坐标。这一过程称为地理编码。或者,您可以将地理位置转换为地址。地址查询功能也称为逆向地理编码。
    image.png
    maxResults:返回地址的数目取1~5之间
    因为同一经纬度可能对应多个地址
    public List<Address> getFromLocation(double latitude, double longitude, int maxResults)
    
    

    1、可见提供了三个方法,常用的为第一个。根据经纬度获取Address集合。
    2、Geocoder 请求的是一个后台服务,但是该服务不包括在标准android framework中。因此如果当前设备不包含location services,则Geocoder返回的地址或者经纬度为空。当然你可以使用 Geocoder#isPresent()方法来判断当前设备是否包含地理位置服务。由于国内使用不了Google Services服务,因此一般的手机厂商都会在自己的手机内内置百度地图服务,或者高德地图服务来替代Google Services服务。

    4、Address:详细的地理位置

    一般通过如下api我们即可获得 XXX市XXX区XXX街区XXX小区信息。

    image.png

    三、权限&api请求流程

    1、动态权限
     <!--   GPS定位权限 -->
        <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
        <!--    wifi/基站定位权限-->
        <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    
        <uses-permission android:name="android.permission.INTERNET" />
    

    安卓6.0开始,系统对危险权限添加了动态权限申请的功能。上述的位置权限在6.0或者以上的手机中需要动态申请下。
    参考文章:安卓动态权限

    2、大致使用流程图解
    流程.png

    四 、栗子及其Criteria类的补充

    1、Criteria
    String bestProvider = locationManager.getBestProvider(new Criteria(), true);
    

    细心的你可能会留意到,上述api简介中,获得provider时我们还会用到Criteria类。其实这个类就是筛选合适的provider的。常见的筛选条件如下:

    public void setAccuracy(int accuracy):位置解析精确度,参数Criteria.ACCURACY_FINE:表示高精确度。Criteria.ACCURACY_COARSE:表示模糊精确度。
    public void setAltitudeRequired(boolean altitudeRequired ):是否要求海拔信息。
    public void setBearingRequired(boolean bearingRequired):是否要求方向信息。
    public void setCostAllowed(boolean costAllowed):是否允许收费。
    public void setSpeedRequired(boolean speedRequired):是否要求速度信息。
    public void setBearingRequired(boolean bearingRequired):是否要求方向信息。
    public void setPowerRequirement(int level):设置电池消耗要求,参数 Criteria. NO_REQUIREMENT, Criteria. POWER_LOW, Criteria. POWER_MEDIUM, Criteria. POWER_HIGH。分别表示 :无、低、中、高,。
    public void setBearingAccuracy(int accuracy):设置方向的精准度。参数 Criteria.NO_REQUIREMENT, Criteria.ACCURACY_LOW, Criteria.ACCURACY_HIGH。分别表示:无,低,高。
    public void setSpeedAccuracy(int accuracy):设置速度的精准度。参数同上。
    public void setHorizontalAccuracy(int accuracy):设置水平方向的精准度。参数同上。
    public void setVerticalAccuracy(int accuracy):设置垂直方向的精准度。参数同上。
    

    Criteria类该怎么使用?代码示例如下:

            criteria.setAccuracy(Criteria.ACCURACY_FINE);//设置定位精准度
            criteria.setAltitudeRequired(false);//是否要求海拔
            criteria.setBearingRequired(true);//是否要求方向
            criteria.setCostAllowed(true);//是否要求收费
            criteria.setSpeedRequired(true);//是否要求速度
            criteria.setPowerRequirement(Criteria.NO_REQUIREMENT);//设置电池耗电要求
            criteria.setBearingAccuracy(Criteria.ACCURACY_HIGH);//设置方向精确度
            criteria.setSpeedAccuracy(Criteria.ACCURACY_HIGH);//设置速度精确度
            criteria.setHorizontalAccuracy(Criteria.ACCURACY_HIGH);//设置水平方向精确度
            criteria.setVerticalAccuracy(Criteria.ACCURACY_HIGH);//设置垂直方向精确度
    
            //返回满足条件的,当前设备可用的location provider,当第二个参数为false时,返回当前设备所有provider中最符合条件的那个provider(但是不一定可用)。
            String mProvider  = mLocationManager.getBestProvider(criteria,true);
    
    2、栗子
    public class MainActivity extends AppCompatActivity {
        private TextView tvLocation;
        private TextView tvAddress;
        private Geocoder geocoder;
        private List<Address> addressList;
        private StringBuilder sb;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            initUi();
            initData();
        }
    
        private void initData() {
            // 获取经纬度坐标
            // 1 获取位置管理者对象
            LocationManager lm = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
            // 2 通过lm获得经纬调度坐标
            // (参数: provider(定位方式 提供者 通过 LocationManager静态调用),
            // minTime(获取经纬度间隔的最小时间 时时刻刻获得传参数0),
            // minDistance(移动的最小间距 时时刻刻传0),LocationListener(监听))
    
            if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                // TODO: Consider calling
                //    ActivityCompat#requestPermissions
                // here to request the missing permissions, and then overriding
                //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
                //                                          int[] grantResults)
                // to handle the case where the user grants the permission. See the documentation
                // for ActivityCompat#requestPermissions for more details.
                return;
            }
            lm.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, new LocationListener() {
                @Override
                public void onLocationChanged(Location location) {
                    // 获取经纬度主要方法
                    double latitude = location.getLatitude();
                    double longitude = location.getLongitude();
                    tvLocation.setText("latitude"+latitude+"  "+"longitude"+longitude);
                    sb = new StringBuilder();
                    geocoder = new Geocoder(MainActivity.this);
                    addressList = new ArrayList<Address>();
    
                    try {
                        // 返回集合对象泛型address
                     addressList= geocoder.getFromLocation(latitude,longitude,1);
    
    
                        if (addressList.size() > 0) {
                            Address address = addressList.get(0);
                            for (int i = 0; i < address.getMaxAddressLineIndex(); i++) {
                                sb.append(address.getAddressLine(i)).append("\n");
                            }
                            sb.append(address.getFeatureName());//周边地址
                        }
                        tvAddress.setText("当前位置"+sb.toString());
                    ;
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
    
    
                }
    
                @Override
                public void onStatusChanged(String s, int i, Bundle bundle) {
                    //状态发生改变监听
                }
    
                @Override
                public void onProviderEnabled(String s) {
                    // ProviderEnabled
                }
    
                @Override
                public void onProviderDisabled(String s) {
                    // ProviderDisabled
                }
            });
        }
    
        private void initUi() {
            tvLocation = (TextView) findViewById(R.id.tv_location);
            tvAddress = (TextView) findViewById(R.id.tv_address);
        }
    }
    

    贴图如下

    image.png

    五、扩展及其采坑

    1、扩展

    安卓手机从屏幕上端下滑,拉出功能栏,点击GPS图标开启GPS,这时你的手机设置->位置信息模式 会选中“高精确度”定位方式,当你点击GPS图标关闭时,会使用默认的“低耗电量”(如下图)

    Screenshot_2020-03-26-17-51-43-750_com.android.se.jpg
    2、采坑network 提供者下onLocationChange不回调,拿不到Location对象。

    1、场景:
    安卓TV开发的同学或许会碰到这种情况,TV真机上onLocationChange没有回调。拿不到Location对象。
    2、解释:
    其实TV不支持gps硬件功能,如果想采用定位那只有使用network提供者了,但是通过上述的架构图我们也可以知道。提供者为network时,位置的获取需要第三方服务。由于TV上一般都没有地图类app所以第三方就是空实现了。location对象就拿不到了。
    3、验证依据:

    • 安卓系统开机时可以打印系统log(使用adb logcat 命令)
    • LocationManagerService的初始化在安卓系统类的SystemServer的startOtherService方法中完成。这个操作在安卓系统启动时就进行。

    4、进行验证:重启TV打印系统log(如下图找到你指定输出的log.txt文件),结果搜索LocationManagerService关键字发现“no network location provider found”,默认使用的gms(google map service)也没找到。
    5、一种解决方案:在tv上集成三方sdk比如高德地图。

    image.png

    参考:
    1、Android网络定位源码分析
    2、https://developer.android.google.cn/reference/android/location/LocationManager?hl=en

    相关文章

      网友评论

        本文标题:Android地理位置这篇就够啦!

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