繁体   English   中英

在某些设备上无法检索最近的已知位置和当前位置

[英]Last known location and current location cannot be retrieved on some devices

我们已经启动了一个使用GPS信息的Android应用,并且我们的少数用户报告GPS不适用于他们。 尽管我们投入了大量时间来改进代码库,但仍无法为受影响的用户提供相关的改进。 我们现在正在寻找社区中的新想法,以向前迈进并解决这一问题。 感谢您的提前反馈!

方法

在我们的Android应用中,我们通过三个顺序的步骤来确定用户的位置:

  1. 当前的GPS位置(GPS_PROVIDER)
  2. 当前网络位置(NETWORK_PROVIDER)
  3. 上次知道的GPS位置(GPS_PROVIDER)

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

问题

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

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

这是我们已有的代码。 该应用程序调用GeoRepository.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
  • Galaxy Note4

思想

  • 可能是用户只是离线导致了此问题? 通过使用Google Maps进行测试以及利用最后一个已知的位置,也不应该排除这种情况。
  • 难怪是设备设置引起的? 没有通过Google Maps测试排除的可能性。
  • 难道应该归咎于应用程序权限? 没有与用户澄清这一点。
  • 可能是我们的代码有错误吗? 也许可以,但是我们无法发现任何明显的缺陷。
  • 您尝试过其他替代提供商吗? 是的,我们也尝试了较新的Fused Location Provider API,并且发生了同样的问题。

有任何想法吗? 谢谢你的帮助!

LastKnownLocation几乎总是失败。 仅当位置服务之前由另一个应用程序(或您自己的应用程序)最近打开足够快以使该值被缓存时,它才起作用。 手机不会一直主动跟踪位置。 调用它的99%的时间中,您将返回null。 因此,如果1和2失败,则3正常工作的可能性为零。

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

  • 您是否遵循Android 8的GPS请求准则, 请参见此链接
  • 省电模式已关闭?
  • 目标设备是否已使用当前的Google支持库更新?

也增加了您请求位置更新的次数,在某些设备中,自第一次或两次位置数据为空以来,我就遇到了这种情况。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM