Android: ubicación de Google Maps con bajo consumo de batería

Mi aplicación actualmente utiliza Maps by Google Play Services

speficying:

mMap.setMyLocationEnabled(true);

Me doy cuenta cada vez que estoy mostrando el mapa en mi aplicación:

  • la ubicación está indicada en el mapa por un punto azul
  • un icono de ubicación se muestra en la barra superior
  • si entro en Configuración / Ubicación del teléfono, mi aplicación se informa como “Uso de batería alta”

Sin embargo, puedo ver que hay aplicaciones que usan Maps y todavía muestran el punto azul de la ubicación, pero el ícono de ubicación no aparece en la barra superior y el uso de la batería es bajo.

Mi aplicación actualmente otorga ambos permisos:

  • android.permission.ACCESS_COARSE_LOCATION
  • android.permission.ACCESS_FINE_LOCATION

Mi pregunta es:

¿Cómo puedo mostrar el punto azul de ubicación con bajo consumo de batería?

¿es posible especificar la precisión / uso de la batería por código?

ACTUALIZAR

En realidad, me di cuenta de que la forma de hacerlo es usar el GoogleApiClient de FusedLocationApi

  mGoogleApiClient = new GoogleApiClient.Builder(context) .addApi(LocationServices.API) .addConnectionCallbacks(this) .addOnConnectionFailedListener(this) .build(); 

Configuré GoogleApiClient dentro de mi Actividad, llamando:

  • GoogleApiClient.connect() en el inicio de la actividad
  • GoogleApiClient.disconnect() en la parada de la actividad

en la onConnected llamada onConnected establezco los criterios para las actualizaciones de ubicación: intervalo más rápido de 1 minuto con prioridad de baja potencia:

  private static final LocationRequest REQUEST = LocationRequest.create() .setFastestInterval(60000) // in milliseconds .setInterval(180000) // in milliseconds .setPriority(LocationRequest.PRIORITY_LOW_POWER); @Override public void onConnected(Bundle bundle) { LocationServices.FusedLocationApi.requestLocationUpdates( mGoogleApiClient, REQUEST, this); // LocationListener } 

He probado que el GoogleApiClient se conecta correctamente al inicio, pero por alguna razón cada vez que visito el fragmento con el MapView incrustado, ¡todavía obtengo un alto uso de la batería para mi aplicación en la pantalla Configuración / Ubicación!

¡Parece que MapView está ignorando estos criterios de baja potencia!

FINALMENTE ENCONTRÓ LA SOLUCIÓN !!! ¡Gracias a Tristan por su respuesta!

De forma predeterminada, GoogleMap usa su proveedor de ubicación, que no es el Proveedor de ubicación fusionada. Para usar el Proveedor de ubicación fusionada (que le permite controlar la precisión de la ubicación y el consumo de energía), debe establecer explícitamente la fuente de ubicación del mapa con GoogleMap.setLocationSource() ( documentación )

