定位都得集成第三方?Android原生定位服务LocationManager不行吗?

定位都得集成第三方?Android原生定位服务LocationManager不行吗?现在的应用,几乎每一个 App 都存在定位的逻辑,方便更好的推荐产品或服务,获取当前设备的经纬度是必备的功能了。有些 App 还是以LBS(基于位置服务)为基础来实现的,比如美团,饿了吗,不获取到位置

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第19天,点击查看活动详情

前言

现在的应用,几乎每一个 App 都存在定位的逻辑,方便更好的推荐产品或服务,获取当前设备的经纬度是必备的功能了。有些 App 还是以LBS(基于位置服务)为基础来实现的,比如美团,饿了吗,不获取到位置都无法使用的。

有些同学觉得不就是获取到经纬度么,Android 自带的就有位置服务 LocationManager ,我们无需引入第三方服务,就可以很方便的实现定位逻辑。

确实 LocationManager 的使用很简单,获取经纬度很方便,我们就无需第三方的服务了吗? 或者说 LocationManager 有没有坑呢?兼容性问题怎么样?获取不到位置有没有什么兜底策略?

一、LocationManager的使用

由于是Android的系统服务,直接 getSystemService 可以获取到

LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);

一般获取位置有两种方式 NetWork 与 GPS 。我们可以指定方式,也可以让系统自动提供最好的方式。

// 获取所有可用的位置提供器
List<String> providerList = locationManager.getProviders(true);
// 可以指定优先GPS,再次网络定位
if (providerList.contains(LocationManager.GPS_PROVIDER)) {
    provider = LocationManager.GPS_PROVIDER;
} else if (providerList.contains(LocationManager.NETWORK_PROVIDER)) {
    provider = LocationManager.NETWORK_PROVIDER;
} else {
    // 当没有可用的位置提供器时,弹出Toast提示用户
    return;
}

当然我更推荐由系统提供,当我的设备在室内的时候就会以网络的定位提供,当设备在室外的时候就可以提供GPS定位。

 String provider = locationManager.getBestProvider(criteria, true);

我们可以实现一个定位的Service实现这个逻辑

/** * 获取定位服务 */
public class LocationService extends Service {

    private LocationManager lm;
    private MyLocationListener listener;

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @SuppressLint("MissingPermission")
    @Override
    public void onCreate() {
        super.onCreate();

        lm = (LocationManager) getSystemService(LOCATION_SERVICE);
        listener = new MyLocationListener();

        Criteria criteria = new Criteria();
        criteria.setAccuracy(Criteria.ACCURACY_COARSE);
        criteria.setAltitudeRequired(false);//不要求海拔
        criteria.setBearingRequired(false);//不要求方位
        criteria.setCostAllowed(true);//允许有花费
        criteria.setPowerRequirement(Criteria.POWER_LOW);//低功耗

        String provider = lm.getBestProvider(criteria, true);

        YYLogUtils.w("定位的provider:" + provider);

        Location location = lm.getLastKnownLocation(provider);

        YYLogUtils.w("location-" + location);

        if (location != null) {
            //不为空,显示地理位置经纬度
            String longitude = "Longitude:" + location.getLongitude();
            String latitude = "Latitude:" + location.getLatitude();

            YYLogUtils.w("getLastKnownLocation:" + longitude + "-" + latitude);

            stopSelf();

        }

        //第二个参数是间隔时间 第三个参数是间隔多少距离,这里我试过了不同的各种组合,能获取到位置就是能,不能获取就是不能
        lm.requestLocationUpdates(provider, 3000, 10, listener);
    }

    class MyLocationListener implements LocationListener {
        // 位置改变时获取经纬度
        @Override
        public void onLocationChanged(Location location) {

            String longitude = "Longitude:" + location.getLongitude();
            String latitude = "Latitude:" + location.getLatitude();

            YYLogUtils.w("onLocationChanged:" + longitude + "-" + latitude);


            stopSelf();  // 获取到经纬度以后,停止该service
        }

