Как получить текущее местоположение в GoogleMap с помощью FusedLocationProviderClient

Я хочу получать периодические (скажем, каждые 2 минуты) обновления текущего местоположения для этого. Я следую официальной документации, я написал этот код, но он не дает обновлений текущего местоположения каждые две минуты, даже если он указан в объекте LocationRequest, который я передаю в requestLocationUpdates ( ), вот код:

public class MapsActivity extends FragmentActivity implements OnMapReadyCallback,
    GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener{

private FusedLocationProviderClient FusedLocationClient;
private GoogleApiClient mGoogleApiClient;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_maps);
    if (mGoogleApiClient == null) {
        mGoogleApiClient = new GoogleApiClient.Builder(this)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(LocationServices.API)
                .build();
    }

 SupportMapFragment map = 
  getSupportFragmentManager().findFragmentById(R.id.map));

  map.getMapAsync(this);

  FusedLocationClient LocationServices.getFusedLocationProviderClient(this);
}


@Override
public void onConnected(Bundle bundle) {
    FusedLocationClient.getLastLocation()
            .addOnSuccessListener(this, new OnSuccessListener<Location>() {
                @Override
                public void onSuccess(Location location) {

                    if (location != null) {
                        Log.i("MainActivity ", "" + location.getLongitude()) 
                    }
                }
            });
FusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
FusedLocationClient.requestLocationUpdates(requestLocation(), 
new LocationCallback(){
        @Override
        public void onLocationResult(LocationResult locationResult) {
            for (Location location : locationResult.getLocations()) {
               Log.i("MainActivity ", "" + location.getLongitude());
               //not getting current location updates every 2 minutes
            }
        };

    },null);

}

@Override
public void onConnectionSuspended(int i) {}

@Override
public void onConnectionFailed(ConnectionResult connectionResult) {}

person blackHawk    schedule 09.07.2017    source источник


Ответы (4)


Это похоже на мой другой ответ здесь, обновленный для использования недавно представленных FusedLocationProviderClient.

Чтобы использовать FusedLocationProviderClient вместе с картой Google:

  1. Подождите, пока карта Google будет готова

  2. При необходимости запросите разрешение на размещение во время выполнения

  3. Запросить обновления местоположения после получения разрешения

  4. Обновите карту Google после получения местоположения пользователя.

Сначала убедитесь, что вы используете сервисы Google Play как минимум 11, так как в более старых версиях нет класса FusedLocationProviderClient (более новые версии также будут работать):

dependencies {
      implementation 'com.google.android.gms:play-services-maps:17.0.0'
      implementation 'com.google.android.gms:play-services-location:17.0.0'
    //........
}

Обратите внимание, что FusedLocationProviderClient присутствует в версии 11.0.2, но из-за ошибок в начальной реализации рекомендуется использовать этот класс только в версии 11.6.0 и новее. Из документации:

Примечание. Рекомендуется использовать сервисы Google Play версии 11.6.0 или выше, которые включают исправления ошибок для этого класса.

Добавьте разрешения на расположение в файле AndroidManifest.xml внутри тега manifest и вне тега application:

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

Добавьте ключ API в AndroidManifest.xml внутри тега application:

    <meta-data
            android:name="com.google.android.geo.API_KEY"
            android:value="AIza___YOUR_KEY_HERE______"/>

Котлин

Вот полный класс Activity в Котлине:

class MapsActivity : AppCompatActivity(), OnMapReadyCallback {

    lateinit var mGoogleMap: GoogleMap
    var mapFrag: SupportMapFragment? = null
    lateinit var mLocationRequest: LocationRequest
    var mLastLocation: Location? = null
    internal var mCurrLocationMarker: Marker? = null
    internal var mFusedLocationClient: FusedLocationProviderClient? = null