Estoy informando aquí una actividad de muestra para hacer eso:

 import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.api.GoogleApiClient; import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks; import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener; import com.google.android.gms.location.LocationListener; import com.google.android.gms.location.LocationRequest; import com.google.android.gms.location.LocationServices; import com.google.android.gms.maps.GoogleMap; import com.google.android.gms.maps.GoogleMap.OnMyLocationButtonClickListener; import com.google.android.gms.maps.LocationSource; import com.google.android.gms.maps.OnMapReadyCallback; import com.google.android.gms.maps.SupportMapFragment; import android.location.Location; import android.os.Bundle; import android.support.v4.app.FragmentActivity; import android.view.View; import android.widget.TextView; import android.widget.Toast; public class MainActivity extends FragmentActivity implements ConnectionCallbacks, OnConnectionFailedListener, LocationSource, LocationListener, OnMyLocationButtonClickListener, OnMapReadyCallback { private GoogleApiClient mGoogleApiClient; private TextView mMessageView; private OnLocationChangedListener mMapLocationListener = null; // location accuracy settings private static final LocationRequest REQUEST = LocationRequest.create() .setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mMessageView = (TextView) findViewById(R.id.message_text); SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map); mapFragment.getMapAsync(this); mGoogleApiClient = new GoogleApiClient.Builder(this) .addApi(LocationServices.API) .addConnectionCallbacks(this) .addOnConnectionFailedListener(this) .build(); } @Override protected void onResume() { super.onResume(); mGoogleApiClient.connect(); } @Override public void onPause() { super.onPause(); mGoogleApiClient.disconnect(); } @Override public void onMapReady(GoogleMap map) { map.setLocationSource(this); map.setMyLocationEnabled(true); map.setOnMyLocationButtonClickListener(this); } public void showMyLocation(View view) { if (mGoogleApiClient.isConnected()) { String msg = "Location = " + LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient); Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show(); } } /** * Implementation of {@link LocationListener}. */ @Override public void onLocationChanged(Location location) { mMessageView.setText("Location = " + location); if (mMapLocationListener != null) { mMapLocationListener.onLocationChanged(location); } } @Override public void onConnected(Bundle connectionHint) { LocationServices.FusedLocationApi.requestLocationUpdates( mGoogleApiClient, REQUEST, this); // LocationListener } @Override public void onConnectionSuspended(int cause) { // Do nothing } @Override public void onConnectionFailed(ConnectionResult result) { // Do nothing } @Override public boolean onMyLocationButtonClick() { Toast.makeText(this, "MyLocation button clicked", Toast.LENGTH_SHORT).show(); // Return false so that we don't consume the event and the default behavior still occurs // (the camera animates to the user's current position). return false; } @Override public void activate(OnLocationChangedListener onLocationChangedListener) { mMapLocationListener = onLocationChangedListener; } @Override public void deactivate() { mMapLocationListener = null; } } 

Querrá hacer que su actividad (o mejor un objeto separado para este fin) implemente la interfaz de LocationSource .

Es bastante simple que necesite almacenar el oyente pasado en el método activate() y llamarlo cuando se actualice la ubicación y olvidarlo cuando se deactivate() . Consulte esta respuesta para ver un ejemplo; es probable que desee actualizarla para usar FusedLocationProvider .

Una vez que tenga esta configuración, puede pasar su actividad como LocationSource para el mapa como mMap.setLocationSource(this) ( documentación ).

Esto impedirá que el mapa use su LocationSource predeterminado que usa los servicios de ubicación de uso de batería alta.

Aquí se dice que

FusedLocationProviderApi proporciona un mejor hallazgo de ubicación y uso de energía y es utilizado por el punto azul “Mi ubicación”.

Así que el punto “Mi ubicación” en el mapa es alimentado por FusedLocationProviderApi . Y a medida que otorga permiso a android.permission.ACCESS_FINE_LOCATION , permite que FusedLocationProviderApi para su aplicación obtenga datos del GPS, lo que puede ocasionar un uso elevado de la batería.

Así que solo agregue el permiso android.permission.ACCESS_COARSE_LOCATION para que se manifieste y Android no debería culparlo por el uso de la batería.

Puede hacerlo utilizando clases de proveedor de red. Puede utilizar el siguiente código AppLocationService.java // Special para obtener la ubicación actual con bajo consumo de batería (lo mismo que el modo de ahorro de batería en nexus 5, 5.0)

 package coreclass; 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.IBinder; public class AppLocationService extends Service implements LocationListener { protected LocationManager locationManager; Location location; private static final long MIN_DISTANCE_FOR_UPDATE = 10; private static final long MIN_TIME_FOR_UPDATE = 1000 * 60 * 2; public AppLocationService(Context context) { locationManager = (LocationManager) context .getSystemService(LOCATION_SERVICE); } public Location getLocation(String provider) { if (locationManager.isProviderEnabled(provider)) { locationManager.requestLocationUpdates(provider, MIN_TIME_FOR_UPDATE, MIN_DISTANCE_FOR_UPDATE, this); if (locationManager != null) { location = locationManager.getLastKnownLocation(provider); return location; } } return null; } @Override public void onLocationChanged(Location location) { } @Override public void onProviderDisabled(String provider) { } @Override public void onProviderEnabled(String provider) { } @Override public void onStatusChanged(String provider, int status, Bundle extras) { } @Override public IBinder onBind(Intent arg0) { return null; } } 

Uso de la clase superior MainActivity.java

 AppLocationService appLocationService; appLocationService = new AppLocationService(getActivity()); Location nwLocation = appLocationService.getLocation(LocationManager.NETWORK_PROVIDER); if (nwLocation != null) { Lat = nwLocation.getLatitude(); Longi = nwLocation.getLongitude(); } 

De esta forma, puedes obtener la ubicación actual con el modo GPS en modo de uso mejorado, después de que puedas establecer el punto azul o lo que quieras

Espero que te ayude a ti y a todos