Android находит местоположение GPS один раз, показывает диалог загрузки

Я пишу приложение, которое требует текущее местоположение пользователя (последнее известное местоположение не очень полезно) и отображает список всех ближайших к ним «элементов», взятых из базы данных.

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

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

Как я могу запустить прослушиватель местоположения один раз в фоновом режиме, а затем удалить обновления местоположения, как только он найдет местоположение. Мне нужно, чтобы остальная часть приложения ждала, пока у него не будет местоположения GPS или тайм-аута, скажем, через 20-30 секунд. Я хотел бы, чтобы ProgressDialog был открыт, чтобы пользователь знал, что что-то происходит, но это просто должна быть вращающаяся анимация загрузки, а не процент или что-то в этом роде. Если возможно, я хотел бы, чтобы пользователь мог отменить диалог, если ему надоело ждать, а затем он может выполнить поиск, набрав вместо этого пригород и т. Д.

Я пытался сделать это с помощью потоков, но это становится намного сложнее, чем я чувствую, и все равно не работает. На айфоне это намного проще?

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

Подводя итог, я хочу:
* Найти текущие координаты
* Показать диалоговое окно прогресса при получении местоположения
* Может ли приложение ждать успешного определения местоположения или сдаться через определенное время?
* Если успешно : закрыть диалоговое окно "Ход выполнения" и использовать координаты для запуска других моих методов поиска предметов поблизости.
* Если не удалось получить местоположение: закрыть диалоговое окно "Ход выполнения" и показать сообщение об ошибке, а также предложить пользователю использовать меню для перехода к действиям поиска.
* Используйте эти координаты, если пользователь выбирает вид карты из меню, чтобы показать текущее местоположение на карте и булавки, установленные на всех близлежащих элементах, используя их координаты из базы данных.

Спасибо, ребята, надеюсь, я объяснил себя достаточно хорошо. Любые вопросы просто задавайте, я буду регулярно проверять, так как очень хочу завершить эту часть. Ваше здоровье

ИЗМЕНИТЬ
Код согласно запросу

файл активности locationList

protected void onStart()
{
    super.onStart();

    // ....

    new LocationControl().execute(this);
}


private class LocationControl extends AsyncTask<Context, Void, Void>
{
    private final ProgressDialog dialog = new ProgressDialog(branchAtmList.this);

    protected void onPreExecute()
    {
        this.dialog.setMessage("Determining your location...");
        this.dialog.show();
    }

    protected Void doInBackground(Context... params)
    {
        LocationHelper location = new LocationHelper();

        location.getLocation(params[0], locationResult);

        return null;
    }

    protected void onPostExecute(final Void unused)
    {
        if(this.dialog.isShowing())
        {
            this.dialog.dismiss();
        }

        useLocation(); //does the stuff that requires current location
    }

}

public LocationResult locationResult = new LocationResult()
{
    @Override
    public void gotLocation(final Location location)
    {
        currentLocation = location;
    }
};  

Класс LocationHelper

    package org.xxx.xxx.utils;

import java.util.Timer;
/*
imports
*/

public class LocationHelper
{
    LocationManager locationManager;
    private LocationResult locationResult;
    boolean gpsEnabled = false;
    boolean networkEnabled = false;

    public boolean getLocation(Context context, LocationResult result)
    {       
        locationResult = result;

        if(locationManager == null)
        {
            locationManager = (LocationManager)context.getSystemService(Context.LOCATION_SERVICE);
        }
            //exceptions thrown if provider not enabled
            try
            {
                gpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
            }
            catch (Exception ex) {}
            try
            {
                networkEnabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
            }
            catch (Exception ex) {}

            //dont start listeners if no provider is enabled
            if(!gpsEnabled && !networkEnabled)
            {
                return false;
            }

            if(gpsEnabled)
            {
                locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locationListenerGps);
            }
            if(networkEnabled)
            {
                locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListenerNetwork);
            }


            GetLastLocation();
            return true;
    }

    LocationListener locationListenerGps = new LocationListener() {
        public void onLocationChanged(Location location)
        {
            locationResult.gotLocation(location);
            locationManager.removeUpdates(this);
            locationManager.removeUpdates(locationListenerNetwork);

        }
        public void onProviderDisabled(String provider) {}
        public void onProviderEnabled(String provider) {}
        public void onStatusChanged(String provider, int status, Bundle extra) {}
    };

    LocationListener locationListenerNetwork = new LocationListener() {
        public void onLocationChanged(Location location)
        {
            locationResult.gotLocation(location);
            locationManager.removeUpdates(this);
            locationManager.removeUpdates(locationListenerGps);

        }
        public void onProviderDisabled(String provider) {}
        public void onProviderEnabled(String provider) {}
        public void onStatusChanged(String provider, int status, Bundle extra) {}

    };

    private void GetLastLocation()
    {
            locationManager.removeUpdates(locationListenerGps);
            locationManager.removeUpdates(locationListenerNetwork);

            Location gpsLocation = null;
            Location networkLocation = null;

            if(gpsEnabled)
            {
                gpsLocation = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
            }
            if(networkEnabled)
            {
                networkLocation = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
            }

            //if there are both values use the latest one
            if(gpsLocation != null && networkLocation != null)
            {
                if(gpsLocation.getTime() > networkLocation.getTime())
                {
                    locationResult.gotLocation(gpsLocation);
                }
                else
                {
                    locationResult.gotLocation(networkLocation);
                }

                return;
            }

            if(gpsLocation != null)
            {
                locationResult.gotLocation(gpsLocation);
                return;
            }

            if(networkLocation != null)
            {
                locationResult.gotLocation(networkLocation);
                return;
            }

            locationResult.gotLocation(null);
    }

    public static abstract class LocationResult
    {
        public abstract void gotLocation(Location location);
    }
}