    internal var mLocationCallback: LocationCallback = object : LocationCallback() {
        override fun onLocationResult(locationResult: LocationResult) {
            val locationList = locationResult.locations
            if (locationList.isNotEmpty()) {
                //The last location in the list is the newest
                val location = locationList.last()
                Log.i("MapsActivity", "Location: " + location.getLatitude() + " " + location.getLongitude())
                mLastLocation = location
                if (mCurrLocationMarker != null) {
                    mCurrLocationMarker?.remove()
                }

                //Place current location marker
                val latLng = LatLng(location.latitude, location.longitude)
                val markerOptions = MarkerOptions()
                markerOptions.position(latLng)
                markerOptions.title("Current Position")
                markerOptions.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_MAGENTA))
                mCurrLocationMarker = mGoogleMap.addMarker(markerOptions)

                //move map camera
                mGoogleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng, 11.0F))
            }
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_maps)

        supportActionBar?.title = "Map Location Activity"

        mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this)

        mapFrag = supportFragmentManager.findFragmentById(R.id.map) as SupportMapFragment?
        mapFrag?.getMapAsync(this)
    }

    public override fun onPause() {
        super.onPause()

        //stop location updates when Activity is no longer active
        mFusedLocationClient?.removeLocationUpdates(mLocationCallback)
    }

    override fun onMapReady(googleMap: GoogleMap) {
        mGoogleMap = googleMap
        mGoogleMap.mapType = GoogleMap.MAP_TYPE_HYBRID

        mLocationRequest = LocationRequest()
        mLocationRequest.interval = 120000 // two minute interval
        mLocationRequest.fastestInterval = 120000
        mLocationRequest.priority = LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (ContextCompat.checkSelfPermission(
                    this,
                    Manifest.permission.ACCESS_FINE_LOCATION
                ) == PackageManager.PERMISSION_GRANTED
            ) {
                //Location Permission already granted
                mFusedLocationClient?.requestLocationUpdates(mLocationRequest, mLocationCallback, Looper.myLooper())
                mGoogleMap.isMyLocationEnabled = true
            } else {
                //Request Location Permission
                checkLocationPermission()
            }
        } else {
            mFusedLocationClient?.requestLocationUpdates(mLocationRequest, mLocationCallback, Looper.myLooper())
            mGoogleMap.isMyLocationEnabled = true
        }
    }

    private fun checkLocationPermission() {
        if (ActivityCompat.checkSelfPermission(
                this,
                Manifest.permission.ACCESS_FINE_LOCATION
            ) != PackageManager.PERMISSION_GRANTED
        ) {
            // Should we show an explanation?
            if (ActivityCompat.shouldShowRequestPermissionRationale(
                    this,
                    Manifest.permission.ACCESS_FINE_LOCATION
                )
            ) {
                // Show an explanation to the user *asynchronously* -- don't block
                // this thread waiting for the user's response! After the user
                // sees the explanation, try again to request the permission.
                AlertDialog.Builder(this)
                    .setTitle("Location Permission Needed")
                    .setMessage("This app needs the Location permission, please accept to use location functionality")
                    .setPositiveButton(
                        "OK"
                    ) { _, _ ->
                        //Prompt the user once explanation has been shown
                        ActivityCompat.requestPermissions(
                            this@MapsActivity,
                            arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
                            MY_PERMISSIONS_REQUEST_LOCATION
                        )
                    }
                    .create()
                    .show()


            } else {
                // No explanation needed, we can request the permission.
                ActivityCompat.requestPermissions(
                    this,
                    arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
                    MY_PERMISSIONS_REQUEST_LOCATION
                )
            }
        }
    }

    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<String>, grantResults: IntArray
    ) {
        when (requestCode) {
            MY_PERMISSIONS_REQUEST_LOCATION -> {
                // If request is cancelled, the result arrays are empty.
                if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                    // permission was granted, yay! Do the
                    // location-related task you need to do.
                    if (ContextCompat.checkSelfPermission(
                            this,
                            Manifest.permission.ACCESS_FINE_LOCATION
                        ) == PackageManager.PERMISSION_GRANTED
                    ) {

                        mFusedLocationClient?.requestLocationUpdates(
                            mLocationRequest,
                            mLocationCallback,
                            Looper.myLooper()
                        )
                        mGoogleMap.setMyLocationEnabled(true)
                    }

                } else {

                    // permission denied, boo! Disable the
                    // functionality that depends on this permission.
                    Toast.makeText(this, "permission denied", Toast.LENGTH_LONG).show()
                }
                return
            }
        }// other 'case' lines to check for other
        // permissions this app might request
    }

    companion object {
        val MY_PERMISSIONS_REQUEST_LOCATION = 99
    }
}

