Хороший способ получить местоположение пользователя в Android - PullRequest
210 голосов
/ 31 мая 2011

Проблема:

Получение текущего местоположения пользователя в пределах порога как можно скорее и в то же время экономит заряд батареи.

Почему проблема - проблема:

Во-первых, у Android есть два провайдера; сеть и GPS. Иногда сеть лучше, а иногда лучше GPS.

Под «лучше» я подразумеваю соотношение скорости и точности.
Я готов пожертвовать точностью в несколько метров, если смогу определить местоположение практически мгновенно и без включения GPS.

Во-вторых, если вы запрашиваете обновления для изменений местоположения, ничего не отправляется, если текущее местоположение стабильно.

У Google есть пример определения «лучшего» местоположения здесь: http://developer.android.com/guide/topics/location/obtaining-user-location.html#BestEstimate
Но я думаю, что это не так хорошо, как должно / могло бы быть.

Я немного сбит с толку, почему у Google нет нормализованного API для определения местоположения, разработчику не нужно заботиться о том, откуда оно, вы просто должны указать, что вы хотите, и телефон должен выбрать именно для вас.

В чем мне нужна помощь:

Мне нужно найти хороший способ определить «лучшее» местоположение, возможно, через какое-то эвристическое или, возможно, через какую-нибудь стороннюю библиотеку.

Это не значит, определить лучшего поставщика!
Я, вероятно, собираюсь использовать всех провайдеров и выбирать лучших из них.

Фон приложения:

Приложение будет собирать местоположение пользователя с фиксированным интервалом (скажем, каждые 10 минут или около того) и отправлять его на сервер.
Приложение должно сохранять как можно больше батареи, а местоположение должно иметь точность X (50–100?) Метров.

Цель состоит в том, чтобы позже иметь возможность прокладывать путь пользователя в течение дня на карте, поэтому мне нужна достаточная точность для этого.

Разное:

Как вы думаете, являются ли разумные значения желаемой и принятой точности?
Я использовал 100 м как принято и 30 м как хотелось бы, это много, чтобы спросить?
Я хотел бы иметь возможность нанести путь пользователя на карту позже.
100 м для желаемого и 500 м для принятого лучше?

Кроме того, сейчас у меня включен GPS максимум на 60 секунд для каждого обновления местоположения, это слишком мало, чтобы получить местоположение, если вы находитесь в помещении с точностью, может быть, 200 м?


Это мой текущий код, любая обратная связь приветствуется (за исключением отсутствия проверки ошибок, которая является TODO):

protected void runTask() {
    final LocationManager locationManager = (LocationManager) context
            .getSystemService(Context.LOCATION_SERVICE);
    updateBestLocation(locationManager
            .getLastKnownLocation(LocationManager.GPS_PROVIDER));
    updateBestLocation(locationManager
            .getLastKnownLocation(LocationManager.NETWORK_PROVIDER));
    if (getLocationQuality(bestLocation) != LocationQuality.GOOD) {
        Looper.prepare();
        setLooper(Looper.myLooper());
        // Define a listener that responds to location updates
        LocationListener locationListener = new LocationListener() {

            public void onLocationChanged(Location location) {
                updateBestLocation(location);
                if (getLocationQuality(bestLocation) != LocationQuality.GOOD)
                    return;
                // We're done
                Looper l = getLooper();
                if (l != null) l.quit();
            }

            public void onProviderEnabled(String provider) {}

            public void onProviderDisabled(String provider) {}

            public void onStatusChanged(String provider, int status,
                    Bundle extras) {
                // TODO Auto-generated method stub
                Log.i("LocationCollector", "Fail");
                Looper l = getLooper();
                if (l != null) l.quit();
            }
        };
        // Register the listener with the Location Manager to receive
        // location updates
        locationManager.requestLocationUpdates(
                LocationManager.GPS_PROVIDER, 1000, 1, locationListener,
                Looper.myLooper());
        locationManager.requestLocationUpdates(
                LocationManager.NETWORK_PROVIDER, 1000, 1,
                locationListener, Looper.myLooper());
        Timer t = new Timer();
        t.schedule(new TimerTask() {

            @Override
            public void run() {
                Looper l = getLooper();
                if (l != null) l.quit();
                // Log.i("LocationCollector",
                // "Stopping collector due to timeout");
            }
        }, MAX_POLLING_TIME);
        Looper.loop();
        t.cancel();
        locationManager.removeUpdates(locationListener);
        setLooper(null);
    }
    if (getLocationQuality(bestLocation) != LocationQuality.BAD) 
        sendUpdate(locationToString(bestLocation));
    else Log.w("LocationCollector", "Failed to get a location");
}

private enum LocationQuality {
    BAD, ACCEPTED, GOOD;

    public String toString() {
        if (this == GOOD) return "Good";
        else if (this == ACCEPTED) return "Accepted";
        else return "Bad";
    }
}

private LocationQuality getLocationQuality(Location location) {
    if (location == null) return LocationQuality.BAD;
    if (!location.hasAccuracy()) return LocationQuality.BAD;
    long currentTime = System.currentTimeMillis();
    if (currentTime - location.getTime() < MAX_AGE
            && location.getAccuracy() <= GOOD_ACCURACY)
        return LocationQuality.GOOD;
    if (location.getAccuracy() <= ACCEPTED_ACCURACY)
        return LocationQuality.ACCEPTED;
    return LocationQuality.BAD;
}

private synchronized void updateBestLocation(Location location) {
    bestLocation = getBestLocation(location, bestLocation);
}

// Pretty much an unmodified version of googles example
protected Location getBestLocation(Location location,
        Location currentBestLocation) {
    if (currentBestLocation == null) {
        // A new location is always better than no location
        return location;
    }
    if (location == null) return currentBestLocation;
    // Check whether the new location fix is newer or older
    long timeDelta = location.getTime() - currentBestLocation.getTime();
    boolean isSignificantlyNewer = timeDelta > TWO_MINUTES;
    boolean isSignificantlyOlder = timeDelta < -TWO_MINUTES;
    boolean isNewer = timeDelta > 0;
    // If it's been more than two minutes since the current location, use
    // the new location
    // because the user has likely moved
    if (isSignificantlyNewer) {
        return location;
        // If the new location is more than two minutes older, it must be
        // worse
    } else if (isSignificantlyOlder) {
        return currentBestLocation;
    }
    // Check whether the new location fix is more or less accurate
    int accuracyDelta = (int) (location.getAccuracy() - currentBestLocation
            .getAccuracy());
    boolean isLessAccurate = accuracyDelta > 0;
    boolean isMoreAccurate = accuracyDelta < 0;
    boolean isSignificantlyLessAccurate = accuracyDelta > 200;
    // Check if the old and new location are from the same provider
    boolean isFromSameProvider = isSameProvider(location.getProvider(),
            currentBestLocation.getProvider());
    // Determine location quality using a combination of timeliness and
    // accuracy
    if (isMoreAccurate) {
        return location;
    } else if (isNewer && !isLessAccurate) {
        return location;
    } else if (isNewer && !isSignificantlyLessAccurate
            && isFromSameProvider) {
        return location;
    }
    return bestLocation;
}

/** Checks whether two providers are the same */
private boolean isSameProvider(String provider1, String provider2) {
    if (provider1 == null) {
        return provider2 == null;
    }
    return provider1.equals(provider2);
}

Ответы [ 12 ]

0 голосов
/ 09 июня 2011

По своему опыту, я нашел, что лучше всего использовать исправление GPS, если оно недоступно. Я не знаю много о других провайдерах определения местоположения, но я знаю, что для GPS есть несколько уловок, которые можно использовать, чтобы дать немного гетто точности измерения. Высота над уровнем моря часто является признаком, поэтому вы можете проверить смешные значения. Существует исправление на точность определения местоположения Android. Также, если вы видите количество используемых спутников, это также может указывать на точность.

Интересный способ получить лучшее представление о точности может заключаться в том, чтобы очень быстро запросить набор исправлений, например ~ 1 / сек, в течение 10 секунд, а затем поспать минуту или две. Один разговор, на котором я был, привел к убеждению, что некоторые Android-устройства будут делать это в любом случае. Затем вы должны отсеять выбросы (я слышал, что фильтр Калмана упоминался здесь) и использовать некую стратегию центрирования, чтобы получить одно исправление.

Очевидно, что глубина, к которой вы попадаете, зависит от того, насколько сложны ваши требования. Если вы предъявляете особенно строгие требования к получению ЛУЧШЕГО местоположения, я думаю, вы обнаружите, что GPS и сетевое местоположение такие же, как яблоки и апельсины. Также GPS может сильно отличаться от устройства к устройству.

0 голосов
/ 09 июня 2011

Skyhook (http://www.skyhookwireless.com/) имеет провайдера местоположения, который намного быстрее, чем стандартный, предоставляемый Google. Это может быть то, что вы ищете. Я не связан с ними.

...