Это действительно кажется большим усилием, чтобы получить текущее местоположение один раз? Мне не требуются обновления или что-то еще? Нет ли более простого способа заставить приложение ждать результата? Я застрял на этом так долго.


person Daniel Bowden    schedule 15.08.2010    source источник
comment
stackoverflow.com/questions/3145089/   -  person Rajal    schedule 07.07.2014


Ответы (3)


Используйте AsyncTask и используйте и network_provider, и gps_provider, что означает два прослушивателя. GPS занимает больше времени, может быть, иногда минуту, и работает только на улице, в то время как сеть получает местоположение довольно быстро.

Хороший пример кода здесь: location-in-an/3145655#3145655">Какой самый простой и надежный способ узнать текущее местоположение пользователя на Android?

Для AsyncTask см. http://developer.android.com/reference/android/os/AsyncTask.html

Здесь на SO также есть много примеров кода, например здесь: Android Показать диалоговое окно, пока поток не будет завершен, а затем продолжить работу с программой

РЕДАКТИРОВАТЬ: концепция кода:

добавить переменную класса

boolean hasLocation = false;

вызов onCreate (не в AsyncTask):

locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locationListener);
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListener);

затем в locationListener.onLocationChanged измените значение, как только местоположение будет найдено:

boolean hasLocation = false;

AsyncTask: оставить как есть, но не звонить

LocationHelper location = new LocationHelper();
location.getLocation(params[0], locationResult);

там, вместо этого

Long t = Calendar.getInstance().getTimeInMillies(); 
while (!hasLocation && Calendar.getInstance().getTimeInMillies()-t<30000)) {
    Thread.Sleep(1000); 
};    

вероятно, с задержкой между ними было бы достаточно.

person Mathias Conradt    schedule 15.08.2010
comment
Спасибо, это выглядит полезным. Я думал, что GPS API все равно асинхронный? Или мне все еще нужно реализовать это, чтобы приложение ждало результата? Основываясь на этих примерах: Буду ли я вызывать myLocation.getLocation(this, locationResult) из doInBackground(final String... args)? Я просто не очень понимаю, когда будет вызываться onPostExecute(final Void unused), поскольку конечным результатом материала MyLocation является gotLocation(final Location location)? также что произойдет, если вместо этого таймер истечет? - person Daniel Bowden; 15.08.2010
comment
да, GPS-вещь асинхронна, но отображение прогресса - нет. поэтому вам нужно обернуть его в AsyncTask, так как диалоговое окно прогресса зависит от вашего статуса/результатов GPS. onPostExecute и onPreExecute запускаются в основном потоке пользовательского интерфейса, поэтому вы обновляете свой пользовательский интерфейс там, то есть, например, после того, как вы получили исправление GPS, а также закрываете там диалоговое окно прогресса. - person Mathias Conradt; 15.08.2010
comment
Хорошо, спасибо, я попробую. Как onPostExecute узнает, когда у меня есть исправление GPS или когда оно завершено? т.е. как он узнает, когда вызывается gotLocation() или истекает время таймера? - person Daniel Bowden; 15.08.2010
comment
для исправления GPS (не сети) вы можете использовать GpsStatus.Listener, хотя у меня возникли некоторые проблемы с ним, но, возможно, это потому, что я был в помещении... stackoverflow.com/questions/3287389/ - person Mathias Conradt; 15.08.2010
comment
Сегодня попробовал использовать AsyncTask, и у меня возникли проблемы. из doInBackground я вызываю LocationHelper location = new LocationHelper(); и location.getLocation(params[0], locationResult); Когда это доходит до строки: locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locationListenerGps); Выдает исключение: ERROR/AndroidRuntime(6534): Причина: java.lang.RuntimeException: Не удается создать обработчик внутри потока, который не вызвал Looper.prepare(). Помогите, пожалуйста? - person Daniel Bowden; 16.08.2010
comment
пожалуйста, разместите все соответствующие части кода где-нибудь, pastebin или в начальном вопросе, чтобы посмотреть. Не уверен, зачем вам Looper.prepare — вы запускаете поток где-то рядом с AsyncTask? Вы можете добавить эту строку (Looper.prepare()) и посмотреть, работает ли она. Но давайте сначала посмотрим код. - person Mathias Conradt; 16.08.2010
comment
Я предполагаю, что некоторые потоки автоматически происходят в requestLocationUpdates, где возникает исключение. Это кажется очень сложным способом делать то, что я хочу? Это должно быть так сложно? - person Daniel Bowden; 16.08.2010
comment
не вызывайте requestLocationUpdates из AsyncTask/background. Посмотрите на концепцию кода, которую я разместил выше (назовите ее onCreate или onResume) — этот способ должен работать. Да, возможно, вы могли бы упростить код — обратитесь к моей концепции кода выше. - person Mathias Conradt; 16.08.2010
comment
Хорошо, я попробую. Один вопрос: если я заменяю вызов LocationHelper циклом while, когда мне вызывать location.getLocation()? - person Daniel Bowden; 16.08.2010
comment
Игнорируйте мой последний вопрос, теперь у меня все работает. Как бы я мог ограничить поиск местоположения, скажем, 30 секундами, а затем тайм-аутом? В противном случае диалог прогресса будет крутиться вечно. - person Daniel Bowden; 16.08.2010
comment
см. отредактированный код выше: Long t = Calendar.getInstance().getTimeInMillies(); while (!hasLocation || Calendar.getInstance().getTimeInMillies()-t‹30000)) {}; - person Mathias Conradt; 16.08.2010
comment
Работает хорошо для меня до сих пор, спасибо! Не совсем уверен, как долго будет сохраняться время ожидания, так как получение GPS или сетевого местоположения кажется таким непостоянным на моем устройстве, но я буду продолжать тестировать значения, пока не буду доволен. Отмечен как принятый. :) - person Daniel Bowden; 19.08.2010
comment
Спасибо, мне удалось заставить его работать! спасибо Матиас и Даниэль: D - person ; 11.09.2010

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