Java

Вот полный класс Activity в Java:

public class MapsActivity extends AppCompatActivity
        implements OnMapReadyCallback {

    GoogleMap mGoogleMap;
    SupportMapFragment mapFrag;
    LocationRequest mLocationRequest;
    Location mLastLocation;
    Marker mCurrLocationMarker;
    FusedLocationProviderClient mFusedLocationClient;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_maps);

        getSupportActionBar().setTitle("Map Location Activity");

        mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this);

        mapFrag = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map);
        mapFrag.getMapAsync(this);
    }

    @Override
    public void onPause() {
        super.onPause();

        //stop location updates when Activity is no longer active
        if (mFusedLocationClient != null) {
            mFusedLocationClient.removeLocationUpdates(mLocationCallback);
        }
    }

    @Override
    public void onMapReady(GoogleMap googleMap) {
        mGoogleMap = googleMap;
        mGoogleMap.setMapType(GoogleMap.MAP_TYPE_HYBRID);

        mLocationRequest = new LocationRequest();
        mLocationRequest.setInterval(120000); // two minute interval
        mLocationRequest.setFastestInterval(120000);
        mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);

        if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (ContextCompat.checkSelfPermission(this,
                    Manifest.permission.ACCESS_FINE_LOCATION)
                    == PackageManager.PERMISSION_GRANTED) {
                //Location Permission already granted
                mFusedLocationClient.requestLocationUpdates(mLocationRequest, mLocationCallback, Looper.myLooper());
                mGoogleMap.setMyLocationEnabled(true);
            } else {
                //Request Location Permission
                checkLocationPermission();
            }
        }
        else {
            mFusedLocationClient.requestLocationUpdates(mLocationRequest, mLocationCallback, Looper.myLooper());
            mGoogleMap.setMyLocationEnabled(true);
        }
    }

    LocationCallback mLocationCallback = new LocationCallback() {
        @Override
        public void onLocationResult(LocationResult locationResult) {
            List<Location> locationList = locationResult.getLocations();
            if (locationList.size() > 0) {
                //The last location in the list is the newest
                Location location = locationList.get(locationList.size() - 1);
                Log.i("MapsActivity", "Location: " + location.getLatitude() + " " + location.getLongitude());
                mLastLocation = location;
                if (mCurrLocationMarker != null) {
                    mCurrLocationMarker.remove();
                }

                //Place current location marker
                LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude());
                MarkerOptions markerOptions = new MarkerOptions();
                markerOptions.position(latLng);
                markerOptions.title("Current Position");
                markerOptions.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_MAGENTA));
                mCurrLocationMarker = mGoogleMap.addMarker(markerOptions);

                //move map camera
                mGoogleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng, 11));
            }
        }
    };

    public static final int MY_PERMISSIONS_REQUEST_LOCATION = 99;
    private void checkLocationPermission() {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
                != PackageManager.PERMISSION_GRANTED) {

            // Should we show an explanation?
            if (ActivityCompat.shouldShowRequestPermissionRationale(this,
                    Manifest.permission.ACCESS_FINE_LOCATION)) {

                // Show an explanation to the user *asynchronously* -- don't block
                // this thread waiting for the user's response! After the user
                // sees the explanation, try again to request the permission.
                new AlertDialog.Builder(this)
                        .setTitle("Location Permission Needed")
                        .setMessage("This app needs the Location permission, please accept to use location functionality")
                        .setPositiveButton("OK", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialogInterface, int i) {
                                //Prompt the user once explanation has been shown
                                ActivityCompat.requestPermissions(MapsActivity.this,
                                        new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
                                        MY_PERMISSIONS_REQUEST_LOCATION );
                            }
                        })
                        .create()
                        .show();


            } else {
                // No explanation needed, we can request the permission.
                ActivityCompat.requestPermissions(this,
                        new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
                        MY_PERMISSIONS_REQUEST_LOCATION );
            }
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode,
                                           String permissions[], int[] grantResults) {
        switch (requestCode) {
            case MY_PERMISSIONS_REQUEST_LOCATION: {
                // If request is cancelled, the result arrays are empty.
                if (grantResults.length > 0
                        && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                    // permission was granted, yay! Do the
                    // location-related task you need to do.
                    if (ContextCompat.checkSelfPermission(this,
                            Manifest.permission.ACCESS_FINE_LOCATION)
                            == PackageManager.PERMISSION_GRANTED) {

                        mFusedLocationClient.requestLocationUpdates(mLocationRequest, mLocationCallback, Looper.myLooper());
                        mGoogleMap.setMyLocationEnabled(true);
                    }

                } else {

                    // permission denied, boo! Disable the
                    // functionality that depends on this permission.
                    Toast.makeText(this, "permission denied", Toast.LENGTH_LONG).show();
                }
                return;
            }

            // other 'case' lines to check for other
            // permissions this app might request
        }
    }
}

