Estrategias localización v2

Estrategias para escoger un proveedor de localización

Determinar cuál es el proveedor de localización idóneo para nuestra aplicación puede resultar una tarea compleja. Además, esta decisión puede variar con el tiempo según el usuario cambie de posición, o puede desactivarse alguno de los proveedores. A continuación se plantean tres posibles estrategias.

Usar siempre el mismo tipo de proveedor

Los dos proveedores de localización disponibles en Android tienen características muy diferentes. Muchas aplicaciones tienen algún tipo de requisito que hace que podamos decantarnos de entrada por un sistema en concreto. Veamos algunos ejemplos.

Usaremos GPS si:

·         La aplicación requiere una precisión inferior a 10 m.

·         Está pensada para su uso al aire libre (p. ej., senderismo).

Usaremos localización por redes si:

·         El consumo de batería es un problema.

·         Está pensada para su uso en el interior de edificios (visita museo).

Una vez decidido, usaremos las constantes GPS_PROVIDER o NETWORK_PROVIDER de la clase LocationManager para indicar el proveedor deseado.

Existe un tercer tipo de proveedor identificado con la constante PASSIVE _PROVIDER. Puedes usarlo si quieres observar pasivamente actualizaciones de ubicación provocadas por otras aplicaciones, pero no quieres que se lancen nuevas lecturas de posición. De esta manera no provocamos consumo de energía adicional.

El mejor proveedor según un determinado criterio

Como vimos en el apartado anterior, la API de localización de Android nos proporciona la clase Criteria para seleccionar un proveedor de localización según el criterio indicado. Recordemos el código utilizado:

Criteria criterio = new Criteria();

criterio.setCostAllowed(false);

criterio.setAltitudeRequired(false);

criterio.setAccuracy(Criteria.ACCURACY_FINE);

proveedor = manejador.getBestProvider(criterio, true);

Los proveedores pueden variar de estado, por lo que podría ser interesante consultar cuál es el mejor proveedor cada vez que cambie su estado.

Usar los dos proveedores en paralelo

Otra alternativa podría ser programar actualizaciones de los dos proveedores de localización disponibles. Luego podríamos seleccionar la mejor localización entre las suministradas. Para estudiar esta alternativa realiza el siguiente ejercicio:

https://encrypted-tbn1.gstatic.com/images?q=tbn:ANd9GcRzLxOg2I343bfrEax5YX8gX5FTGpThiGtEf0FJTkM2BCEJVADlPg Vídeo[tutorial]: Estrategias de localizacion en Android

Ejercicio: Añadiendo localización en Mis Lugares

 

1.     Añade en AndroidManifest.xml de Mis Lugares el siguiente permiso:

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

2.     Añade a la clase MainActivity las siguientes variables:

private LocationManager manejador;

private Location mejorLocaliz;

La variable mejorLocaliz almacenará la mejor localización actual.

3.     En el método onCreate() añade:

manejador = (LocationManager) getSystemService(LOCATION_SERVICE);

if (manejador.isProviderEnabled(LocationManager.GPS_PROVIDER)) {

   actualizaMejorLocaliz(manejador.getLastKnownLocation(
                                 LocationManager.GPS_PROVIDER));

}

