Подтвердить что ты не робот

Android: получите текущее местоположение от лучшего поставщика

У меня есть код Android, который должен получить лучшее доступное местоположение БЫСТРО, от GPS, сети или любого другого. Точность менее важна, чем скорость.

Получение наилучшего доступного местоположения - действительно стандартная задача. Тем не менее, я не могу найти код для его демонстрации. Код местоположения Android ожидает, что вы укажете критерии, зарегистрируйтесь для получения обновлений и подождите - это нормально, если у вас есть подробные критерии и вы не можете ждать.

Но мое приложение должно работать немного больше, как приложение Maps, когда оно сначала находит вас - работайте с любым доступным провайдером, и просто проверьте, что местоположение не дико устарело или нулевое.

Я попытался свернуть свой собственный код, чтобы сделать это, но у меня проблемы. (Это внутри IntentService, где происходит загрузка, если это имеет значение. Я включил весь код для информации.) Что не так с этим кодом?

@Override
protected void onHandleIntent(Intent arg0) {
    testProviders();
    doUpload();
}
private boolean doUpload() {
       int j = 0;
       // check if we have accurate location data yet - wait up to 30 seconds
       while (j < 30) {
           if ((latString == "") || (lonString == "")) {
               Log.d(LOG_TAG, "latlng null");
               Thread.sleep(1000);
               j++;
       } else {
                Log.d(LOG_TAG, "found lat " + latString + " and lon " + lonString);
            break;
       }
       //do the upload here anyway, with or without location data
       //[code removed for brevity]
}
public boolean testProviders() {
    Log.e(LOG_TAG, "testProviders");
    String location_context = Context.LOCATION_SERVICE;
    locationmanager = (LocationManager) getSystemService(location_context);
    List<String> providers = locationmanager.getProviders(true);
    for (String provider : providers) {
        Log.e(LOG_TAG, "registering provider " + provider);
        listener = new LocationListener() {
            public void onLocationChanged(Location location) {
                // keep checking the location - until we have
                // what we need
                //if (!checkLoc(location)) {
                Log.e(LOG_TAG, "onLocationChanged");
                locationDetermined = checkLoc(location);
                //}
            }
            public void onProviderDisabled(String provider) {
            }
            public void onProviderEnabled(String provider) {
            }
            public void onStatusChanged(String provider, int status,
                    Bundle extras) {
            }
        };
        locationmanager.requestLocationUpdates(provider, 0,
                0, listener);
    }
    Log.e(LOG_TAG, "getting updates");
    return true;
}
private boolean checkLoc(Location location) {
    float tempAccuracy = location.getAccuracy();
    int locAccuracy = (int) tempAccuracy;
    Log.d(LOG_TAG, "locAccuracy = " + locAccuracy);
    if ((locAccuracy != 0) && (locAccuracy < LOCATION_ACCURACY)) {
        latitude = location.getLatitude();
        longitude = location.getLongitude();
        latString = latitude.toString();
        lonString = longitude.toString();
        return true;
    }
    return false;
}
public void removeListeners() {
    // Log.e(LOG_TAG, "removeListeners");
    if ((locationmanager != null) && (listener != null)) {
        locationmanager.removeUpdates(listener);
    }
    locationmanager = null;
    // Log.d(LOG_TAG, "Removed " + listener.toString());
}
@Override
public void onDestroy() {
    super.onDestroy();
    removeListeners();
}

К сожалению, это находит сетевого провайдера, но только когда-либо выдает latlng null 30 раз - он, кажется, вообще не получает места. Я даже не получаю оператор журнала locationChanged.

Это смешно, потому что из ddms я вижу вывод вроде:

NetworkLocationProvider: onCellLocationChanged [305,8580]
NetworkLocationProvider: getNetworkLocation(): returning cache location with accuracy 75.0

похоже, что у провайдера сети есть какая-то информация о местоположении, я просто не понимаю.

Может ли кто-нибудь помочь? Я думаю, что рабочий пример кода будет полезным ресурсом для сообщества Android/StackOverflow.

4b9b3361

Ответ 1

Вы определенно пытаетесь сделать это с трудом. Вот некоторые фрагменты нового приложения, над которым я работаю. Он использует Criteria, чтобы все поставщики могли точно и точно получать точность без затрат.

Если ни один провайдер не включен, отображается диалоговое окно, в котором пользователь запрашивает параметры своего местоположения. Если пользователь нажимает ok, на самом деле умышленно запущен Intent, который отправляет их в настройки на своем телефоне. Если есть поставщики, приложение использует последнее последнее известное местоположение от любого из разрешенных поставщиков. Для моего приложения мне просто нужно знать, в какой общей области находится пользователь, и, вероятно, последнее известное местоположение находится из их родной области.

Если провайдеры включены, цикл также запрашивает обновления местоположения как можно быстрее. Это идеально подходит для моего приложения, но вы можете изменить это, чтобы сохранить батарею, модифицируя аргументы метода requestLocationUpdates.

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

Начать обновление местоположения:

void getCurrentLocation() {
    List<String> providers = locationManager.getProviders(criteria, true);
    if (providers != null) {
        Location newestLocation = null;
        for (String provider : providers) {
            Location location = locationManager.getLastKnownLocation(provider);
            if (location != null) {
                if (newestLocation == null) {
                    newestLocation = location;
                } else {
                    if (location.getTime() > newestLocation.getTime()) {
                        newestLocation = location;
                    }
                }
                locationManager.requestLocationUpdates(provider, 0, 0, this);
            }
        }
    } else {
        LocationDialogFragment dialog = new LocationDialogFragment();
        dialog.show(getSupportFragmentManager(),
            LocationDialogFragment.class.getName());
    }
}

Получить обновление местоположения:

@Override
public void onLocationChanged(Location location) {
    float bestAccuracy = -1f;
    if (location.getAccuracy() != 0.0f
        && (location.getAccuracy() < bestAccuracy) || bestAccuracy == -1f) {
        if (location.getAccuracy() < Const.MIN_ACCURACY) {
            locationManager.removeUpdates(this);
        }
    }
    bestAccuracy = location.getAccuracy();
}

Диалог настроек местоположения:

public class LocationDialogFragment extends DialogFragment {

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        builder.setMessage(R.string.location_dialog_message)
                .setPositiveButton(R.string.location_dialog_positive_button,
                    new OnClickListener() {

                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            Intent settingsIntent = new Intent(
                                    Settings.ACTION_LOCATION_SOURCE_SETTINGS);
                            startActivity(settingsIntent);
                        }
                    })
                .setNegativeButton(R.string.location_dialog_negative_button,
                    new OnClickListener() {

                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            Toast.makeText(getActivity(),
                                R.string.no_location_message, Toast.LENGTH_LONG)
                                    .show();
                        }
                    });
        return builder.create();
    }
}

Ответ 2

Thread.sleep() в производственном коде является серьезным запахом кода IMHO. Если вы обнаружите, что вам нужно это сделать, вы, вероятно, делаете то, что не должно работать так. В этом случае я думаю, что это источник вашей проблемы - вы не позволяете Android вернуться, чтобы обработать эту очередь сообщений потока, чтобы отправлять любые найденные им обновления местоположения. Я подозреваю, что IntentService просто не будет работать для вашего сценария.