activity_maps.xml:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".map.MapsActivity">

    <fragment android:id="@+id/map"
              xmlns:tools="http://schemas.android.com/tools"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              tools:context=".map.MapsActivity"
              app:layout_constraintTop_toTopOf="parent"
              app:layout_constraintStart_toStartOf="parent"
              app:layout_constraintBottom_toBottomOf="parent"
              app:layout_constraintEnd_toEndOf="parent"
              android:name="com.google.android.gms.maps.SupportMapFragment"/>

</androidx.constraintlayout.widget.ConstraintLayout>

Пользователю будет предложено принять разрешение на размещение:

введите описание изображения здесь

Местоположение будет обновляться при запуске приложения и каждые две минуты:

введите описание изображения здесь

Дополнительное примечание относительно AndroidX

Если вы используете AndroidX, вам может потребоваться добавить эти строки в свой gradle.properties файл (дополнительную информацию см. здесь):

android.useAndroidX=true
android.enableJetifier=true
person Daniel Nugent    schedule 09.07.2017
comment
Спасибо, а как насчет того, чтобы получать текущее местоположение устройства каждые 10 шагов, которые мы перемещаем, например, когда мы движемся, и получаем обновление текущего местоположения в режиме реального времени? - person blackHawk; 09.07.2017
comment
также есть небольшое изменение в координатах, например, сначала я получил 25,3767231, а затем 25,3756256, даже если местоположение устройства не изменилось, также после получения 2 обновлений задержка увеличивается - person blackHawk; 09.07.2017
comment
@blackHawk Для первого комментария вы можете установить меньший временной интервал и использовать метод setSmallestDisplacement() в LocationRequest: developers.google.com/android/reference/com/google/android/gms/ - person Daniel Nugent; 09.07.2017
comment
Что касается второго комментария, это похоже на обычный дрейф GPS. Очень редко два разных исправления местоположения будут иметь одинаковые значения, каждый раз они немного отличаются. - person Daniel Nugent; 09.07.2017
comment
Что такое mLocationCallback? Я не могу найти его в поле переменных - person ; 22.10.2017
comment
@ СергейГрушин Это в операторах импорта: import com.google.android.gms.location.LocationCallback Также убедитесь, что вы компилируете расположение сервисов Google Play, см. Здесь: stackoverflow.com/a / 34801349/4409409 - person Daniel Nugent; 22.10.2017
comment
Я вижу, мы получаем набор локаций. Как это работает на практике и почему нужно получать больше одного? Если мы получим больше одного. если вы хотите узнать ваше текущее местоположение, какое из них мы выберем первым, последним или другим? - person Andrew S; 24.04.2018
comment
@AndrewS Объяснение здесь, в документации: разработчикам. google.com/android/reference/com/google/android/gms/ Метод getLocations() говорит: Returns locations computed, ordered from oldest to newest. - person Daniel Nugent; 24.04.2018
comment
Не забудьте добавить import android.Manifest;, иначе могут возникнуть проблемы с Manifest.permission.ACCESS_FINE_LOCATION - person RobertoFRey; 30.06.2018
comment
FusedLocationProviderClient не обновляет местоположение, когда GPS выключен. Мне не удалось найти никаких настроек для включения обновления местоположения в LocationRequest классе. Есть ли какие-либо настройки для получения обновлений местоположения, даже если GPS недоступен или выключен? - person Farid; 04.08.2018
comment
@FARID Посмотрите здесь, как предложить пользователю включить параметр Location: stackoverflow.com/questions/31235564/ - person Daniel Nugent; 05.08.2018
comment
Когда я использую этот код, он не предлагает мне получить доступ к местоположению устройства. Он просто показывает мне карту со значением по умолчанию Location: 37.4219983 -122.084 - person RoadRunner; 26.08.2018
comment
@DanielNugent В чем разница между вызовом requestLocationUpdates с помощью Looper.myLooper () и null для значения петлителя? - person Noterezeck; 20.09.2018
comment
@Noterezeck Это то, что написано в документации: The Looper object whose message queue will be used to implement the callback mechanism, or null to make callbacks on the calling thread. Итак, похоже, что вызов с нулевым значением, по сути, то же самое, что и использование Looper.myLooper(). - person Daniel Nugent; 20.09.2018
comment
Не забудьте добавить это в manifest.xml <meta-data android:name="com.google.android.geo.API_KEY" android:value="Your Key"/> - person Behnam Heydari; 06.11.2018
comment
Разве вы не должны позвонить requestLocationUpdates в onStart? - person Lester; 01.02.2019
comment
@Lester Вы можете это сделать, но тогда вам не будет гарантировано, что карта будет готова к тому времени, когда вы получите Location, и вам придется по-другому защищаться от этого состояния гонки. - person Daniel Nugent; 02.02.2019
comment
@Daniel Nugent Что делать, если обратный вызов местоположения не был вызван? Выполнены все шаги, но для некоторых устройств ИЛИ иногда обратный вызов не вызывался вообще, а не последнее местоположение события. - person Hiren Dabhi; 05.02.2019
comment
В этом коде маркер перемещается в текущее местоположение каждые две минуты. Я хочу впервые показать текущее местоположение. Так что же для этого нужно изменить? - person Anand Savjani; 07.03.2019
comment
@AnandSavjani Просто позвоните mFusedLocationClient.removeLocationUpdates(mLocationCallback); после того, как появится первое место. - person Daniel Nugent; 08.03.2019
comment
На самом деле для определения местоположения требуется около 4 секунд ... это ожидается? @DanielNugent ... Кроме того, отключение и повторное включение служб определения местоположения занимает даже 10 секунд для определения местоположенияnnnnnn .. это ожидается? - person Kannan_SJD; 09.04.2019
comment
@Kannan_SJD Получение блокировки местоположения требует многого, в том числе, возможно, связи с несколькими спутниками, вращающимися вокруг Земли. Так что да, ожидается, что это займет 4 секунды (а иногда и дольше, а иногда и вовсе). - person Daniel Nugent; 24.04.2019
comment
Я поместил LocationCallback mLocationCallback = new LocationCallback() в метод onCreate(). Вот почему я не получаю обновления в моем текущем местоположении? - person Aliton Oliveira; 23.11.2019
comment
@DanielNugent отличный ответ и объяснение - person Ajeett; 02.05.2020

 mLocationCallback = new LocationCallback() {
        @Override
        public void onLocationResult(LocationResult locationResult) {
            super.onLocationResult(locationResult);
            currentLocation = locationResult.getLastLocation();

        }
    };

эта работа для меня.

person Vishal G. Gohel    schedule 10.10.2017

    private LocationRequest locationRequest;

    public class MapsActivity extends FragmentActivity implements LocationListener{

    locationRequest = new LocationRequest();
    locationRequest.setInterval(60 * 1000);
    locationRequest.setFastestInterval(15 * 1000);
    locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);


@Override
public void onLocationChanged(Location location) {

    latitude = location.getLatitude();
    longitude = location.getLongitude();

}

Внедрите прослушиватель изменения местоположения, и вы сможете переопределить изменение местоположения ...

person Ashith VL    schedule 09.07.2017

Для упрощения попробуйте воспользоваться этой библиотекой https://github.com/mrmans0n/smart-location-lib. Это будет использовать Fused Location Provider.

Вы просто поместили этот код

SmartLocation.with(context).location(new LocationBasedOnActivityProvider(callback))
    .start(new OnLocationUpdatedListener() { ... });
person Deni Rohimat    schedule 09.03.2018