if (manejador.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {

   actualizaMejorLocaliz(manejador.getLastKnownLocation(
                                 LocationManager.NETWORK_PROVIDER));

}

Comenzamos inicializando un LocationManager y tratando de buscar una primera localización para la variable mejorLocaliz. Usamos el método getLastKnownLocation() aplicado a los dos proveedores que vamos a utilizar. El método actualizaMejorLocaliz() se explicará más adelante.

4.     Añade a la clase los siguientes métodos:

@Override protected void onResume() {

   super.onResume();

   activarProveedores();

}

private void activarProveedores() {

   if (manejador.isProviderEnabled(LocationManager.GPS_PROVIDER)) {

      manejador.requestLocationUpdates(LocationManager.GPS_PROVIDER,

               20 * 1000, 5, this);

   }

   if (manejador.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {

      manejador.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,

               10 * 1000, 10, this);

   }

}

@Override protected void onPause() {

   super.onPause();

   manejador.removeUpdates(this);

}

Cuando la actividad pasa a activa, se llamará al método activarProveedores(), donde se programarán actualizaciones de los proveedores disponibles. Las actualizaciones se programan con un periodo de 20 y 10 segundos y con una distancia mínima de 5 o 10 m, dependiendo del proveedor. Para los dos proveedores, será nuestra clase quien actúe como escuchador. Cuando la actividad deje de estar activa eliminamos las actualizaciones.

5.     Nuestra clase ha de implementar la interfaz LocationListener:

public class MainActivity extends ListActivity implements LocationListener {

6.     Añade los métodos de esta interfaz, con el siguiente código:

@Override public void onLocationChanged(Location location) {

   Log.d(Lugares.TAG, "Nueva localización: "+location);

   actualizaMejorLocaliz(location);

}

@Override public void onProviderDisabled(String proveedor) {

   Log.d(Lugares.TAG, "Se deshabilita: "+proveedor);

   activarProveedores();

}

@Override   public void onProviderEnabled(String proveedor) {

   Log.d(Lugares.TAG, "Se habilita: "+proveedor);

   activarProveedores();

}

@Override

public void onStatusChanged(String proveedor, int estado, Bundle extras) {

   Log.d(Lugares.TAG, "Cambia estado: "+proveedor);

   activarProveedores();

}

Las acciones a realizar resultan evidentes: cuando la actualizamos cambie la posición y cuando cambie el estado tratamos de activar nuevos proveedores.

7.     En esta clase ya solo nos queda añadir el siguiente método:

private static final long DOS_MINUTOS = 2 * 60 * 1000;

 

private void actualizaMejorLocaliz(Location localiz) {

   if (localiz != null && (mejorLocaliz == null

         || localiz.getAccuracy() < 2*mejorLocaliz.getAccuracy()

         || localiz.getTime() - mejorLocaliz.getTime() > DOS_MINUTOS)) {

      Log.d(Lugares.TAG, "Nueva mejor localización");

      mejorLocaliz = localiz;

      Lugares.posicionActual.setLatitud(localiz.getLatitude());

      Lugares.posicionActual.setLongitud(localiz.getLongitude());

   }

}

En la variable mejorLocaliz almacenamos la mejor localización. Esta solo será actualizada con la nueva propuesta si: todavía no ha sido inicializada; o la nueva localización tiene una precisión aceptable (almenos la mitad que la actual); o la diferencia de tiempo es superior a dos minutos. Una vez comprobado si se cumple alguna de las tres condiciones, actualizamos mejorLocaliz y guardamos la posición en Lugares.posicionActual.

8.     Pasemos a declarar la constante TAG y la variable posicionActual. En la clase Lugares añade los atributos:

final static String TAG = "MisLugares";

protected static GeoPunto posicionActual = new GeoPunto(0,0);

9.     Una vez que ya disponemos de la posición actual, vamos a tratar de mostrar la distancia a cada lugar en el RecyclerView de la actividad principal. Abre el layout elemento_lista.xml y añade al final del RelativeLayout la nueva vista que se indica:

<TextView android:id="@+id/distancia"

          android:layout_width="wrap_content"

          android:layout_height="wrap_content"

          android:layout_alignParentBottom="true"

          android:layout_alignParentRight="true"

          android:layout_below="@id/direccion"

          android:singleLine="true"

          android:text="... Km" />

</RelativeLayout>

10.  En AdaptadorLugares, dentro de la clase ViewHolder, añade la variable:

public TextView distancia;

11.  En el constructor de ViewHolder añade al final:

distancia = (TextView) vistaReciclada.findViewById(R.id.distancia);

12.  Dentro del método personalizaVista() añade el siguiente código al final:

if (Lugares.posicionActual != null && lugar.getPosicion() != null) {

   int d=(int) Lugares.posicionActual.distancia(lugar.getPosicion());

   if (d < 2000) {

      holder.distancia.setText(d + " m");

   } else {

      holder.distancia.setText(d / 1000 + " Km");

   }

}

Nos aseguramos de que la posición actual y la del lugar existen. Luego calculamos la distancia en la variable d. Si la distancia es inferior a 2000, se muestra en metros; en caso contrario se muestra en Km.

13.  Ejecuta la aplicación y verifica el resultado obtenido: