Какой самый простой и надежный способ узнать текущее местоположение пользователя на Android? - PullRequest
771 голосов
/ 30 июня 2010

API LocationManager на Android кажется немного трудным для приложения, которое требует лишь приблизительного и приблизительного определения местоположения пользователя.

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

Вот что я хотел бы сделать:

  1. Показать пользователю список ближайшихместоположения.
  2. Предварительно загрузите местоположение пользователя, чтобы к моменту, когда оно понадобится, в Activity X, оно было доступно.
  3. Меня не особо заботит точность или частота обновления.Достаточно просто захватить одно место, пока оно не далеко.Возможно, если я захочу проявить фантазию, я буду обновлять местоположение каждые несколько минут или около того, но это не является огромным приоритетом.
  4. Работайте на любом устройстве, если оно имеет либо GPS, либо поставщика сетевого расположения..

Кажется, что это не должно быть так сложно, но мне кажется, что я должен раскрутить двух разных провайдеров местоположения (GPS и NETWORK) и управлять жизненным циклом каждого из них.Не только это, но я должен дублировать один и тот же код в нескольких действиях, чтобы удовлетворить # 2.Я пытался использовать getBestProvider() в прошлом, чтобы сократить решение до использования только одного провайдера местоположения, но это, кажется, дает вам только лучшего «теоретического» провайдера, а не провайдера, который фактически даст вам лучшие результаты.

Есть ли более простой способ сделать это?

Ответы [ 26 ]

0 голосов
/ 16 декабря 2018

Kotlin версия @Fedor Greate ответ:

использование класса:

val locationResult = object : MyLocation.LocationResult() {

    override fun gotLocation(location: Location?) {

        val lat = location!!.latitude
        val lon = location.longitude

        Toast.makeText(context, "$lat --SLocRes-- $lon", Toast.LENGTH_SHORT).show()
    }

}

val myLocation = MyLocation()
myLocation.getLocation(inflater.context, locationResult)

MyLocation Класс:

class MyLocation {
    internal lateinit var timer1: Timer
    internal var lm: LocationManager? = null
    internal lateinit var locationResult: LocationResult
    internal var gps_enabled = false
    internal var network_enabled = false

    internal var locationListenerGps: LocationListener = object : LocationListener {


        override fun onLocationChanged(location: Location) {
            timer1.cancel()
            locationResult.gotLocation(location)
            lm!!.removeUpdates(this)
            lm!!.removeUpdates(locationListenerNetwork)
        }

        override fun onProviderDisabled(provider: String) {}
        override fun onProviderEnabled(provider: String) {}
        override fun onStatusChanged(provider: String, status: Int, extras: Bundle) {}
    }

    internal var locationListenerNetwork: LocationListener = object : LocationListener {
        override fun onLocationChanged(location: Location) {
            timer1.cancel()
            locationResult.gotLocation(location)
            lm!!.removeUpdates(this)
            lm!!.removeUpdates(locationListenerGps)
        }

        override fun onProviderDisabled(provider: String) {}
        override fun onProviderEnabled(provider: String) {}
        override fun onStatusChanged(provider: String, status: Int, extras: Bundle) {}
    }

    fun getLocation(context: Context, result: LocationResult): Boolean {
        //I use LocationResult callback class to pass location value from MyLocation to user code.
        locationResult = result
        if (lm == null)
            lm = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager?

        //exceptions will be thrown if provider is not permitted.
        try {
            gps_enabled = lm!!.isProviderEnabled(LocationManager.GPS_PROVIDER)
        } catch (ex: Exception) {
        }

        try {
            network_enabled = lm!!.isProviderEnabled(LocationManager.NETWORK_PROVIDER)
        } catch (ex: Exception) {
        }

        //don't start listeners if no provider is enabled
        if (!gps_enabled && !network_enabled)
            return false

        if (ActivityCompat.checkSelfPermission(context,
                Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED ||
            ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) run {

            ActivityCompat.requestPermissions(context as Activity,
                arrayOf(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION), 111)
        }


        if (gps_enabled)
            lm!!.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0f, locationListenerGps)
        if (network_enabled)
            lm!!.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0f, locationListenerNetwork)
        timer1 = Timer()
        timer1.schedule(GetLastLocation(context), 20000)
        return true
    }

    internal inner class GetLastLocation(var context: Context) : TimerTask() {
        override fun run() {
            lm!!.removeUpdates(locationListenerGps)
            lm!!.removeUpdates(locationListenerNetwork)

            var net_loc: Location? = null
            var gps_loc: Location? = null

            if (ActivityCompat.checkSelfPermission(context,
                    Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED ||
                ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED
            ) run {

                ActivityCompat.requestPermissions(context as Activity,
                    arrayOf(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION),111)
            }


            if (gps_enabled)
                gps_loc = lm!!.getLastKnownLocation(LocationManager.GPS_PROVIDER)
            if (network_enabled)
                net_loc = lm!!.getLastKnownLocation(LocationManager.NETWORK_PROVIDER)

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

            if (gps_loc != null) {
                locationResult.gotLocation(gps_loc)
                return
            }
            if (net_loc != null) {
                locationResult.gotLocation(net_loc)
                return
            }
            locationResult.gotLocation(null)
        }
    }

     abstract class LocationResult {
          abstract fun gotLocation(location: Location?)
    }
}
0 голосов
/ 13 мая 2018

После просмотра всех ответов и вопросов ( Самый простой и надежный ).Меня щелкнули только о библиотеке Android-ReactiveLocation .

Когда я сделал приложение для отслеживания местоположения.Тогда я понял, что это очень типично для отслеживания местоположения с оптимизированной батареей.

Итак, я хочу сказать, как новичкам, так и разработчикам, которые не хотят поддерживать свой код местоположения с будущими оптимизациями.Используйте эту библиотеку.

ReactiveLocationProvider locationProvider = new 

    ReactiveLocationProvider(context);
    locationProvider.getLastKnownLocation()
        .subscribe(new Consumer<Location>() {
            @Override
            public void call(Location location) {
                doSthImportantWithObtainedLocation(location);
            }
        });

Зависимости для уровня приложения build.gradle

dependencies {
    ...
    compile 'pl.charmas.android:android-reactive-location2:2.1@aar'
    compile 'com.google.android.gms:play-services-location:11.0.4' //you can use newer GMS version if you need
    compile 'com.google.android.gms:play-services-places:11.0.4'
    compile 'io.reactivex:rxjava:2.0.5' //you can override RxJava version if you need
}

Плюсы для использования этой библиотеки:

  • Эта библиотека и будет активно поддерживаться.
  • Вы не беспокоитесь об оптимизации батареи.Как разработчики сделали все возможное.
  • Простая установка, установление зависимости и игра.
  • Простое подключение к API сервисов Play
  • получение последнего известного местоположения
  • подпискадля обновлений местоположения используйте
  • API настроек местоположения
  • управление геозонами
  • геокодирование местоположения к списку адресов
  • распознавание активности
  • использование текущего местаМесто получения API
  • автозаполнение предложений
0 голосов
/ 05 марта 2018

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

0 голосов
/ 25 февраля 2017

Прежде чем ответить, я просто хочу быть откровенным о том, что я связан с Neura.Теперь к вашему вопросу.Таким образом, вместо того, чтобы тратить время на программирование, чтобы узнать местоположение вашего пользователя, вы можете использовать наш SDK.Интеграция занимает менее 30 минут.Neura предоставляет вам местоположение / активность вашего пользователя, и вы можете соответствующим образом настроить свое приложение для выполнения определенных задач после получения данных.Вот несколько причин использовать Neura над другими API распознавания активности:

  1. Нет необходимости кодировать тонны строк для понимания вашего пользователя.
  2. Дает вам точные результаты.
  3. Потребляет менее 1% от вашей батареи.
0 голосов
/ 24 октября 2016

Вот что я делаю:

  1. Прежде всего я проверяю, включены ли провайдеры NETWORK или GPS. Некоторые могут быть отключены на устройстве, некоторые могут быть отключены в манифесте приложения. Если какой-либо поставщик включен, я извлекаю кэшированное последнее местоположение для этого поставщика и запускаю прослушиватели обновления местоположения для этого поставщика.
  2. Существует метод определения, является ли местоположение лучше, чем последнее полученное местоположение, как указано в ссылке: - https://developer.android.com/guide/topics/location/strategies.html#BestEstimate
  3. Если я получаю обновление от слушателя местоположения, я проверяю, является ли это местоположение лучше, чем ранее полученное местоположение. и если это лучше, чем заменить это местоположение на предыдущее лучшее местоположение (mFinalLocation).
  4. Существует также обработчик (таймер) на две минуты, который в конечном итоге останавливает службу, и в onDestroy() методе службы прекращается прослушивание обновлений местоположения для каждого поставщика.

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

import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.support.annotation.Nullable;
import android.util.Log;


public class RecordLocationService extends Service {

    private final String TAG = RecordLocationService.class.getSimpleName();

    private final int TWO_MINUTES = 1000 * 60 * 2;

    private LocationManager mLocationManager;

    private MyLocationListener mLocationListeners[] = new MyLocationListener[]{
            new MyLocationListener(LocationManager.NETWORK_PROVIDER),
            new MyLocationListener(LocationManager.GPS_PROVIDER)
    };

    private Location mFinalLocation;

    private class MyLocationListener implements LocationListener {
        private String mProvider;

        public MyLocationListener(String provider) {
            Log.d(TAG, "LocationListener : " + provider);
            mProvider = provider;
        }

        public String getProvider() {
            return mProvider;
        }

        @Override
        public void onLocationChanged(Location location) {
            Log.d(TAG, "onLocationChanged : " + location);

            if (isBetterLocation(location, mFinalLocation)) {
                Log.d(TAG, "Setting current Final Location to recent most Location for Provider : " + location.getProvider());
                Log.d(TAG, "Setting current Final Location to : " + location);
                mFinalLocation = location;
            } else {
                Log.d(TAG, "Keeping current Final Location to previous Final Location");
            }

        }