Если кто-то столкнется с этим и предложит более чистый результат, чтобы просто получить текущие координаты местоположения пользователя только один раз, тогда прекратите искать, это будет очень признательно!!

private LocationControl locationControlTask;
private boolean hasLocation = false;
LocationHelper locHelper;

Из onCreate() я звоню

locHelper = new LocationHelper();
locHelper.getLocation(context, locationResult);
locationControlTask = new LocationControl();
locationControlTask.execute(this);

Затем у меня есть частный класс LocationControl

private class LocationControl extends AsyncTask<Context, Void, Void>
    {
        private final ProgressDialog dialog = new ProgressDialog(activityname.this);

        protected void onPreExecute()
        {
            this.dialog.setMessage("Searching");
            this.dialog.show();
        }

        protected Void doInBackground(Context... params)
        {
            //Wait 10 seconds to see if we can get a location from either network or GPS, otherwise stop
            Long t = Calendar.getInstance().getTimeInMillis();
            while (!hasLocation && Calendar.getInstance().getTimeInMillis() - t < 15000) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            };
            return null;
        }

        protected void onPostExecute(final Void unused)
        {
            if(this.dialog.isShowing())
            {
                this.dialog.dismiss();
            }

            if (currentLocation != null)
            {
                useLocation();
            }
            else
            {
                //Couldn't find location, do something like show an alert dialog
            }
        }
    }

Затем местоположениеРезультат..

public LocationResult locationResult = new LocationResult()
    {
        @Override
        public void gotLocation(final Location location)
        {
            currentLocation = new Location(location);
            hasLocation = true;
        }
    };

Кроме того, чтобы вы не рвали на себе волосы, как это делал я целую вечность, не забудьте остановить задачу управления местоположением, если кто-то уходит из активности раньше, а также остановите обновления местоположения, чтобы остановить запуск GPS, когда пользователь уходит. В onStop() сделайте что-то вроде этого:

locHelper.stopLocationUpdates();
locationControlTask.cancel(true);

Также stopLocationUpdates в классе Location Helper делает следующее:

public void stopLocationUpdates()
{
locationManager.removeUpdates(locationListenerGps);
locationManager.removeUpdates(locationListenerNetwork);
}

Удачи с ним, он мне был нужен...

person Daniel Bowden    schedule 03.11.2010
comment
размещение полного рабочего кода (например, github) было бы здорово как для вас, так и для нас :) - person ant; 06.07.2012

Почему вместо создания AsyncTask вы не создали окно ProgressDialog и не закрыли его public void gotLocation(final Location location){}? Вы также можете добавить onCancelListener в поле ProgressDialog и отменить любые текущие запросы местоположения.

person Carl    schedule 05.04.2011