代码之家  ›  专栏  ›  技术社区  ›  driAn

无法在某些设备上检索上一个已知位置和当前位置

  •  0
  • driAn  · 技术社区  · 6 年前

    我们已经推出了一款利用GPS信息的Android应用程序,而我们的少数用户报告说,GPS对他们不起作用。虽然我们已经投入了大量的时间来改进代码库,但是我们仍然无法为受影响的用户提供相关的改进。我们现在正在寻找新的想法,从社区向前迈进一步,解决这个问题。提前感谢您的反馈!

    在我们的Android应用程序中,我们通过三个连续步骤来识别用户的位置:

    1. 当前GPS位置(GPS提供程序)
    2. 当前网络位置(网络提供商)
    3. 最后一个已知的GPS位置(GPS提供程序)

    如果一个步骤失败,则尝试下一个步骤。如果没有任何步骤成功,则会向用户显示GPS错误消息。

    问题

    少数用户报告说 总是 接收GPS错误信息。我们与用户一起尝试了以下操作,但没有成功:

    • 确保设备上已启用GPS
    • 确保为应用程序授予GPS权限(细粒度位置)
    • 关闭应用程序,获取谷歌地图上的当前位置,然后重新启动应用程序

    代码

    这是我们的代码。应用程序调用地理位置.getCurrentLocation(..).

    public class GeoRepository {
    private static final String TAG = GeoRepository.class.getSimpleName();
    
    public GeoRepository() {
    }
    
    public class GeoNoPermissionException extends RuntimeException {
        public GeoNoPermissionException(String message, Throwable cause) {
            super(message, cause);
        }
    }
    public class GeoNoLocationException extends RuntimeException {
        public GeoNoLocationException(String message, Throwable cause) {
            super(message, cause);
        }
    }
    
    public interface GeoLocationListener {
        void onCompleted(Location location);
        void onFailed(RuntimeException ex);
    }
    
    public void getCurrentLocation(Context context, GeoLocationListener listener) {
        // The permissions for this is already being requested in the SplashActivity
        boolean hasPermission = !(ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
                && ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED);
        if (!hasPermission) {
            listener.onFailed(new GeoNoPermissionException("Missing permissions", null));
            return;
        }
    
        // try initially with precise GPS provider
        requestLocationOnce(context, LocationManager.GPS_PROVIDER, new GeoLocationListener() {
            @Override
            public void onCompleted(Location location) {
                listener.onCompleted(location);
            }
    
            @Override
            public void onFailed(RuntimeException ex) {
                Log.e(TAG, "Could not find location on first attempt", ex);
                requestLocationOnce(context, LocationManager.NETWORK_PROVIDER, new GeoLocationListener() {
                    @Override
                    public void onCompleted(Location location) {
                        listener.onCompleted(location);
                    }
    
                    @Override
                    public void onFailed(RuntimeException ex) {
                        Log.e(TAG, "Could not find location on second attempt", ex);
    
                        Location location = null;
                        try {
                            location = requestLastKnownLocation(context, LocationManager.GPS_PROVIDER);
                        } catch(RuntimeException ex2) {
                            Log.e(TAG, "Could not find location on third attempt", ex2); 
                            /* !!! this is where we hit the wall and return a GPS-errormessage to the user !!! */
                            listener.onFailed(ex);
                            return;
                        }
                        if(location == null) {
                            Log.e(TAG, "Could not find location on third attempt, location is null");
                            listener.onFailed(ex);
                            return;
                        }
    
                        listener.onCompleted(location);
                    }
                });
            }
        });
    }
    
    private void requestLocationOnce(Context context, String provider, GeoLocationListener listener) {
        // The permissions for this is already being requested in the SplashActivity
        boolean hasPermission = !(ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
                && ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED);
        if (!hasPermission) {
            listener.onFailed(new GeoNoPermissionException("Missing permissions", null));
            return;
        }
    
        Object syncObj = new Object();
        CountDownLatch completedSignal = new CountDownLatch(1);
    
        LocationManager locationManager = (LocationManager) FlourishApp.getContext().getSystemService(Context.LOCATION_SERVICE);
        LocationListener locationListener = new LocationListener() {
            @Override
            public void onLocationChanged(Location location) {
                // this method may never get called hence we need a watchdog later on
                // this happens for example if GPS permissions are there but GPS is turned off
                synchronized(syncObj) {
                    if (completedSignal.getCount() > 0) {
                        completedSignal.countDown();
                        listener.onCompleted(location);
                    }
                }
            }
    
            @Override
            public void onStatusChanged(String provider, int status, Bundle extras) {
            }
    
            @Override
            public void onProviderEnabled(String provider) {
            }
    
            @Override
            public void onProviderDisabled(String provider) {
            }
        };
    
        locationManager.requestSingleUpdate(
                provider,
                locationListener,
                Looper.getMainLooper());
    
        new Handler(Looper.getMainLooper()).postDelayed(() -> {
            // cleanup event subscription
            locationManager.removeUpdates(locationListener);
    
            // watchdog to raise error if we don't get a callback within reasonable time
            boolean receivedLocationUpdate = true;
            synchronized (syncObj) {
                if (completedSignal.getCount() > 0) {
                    completedSignal.countDown();
                    receivedLocationUpdate = false;
                }
            }
    
            if(!receivedLocationUpdate) {
                GeoNoLocationException ex = new GeoNoLocationException("No location could be determined", null);
                listener.onFailed(ex);
            }
        }, Constants.LOCATION_MAX_WAIT); // LOCATION_MAX_WAIT = 50000
    }
    
    private Location requestLastKnownLocation(Context context, String provider) {
        // The permissions for this is already being requested in the SplashActivity
        boolean hasPermission = !(ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
                && ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED);
        if (!hasPermission) {
            throw new GeoNoPermissionException("Missing permissions", null);
        }
    
        LocationManager locationManager = (LocationManager) FlourishApp.getContext().getSystemService(Context.LOCATION_SERVICE);
        return locationManager.getLastKnownLocation(provider);
    }
    
    public interface GetCountryCodeByCurrentLocationListener {
        void onCompleted(String countryCode);
        void onFailed(RuntimeException ex);
    }
    }
    

    设备

    受影响的用户一直在使用以下设备之一:

    • 华为P8
    • Oppo CPH1723公司
    • 银河笔记4

    思想

    • 会不会是因为用户只是离线才导致了这个问题?这不应该被排除在谷歌地图测试和利用最后已知的位置。
    • 应用程序权限是否应该受到指责?没有这一点已经向用户澄清。
    • 我们的代码有问题吗?也许,但我们无法确定任何明显的缺陷。
    • 你试过其他的供应商吗?是的,我们也尝试了新的融合位置提供者API,同样的问题也发生了。

    有什么想法吗?谢谢你的帮助!

    2 回复  |  直到 6 年前
        1
  •  -1
  •   Gabe Sechan    6 年前

    LastKnownLocation几乎总是失败。只有当位置服务最近被另一个应用程序(或你自己的)打开,以至于缓存了该值时,它才起作用。手机不会一直主动跟踪位置。99%的情况下,当你调用它时,你会得到null返回。因此,如果1和2失败,3成功的可能性为零。

        2
  •  -1
  •   Christlin Panneer    6 年前

    我不知道这件事的确切原因。请检查以下几点。

    • 您是否遵循Android 8的GPS请求指南 See this link
    • 节电模式关闭?

    同时增加您请求位置更新的次数,在某些设备中,我经历过这样的情况,因为第一次或第二次位置数据为空。