        // 状态改变时
        @Override
        public void onStatusChanged(String provider, int status, Bundle extras) {
            YYLogUtils.w("onStatusChanged - provider:"+provider +" status:"+status);
        }

        // 提供者可以使用时
        @Override
        public void onProviderEnabled(String provider) {
            YYLogUtils.w("GPS开启了");
        }

        // 提供者不可以使用时
        @Override
        public void onProviderDisabled(String provider) {
            YYLogUtils.w("GPS关闭了");
        }

    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        lm.removeUpdates(listener); // 停止所有的定位服务
    }

}

使用:定义并动态申请权限之后即可开启服务


    fun testLocation() {

        extRequestPermission(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION) {

        startService(Intent(mActivity, LocationService::class.java))

        }

    }

这样我们启动这个服务就可以获取到当前的经纬度,只是获取一次,大家如果想再后台持续定位,那么实现的方式就不同了,我们服务要设置为前台服务,并且需要额外申请后台定位权限。

话说回来,这么使用就一定能获取到经纬度吗?有没有兼容性问题

Android 5.0 Oppo

定位都得集成第三方?Android原生定位服务LocationManager不行吗?

Android 6.0 Oppo海外版

定位都得集成第三方?Android原生定位服务LocationManager不行吗?

Android 7.0 华为

定位都得集成第三方?Android原生定位服务LocationManager不行吗?

Android 11 三星海外版

定位都得集成第三方?Android原生定位服务LocationManager不行吗?

Android 12 vivo

定位都得集成第三方?Android原生定位服务LocationManager不行吗?

目前测试不多,也能发现问题,特别是一些低版本,老系统的手机就可能无法获取位置,应该是系统的问题,这种服务跟网络没关系,开不开代理都是一样的。

并且随着测试系统的变高,越来越完善,提供的最好定位方式还出现混合定位 fused 的选项。

那是不是6.0的Oppo手机太老了,不支持定位了?并不是,百度定位可以获取到位置的。

定位都得集成第三方?Android原生定位服务LocationManager不行吗?

既然只使用 LocationManager 有风险,有可能无法获取到位置,那怎么办?

二、混合定位

其实目前百度,高度的定位Api的服务SDK也不算大,相比地图导航等比较重的功能,定位的SDK很小了,并且目前都支持海外的定位服务。并且定位服务是免费的哦。

既然 LocationManager 有可能获取不到位置,那我们就加入第三方定位服务,比如百度定位。我们同时使用 LocationManager 和百度定位,哪个先成功就用哪一个。(如果LocationManager可用的话,它的定位比百度定位更快的)

完整代码如下:

@SuppressLint("MissingPermission")
public class LocationService extends Service {

    private LocationManager lm;
    private MyLocationListener listener;
    private LocationClient mBDLocationClient = null;
    private MyBDLocationListener mBDLocationListener;

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();

        createNativeLocation();

        createBDLocation();
    }

    /** * 第三方百度定位服务 */
    private void createBDLocation() {
        mBDLocationClient = new LocationClient(UIUtils.getContext());
        mBDLocationListener = new MyBDLocationListener();
        //声明LocationClient类
        mBDLocationClient.registerLocationListener(mBDLocationListener);
        //配置百度定位的选项
        LocationClientOption option = new LocationClientOption();
        option.setLocationMode(LocationClientOption.LocationMode.Battery_Saving);
        option.setCoorType("WGS84");
        option.setScanSpan(10000);
        option.setIsNeedAddress(true);
        option.setOpenGps(true);
        option.SetIgnoreCacheException(false);
        option.setWifiCacheTimeOut(5 * 60 * 1000);
        option.setEnableSimulateGps(false);
        mBDLocationClient.setLocOption(option);
        //开启百度定位
        mBDLocationClient.start();
    }

    /** * 原生的定位服务 */
    private void createNativeLocation() {

        lm = (LocationManager) getSystemService(LOCATION_SERVICE);
        listener = new MyLocationListener();

        Criteria criteria = new Criteria();
        criteria.setAccuracy(Criteria.ACCURACY_COARSE);
        criteria.setAltitudeRequired(false);//不要求海拔
        criteria.setBearingRequired(false);//不要求方位
        criteria.setCostAllowed(true);//允许有花费
        criteria.setPowerRequirement(Criteria.POWER_LOW);//低功耗

        String provider = lm.getBestProvider(criteria, true);

        YYLogUtils.w("定位的provider:" + provider);

        Location location = lm.getLastKnownLocation(provider);

        YYLogUtils.w("location-" + location);

        if (location != null) {
            //不为空,显示地理位置经纬度
            String longitude = "Longitude:" + location.getLongitude();
            String latitude = "Latitude:" + location.getLatitude();

            YYLogUtils.w("getLastKnownLocation:" + longitude + "-" + latitude);

            stopSelf();

        }

        lm.requestLocationUpdates(provider, 3000, 10, listener);
    }

    class MyLocationListener implements LocationListener {
        // 位置改变时获取经纬度
        @Override
        public void onLocationChanged(Location location) {

            String longitude = "Longitude:" + location.getLongitude();
            String latitude = "Latitude:" + location.getLatitude();

            YYLogUtils.w("onLocationChanged:" + longitude + "-" + latitude);


            stopSelf();  // 获取到经纬度以后,停止该service
        }

        // 状态改变时
        @Override
        public void onStatusChanged(String provider, int status, Bundle extras) {
            YYLogUtils.w("onStatusChanged - provider:" + provider + " status:" + status);
        }

        // 提供者可以使用时
        @Override
        public void onProviderEnabled(String provider) {
            YYLogUtils.w("GPS开启了");
        }

        // 提供者不可以使用时
        @Override
        public void onProviderDisabled(String provider) {
            YYLogUtils.w("GPS关闭了");
        }

    }


    /** * 百度定位的监听 */
    class MyBDLocationListener extends BDAbstractLocationListener {

        @Override
        public void onReceiveLocation(BDLocation location) {

            double latitude = location.getLatitude();    //获取纬度信息
            double longitude = location.getLongitude();    //获取经度信息


            YYLogUtils.w("百度的监听 latitude:" + latitude);
            YYLogUtils.w("百度的监听 longitude:" + longitude);

            YYLogUtils.w("onBaiduLocationChanged:" + longitude + "-" + latitude);

            stopSelf();  // 获取到经纬度以后,停止该service
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        // 停止所有的定位服务
        lm.removeUpdates(listener);

        mBDLocationClient.stop();
        mBDLocationClient.unregisterLocationListener(mBDLocationListener);
    }

}

其实逻辑都是很简单的,并且省略了不少回调通信的逻辑,这里只涉及到定位的逻辑,别的逻辑我就尽量不涉及到。

百度定位服务的API申请与初始化请自行完善,这里只是简单的使用。并且坐标系统一为国际坐标,如果需要转gcj02的坐标系,可以网上找个工具类,或者看我之前的文章

获取到位置之后,如何Service与Activity通信,就由大家自由发挥了,有兴趣的可以看我之前的文章

总结

所以说Android原生定位服务 LocationManager 还是有问题啊,低版本的设备可能不行,高版本的Android系统又很行,兼容性有问题!让人又爱又恨。

很羡慕iOS的定位服务,真的好用,我们 Android 的定位服务真是拉跨,居然还有兼容性问题。

我们使用第三方定位服务和自己的 LocationManager 并发获取位置,这样可以增加容错率。是比较好用的,为什么要加上 LocationManager 呢?我直接单独用第三方的定位服务不香吗?可以是可以,但是如果设备支持 LocationManager 的话,它会更快一点,体验更好。

好了,我如有讲解不到位或错漏的地方,希望同学们可以指出交流。

如果感觉本文对你有一点点点的启发,还望你能点赞支持一下,你的支持是我最大的动力。

Ok,这一期就此完结。

定位都得集成第三方?Android原生定位服务LocationManager不行吗?

今天的文章定位都得集成第三方?Android原生定位服务LocationManager不行吗?分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/13559.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注