Ответ 1
Расположение пользователя на Android
Получить местоположение пользователя на Android немного проще, чем на iOS. Чтобы начать путаницу, есть два совершенно разных способа сделать это. Первый использует API-интерфейсы Android от android.location.LocationListener
, а второй - API-интерфейсы сервисов Google Play com.google.android.gms.location.LocationListener
. Давайте пройдем через них обоих.
-
Android Location API
API определения местоположения Android используют три разных провайдера для определения местоположения.
-
LocationManager.GPS_PROVIDER
- этот провайдер определяет местоположение с помощью спутников. В зависимости от условий этому провайдеру может потребоваться некоторое время для возврата местоположения. -
LocationManager.NETWORK_PROVIDER
- этот поставщик определяет местоположение на основе доступности вышки сотовой связи и точек доступа WiFi. Результаты извлекаются с помощью поиска в сети. -
LocationManager.PASSIVE_PROVIDER
- этот провайдер возвращает местоположения, созданные другими провайдерами. Вы пассивно получаете обновления местоположения, когда другие приложения или службы запрашивают их, фактически не запрашивая местоположения самостоятельно.
-
Суть его в том, что вы получаете объект LocationManager
из системы, внедряете LocationListener
и вызываете requestLocationUpdates
для LocationManager
.
Вот фрагмент кода:
LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
// Define a listener that responds to location updates
LocationListener locationListener = new LocationListener() {
public void onLocationChanged(Location location) {
// Called when a new location is found by the network location provider.
makeUseOfNewLocation(location);
}
public void onStatusChanged(String provider, int status, Bundle extras) {}
public void onProviderEnabled(String provider) {}
public void onProviderDisabled(String provider) {}
};
// Register the listener with the Location Manager to receive location updates
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListener);
Руководство Googles API по стратегиям размещения довольно хорошо объясняет код. Но они также отмечают, что в большинстве случаев вы получите лучшую производительность батареи, а также более высокую точность, используя вместо этого API Google Location Services. Теперь путаница начинается!
- Googles Location Services API
Googles Location Services API является частью Google Play Services APK (вот как его настроить). Они построены на основе Androids API. Эти API предоставляют "Поставщик слияния" вместо провайдеров, упомянутых выше. Этот провайдер автоматически выбирает, какого основного провайдера использовать, основываясь на точности, использовании батареи и т.д. Это происходит быстро, потому что вы получаете местоположение из общесистемной службы, которая постоянно обновляет ее. И вы можете использовать более продвинутые функции, такие как геозоны.
Для использования Служб определения местоположения Googles вашему приложению необходимо подключиться к GooglePlayServicesClient
. Чтобы подключиться к клиенту, ваша деятельность (или фрагмент, или около того) должна реализовывать интерфейсы GooglePlayServicesClient.ConnectionCallbacks
и GooglePlayServicesClient.OnConnectionFailedListener
. Вот пример кода:
public class MyActivity extends Activity implements ConnectionCallbacks, OnConnectionFailedListener {
LocationClient locationClient;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my);
locationClient = new LocationClient(this, this, this);
}
@Override
public void onConnected(Bundle bundle) {
Location location = locationClient.getLastLocation() ;
Toast.makeText(this, "Connected to Google Play Services", Toast.LENGTH_SHORT).show();
}
@Override
public void onDisconnected() {
Toast.makeText(this, "Connected from Google Play Services.", Toast.LENGTH_SHORT).show();
}
@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
// code to handle failed connection
// this code can be found here — http://developer.android.com/training/location/retrieve-current.html
}
- Почему
locationClient.getLastLocation()
null?
locationClient.getLastLocation()
получает последнее известное местоположение от клиента. Однако провайдер Fused Location будет поддерживать фоновое местоположение только в том случае, если к нему подключен хотя бы один клиент. Как только первый клиент подключится, он сразу попытается определить местоположение. Если ваша деятельность является первым клиентом, подключившимся, и вы сразу же getLastLocation()
в onConnected()
, этого может не хватить времени для onConnected()
первого местоположения. Это приведет к тому, что location
будет null
.
Чтобы решить эту проблему, нужно подождать (неопределенно), пока провайдер не получит местоположение, а затем вызвать getLastLocation()
, что невозможно узнать. Другой (лучший) вариант - реализовать интерфейс com.google.android.gms.location.LocationListener
для получения периодических обновлений местоположения (и отключать его после первого обновления).
public class MyActivity extends Activity implements ConnectionCallbacks, OnConnectionFailedListener, LocationListener {
// . . . . . . . . more stuff here
LocationRequest locationRequest;
LocationClient locationClient;
@Override
protected void onCreate(Bundle savedInstanceState) {
// . . . . other initialization code
locationClient = new LocationClient(this, this, this);
locationRequest = new LocationRequest();
// Use high accuracy
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
// Set the update interval to 5 seconds
locationRequest.setInterval(UPDATE_INTERVAL);
// Set the fastest update interval to 1 second
locationRequest.setFastestInterval(FASTEST_INTERVAL);
}
// . . . . . . . . other methods
@Override
public void onConnected(Bundle bundle) {
Location location = locationClient.getLastLocation();
if (location == null)
locationClient.requestLocationUpdates(locationRequest, this);
else
Toast.makeText(getActivity(), "Location: " + location.getLatitude() + ", " + location.getLongitude(), Toast.LENGTH_SHORT).show();
}
// . . . . . . . . other methods
@Override
public void onLocationChanged(Location location) {
locationClient.removeLocationUpdates(this);
// Use the location here!!!
}
В этом коде вы проверяете, есть ли у клиента последнее местоположение (в onConnected
). Если нет, вы запрашиваете обновления местоположения и отключаете запросы (в onLocationChanged()
), как только вы получаете обновление.
Обратите внимание, что locationClient.requestLocationUpdates(locationRequest, this);
должен быть внутри обратного вызова onConnected
, иначе вы получите IllegalStateException
поскольку вы будете пытаться запрашивать местоположения, не подключенные к клиенту Google Play Services.
- Пользователь отключил Службы определения местоположения
Во многих случаях у пользователя могут быть отключены службы определения местоположения (для экономии заряда аккумулятора или по соображениям конфиденциальности). В таком случае приведенный выше код будет по-прежнему запрашивать обновления местоположения, но onLocationChanged
никогда не будет вызываться. Вы можете остановить запросы, проверив, отключил ли пользователь службы определения местоположения.
Если ваше приложение требует, чтобы они включили службы определения местоположения, вы хотели бы показать сообщение или тост. К сожалению, нет способа проверить, отключил ли пользователь сервисы определения местоположения в Googles Location Services API. Для этого вам придется вернуться к Androids API.
В вашем методе onCreate
:
LocationManager manager = (LocationManager) getActivity().getSystemService(Context.LOCATION_SERVICE);
if (!manager.isProviderEnabled(LocationManager.GPS_PROVIDER) && !manager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
locationEnabled = false;
Toast.makeText(getActivity(), "Enable location services for accurate data", Toast.LENGTH_SHORT).show();
}
else locationEnabled = true;
И используйте флаг locationEnabled
в вашем методе onConnected
следующим образом:
if (location != null) {
Toast.makeText(getActivity(), "Location: " + location.getLatitude() + ", " + location.getLongitude(), Toast.LENGTH_SHORT).show();
}
else if (location == null && locationEnabled) {
locationClient.requestLocationUpdates(locationRequest, this);
}
благодаря Рахулу Джиресалу
ОБНОВИТЬ
Документ обновлен, LocationClient удален и API поддерживает включение GPS одним щелчком мыши из диалогового окна:
task.addOnSuccessListener(this, new OnSuccessListener<LocationSettingsResponse>() {
@Override
public void onSuccess(LocationSettingsResponse locationSettingsResponse) {
// All location settings are satisfied. The client can initialize
// location requests here.
// ...
}
});
task.addOnFailureListener(this, new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
if (e instanceof ResolvableApiException) {
// Location settings are not satisfied, but this can be fixed
// by showing the user a dialog.
try {
// Show the dialog by calling startResolutionForResult(),
// and check the result in onActivityResult().
ResolvableApiException resolvable = (ResolvableApiException) e;
resolvable.startResolutionForResult(MainActivity.this,
REQUEST_CHECK_SETTINGS);
} catch (IntentSender.SendIntentException sendEx) {
// Ignore the error.
}
}
}
});
Ссылка https://developer.android.com/training/location/change-location-settings#prompt
Новый клиент местоположения: FusedLocationProviderClient
private FusedLocationProviderClient fusedLocationClient;
@Override
protected void onCreate(Bundle savedInstanceState) {
fusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
}
Рекомендуется пройти через https://developer.android.com/training/location перед выполнением каких-либо задач определения местоположения.