        @Override
        public void onStatusChanged(String provider, int status, Bundle extras) {
            Log.d(TAG, "onStatusChanged provider " + provider);
        }

        @Override
        public void onProviderEnabled(String provider) {
            Log.d(TAG, "onProviderEnabled provider " + provider);
        }

        @Override
        public void onProviderDisabled(String provider) {
            Log.d(TAG, "onProviderDisabled provider " + provider);
        }
    }

    private Handler mStopServiceHandler = new Handler() {

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 1: {
                    stopSelf();
                }
                break;
            }
        }
    };

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        super.onStartCommand(intent, flags, startId);
        Log.d(TAG, "onStartCommand");
        return START_STICKY;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG, "onCreate");
        requestLocation();
        mStopServiceHandler.sendEmptyMessageDelayed(1, TWO_MINUTES);
    }

    private void requestLocation() {
        // Acquire a reference to the system Location Manager
        if (mLocationManager == null) {
            mLocationManager = (LocationManager) this.getApplicationContext().getSystemService(Context.LOCATION_SERVICE);
        }

        try {
            if (mLocationManager.getAllProviders().contains(LocationManager.NETWORK_PROVIDER) && mLocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
                Log.d(TAG, "Fetching Cached Location for Provider : " + LocationManager.NETWORK_PROVIDER);
                Location cachedNetworkLocation = mLocationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);

                if (cachedNetworkLocation != null) {
                    Log.d(TAG, "Setting Final Location to Cached Location for Provider : " + LocationManager.NETWORK_PROVIDER);
                    Log.d(TAG, "Setting Final Location to : " + cachedNetworkLocation);
                    mFinalLocation = cachedNetworkLocation;
                } else {
                    Log.d(TAG, "Cached Location for Provider : " + LocationManager.NETWORK_PROVIDER + " is NULL");
                }

                Log.d(TAG, "Requesting Location Update for Provider : " + LocationManager.NETWORK_PROVIDER);
                mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, mLocationListeners[0]);
            }

        } catch (SecurityException se) {
            Log.e(TAG, se.getMessage(), se);
        } catch (IllegalArgumentException iae) {
            Log.e(TAG, iae.getMessage(), iae);
        }

        try {
            if (mLocationManager.getAllProviders().contains(LocationManager.GPS_PROVIDER) && mLocationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
                Log.d(TAG, "Fetching Cached Location for Provider : " + LocationManager.GPS_PROVIDER);
                Location cachedGPSLocation = mLocationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);

                if (cachedGPSLocation != null) {
                    if (isBetterLocation(cachedGPSLocation, mFinalLocation)) {
                        Log.d(TAG, "Setting Final Location to Cached Location for Provider : " + LocationManager.GPS_PROVIDER);
                        Log.d(TAG, "Setting Final Location to : " + cachedGPSLocation);
                        mFinalLocation = cachedGPSLocation;
                    }
                } else {
                    Log.d(TAG, "Cached Location for Provider : " + LocationManager.GPS_PROVIDER + " is NULL");
                }

                Log.d(TAG, "Requesting Location Update for Provider : " + LocationManager.GPS_PROVIDER);
                mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, mLocationListeners[1]);
            }

        } catch (SecurityException se) {
            Log.e(TAG, se.getMessage(), se);
        } catch (IllegalArgumentException iae) {
            Log.e(TAG, iae.getMessage(), iae);
        }


    }

    /**
     * Determines whether one Location reading is better than the current Location fix
     *
     * @param location            The new Location that you want to evaluate
     * @param currentBestLocation The current Location fix, to which you want to compare the new one
     */
    protected boolean isBetterLocation(Location location, Location currentBestLocation) {
        if (currentBestLocation == null) {
            // A new location is always better than no location
            return true;
        }

        // 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 true;
            // If the new location is more than two minutes older, it must be worse
        } else if (isSignificantlyOlder) {
            return false;
        }

        // 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 true;
        } else if (isNewer && !isLessAccurate) {
            return true;
        } else if (isNewer && !isSignificantlyLessAccurate && isFromSameProvider) {
            return true;
        }
        return false;
    }

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

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy");
        if (mLocationManager != null) {
            for (int i = 0; i < mLocationListeners.length; i++) {
                try {
                    Log.d(TAG, "Removing Location Update for Provider : " + mLocationListeners[i].getProvider());
                    mLocationManager.removeUpdates(mLocationListeners[i]);
                } catch (Exception ex) {
                    Log.e(TAG, "fail to remove location listeners, ignore", ex);
                }
            }
        }
    }
}
0 голосов
/ 21 мая 2016

Используя FusedLocationProviderApi, который является новейшим API и лучшим из доступных возможностей определения местоположения в Android. добавить это в файл build.gradle

dependencies {
    compile 'com.google.android.gms:play-services:6.5.87'
}

по этому URL вы можете получить полный исходный код http://javapapers.com/android/android-location-fused-provider/

...