2010-03-23 12 views
19

Tengo un código de Android que necesita obtener la mejor ubicación disponible RÁPIDAMENTE, desde el GPS, la red o lo que esté disponible. La precisión es menos importante que la velocidad.Android: obtenga la ubicación actual del mejor proveedor disponible

Obtener la mejor ubicación disponible es seguramente una tarea realmente estándar. Sin embargo, no puedo encontrar ningún código para demostrarlo. El código de ubicación de Android espera que especifique criterios, se registre para las actualizaciones y espere, lo cual está bien si tiene criterios detallados y no le importa esperar.

Pero mi aplicación necesita funcionar un poco más como la aplicación de Mapas cuando lo localiza por primera vez, trabaja desde cualquier proveedor disponible, y simplemente comprueba que la ubicación no está muy desactualizada o es nula.

He intentado rodar mi propio código para hacer esto, pero estoy teniendo problemas. (Está dentro de un servicio de intención donde ocurre una carga, si eso hace alguna diferencia. He incluido todo el código para obtener información.) ¿Qué pasa con este código?

@Override 
protected void onHandleIntent(Intent arg0) { 
    testProviders(); 
    doUpload(); 
} 
private boolean doUpload() { 
     int j = 0; 
     // check if we have accurate location data yet - wait up to 30 seconds 
     while (j < 30) { 
      if ((latString == "") || (lonString == "")) { 
       Log.d(LOG_TAG, "latlng null"); 
       Thread.sleep(1000); 
       j++; 
     } else { 
       Log.d(LOG_TAG, "found lat " + latString + " and lon " + lonString); 
      break; 
     } 
     //do the upload here anyway, with or without location data 
     //[code removed for brevity] 
} 
public boolean testProviders() { 
    Log.e(LOG_TAG, "testProviders"); 
    String location_context = Context.LOCATION_SERVICE; 
    locationmanager = (LocationManager) getSystemService(location_context); 
    List<String> providers = locationmanager.getProviders(true); 
    for (String provider : providers) { 
     Log.e(LOG_TAG, "registering provider " + provider); 
     listener = new LocationListener() { 
      public void onLocationChanged(Location location) { 
       // keep checking the location - until we have 
       // what we need 
       //if (!checkLoc(location)) { 
       Log.e(LOG_TAG, "onLocationChanged"); 
       locationDetermined = checkLoc(location); 
       //} 
      } 
      public void onProviderDisabled(String provider) { 
      } 
      public void onProviderEnabled(String provider) { 
      } 
      public void onStatusChanged(String provider, int status, 
        Bundle extras) { 
      } 
     }; 
     locationmanager.requestLocationUpdates(provider, 0, 
       0, listener); 
    } 
    Log.e(LOG_TAG, "getting updates"); 
    return true; 
} 
private boolean checkLoc(Location location) { 
    float tempAccuracy = location.getAccuracy(); 
    int locAccuracy = (int) tempAccuracy; 
    Log.d(LOG_TAG, "locAccuracy = " + locAccuracy); 
    if ((locAccuracy != 0) && (locAccuracy < LOCATION_ACCURACY)) { 
     latitude = location.getLatitude(); 
     longitude = location.getLongitude(); 
     latString = latitude.toString(); 
     lonString = longitude.toString(); 
     return true; 
    } 
    return false; 
} 
public void removeListeners() { 
    // Log.e(LOG_TAG, "removeListeners"); 
    if ((locationmanager != null) && (listener != null)) { 
     locationmanager.removeUpdates(listener); 
    } 
    locationmanager = null; 
    // Log.d(LOG_TAG, "Removed " + listener.toString()); 
} 
@Override 
public void onDestroy() { 
    super.onDestroy(); 
    removeListeners(); 
} 

Por desgracia, este se encuentra el proveedor de red, pero sólo alguna vez da salida a latlng null 30 veces - nunca parece conseguir un lugar en absoluto. Ni siquiera recibo una declaración de registro de locationChanged.

Es curioso, porque a partir de DDMS puedo ver una salida como:

NetworkLocationProvider: onCellLocationChanged [305,8580] 
NetworkLocationProvider: getNetworkLocation(): returning cache location with accuracy 75.0 

parecía sugerir que el proveedor de red tiene cierta información de ubicación después de todo, no estoy recibiendo en ella.

¿Alguien puede ayudar? Creo que el código de ejemplo de trabajo sería un recurso útil para la comunidad de Android/StackOverflow.

+0

Tal vez mi ejemplo de código puede ayudar http://stackoverflow.com/ preguntas/3145089/qué-es-la-más-simple-y-más-robusta-manera-de-obtener-los-usuarios-actual-ubicación-en-una/3145655 # 3145655 – Fedor

Respuesta

5

Thread.sleep() en el código de producción es un código grave olor en mi humilde opinión. Si descubre que tiene que hacer eso, probablemente esté haciendo algo que no debería funcionar de esa manera. En este caso, creo que es la fuente de su problema: no está permitiendo que Android vuelva a procesar la cola de mensajes de este subproceso para despachar las actualizaciones de ubicación que encuentre. Sospecho que un IntentService simplemente no va a funcionar para su situación.

+0

Gracias. Supongo que lo haré en una actividad normal y solo pasaré el lat/lon al servicio intencionado. Todavía me gustaría un ejemplo de cómo comprobar todos los proveedores de ubicación y elegir la mejor ubicación, si alguien tiene algún código de ejemplo, por favor, publícalo ... si no, intentaré piratear algo y publicarlo aquí. – AP257

+0

@CommonsWare: ¿cómo sugieres que implemente lo siguiente sin utilizar Thread.sleep()? "Siga buscando actualizaciones de ubicación durante 30 segundos; si no encuentra una ubicación actualizada para entonces, abandone y use valores ficticios". – AP257

+0

Hay un millón y un motivos por los que Thread.sleep se puede utilizar en el código de producción (aunque no estoy diciendo necesariamente aquí). – monkjack

6

Definitivamente intenta hacer esto de la manera difícil. Aquí hay algunos fragmentos de una nueva aplicación en la que estoy trabajando. Utiliza Criteria para que todos los proveedores sean capaces de devolver un buen nivel de precisión sin costo.

Si no hay proveedores habilitados, se muestra un cuadro de diálogo que le solicita al usuario que active su configuración de ubicación. Si el usuario acierta, se activa un intento que los envía a la configuración de su teléfono. Si hay proveedores habilitados, la aplicación toma el last known location más reciente de cualquiera de los proveedores habilitados. Para mi aplicación, solo necesito saber en qué área general se encuentra el usuario y es probable que la última ubicación conocida sea desde su área de origen.

Si los proveedores están habilitados, el ciclo también solicita actualizaciones de ubicación lo más rápido posible. Esto es ideal para mi aplicación, pero puede cambiar esto para ahorrar batería modificando los argumentos al requestLocationUpdates method.

La optimización que tiene este código que los ejemplos en la aplicación de Android realmente no muestran es que todos los proveedores habilitados se inician simultáneamente. Todos los proveedores devolverán actualizaciones por separado al onLocationChanged method. En mi aplicación, elimino el oyente de ubicación después de que uno de los proveedores devuelve una ubicación con la precisión suficiente.

Actualizaciones

Zona de inicio:

void getCurrentLocation() { 
    List<String> providers = locationManager.getProviders(criteria, true); 
    if (providers != null) { 
     Location newestLocation = null; 
     for (String provider : providers) { 
      Location location = locationManager.getLastKnownLocation(provider); 
      if (location != null) { 
       if (newestLocation == null) { 
        newestLocation = location; 
       } else { 
        if (location.getTime() > newestLocation.getTime()) { 
         newestLocation = location; 
        } 
       } 
       locationManager.requestLocationUpdates(provider, 0, 0, this); 
      } 
     } 
    } else { 
     LocationDialogFragment dialog = new LocationDialogFragment(); 
     dialog.show(getSupportFragmentManager(), 
      LocationDialogFragment.class.getName()); 
    } 
} 

ubicación de recepción de actualización:

@Override 
public void onLocationChanged(Location location) { 
    float bestAccuracy = -1f; 
    if (location.getAccuracy() != 0.0f 
     && (location.getAccuracy() < bestAccuracy) || bestAccuracy == -1f) { 
     if (location.getAccuracy() < Const.MIN_ACCURACY) { 
      locationManager.removeUpdates(this); 
     } 
    } 
    bestAccuracy = location.getAccuracy(); 
} 

Ubicación de diálogo Configuración:

public class LocationDialogFragment extends DialogFragment { 

    @Override 
    public Dialog onCreateDialog(Bundle savedInstanceState) { 
     AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); 
     builder.setMessage(R.string.location_dialog_message) 
       .setPositiveButton(R.string.location_dialog_positive_button, 
        new OnClickListener() { 

         @Override 
         public void onClick(DialogInterface dialog, int which) { 
          Intent settingsIntent = new Intent(
            Settings.ACTION_LOCATION_SOURCE_SETTINGS); 
          startActivity(settingsIntent); 
         } 
        }) 
       .setNegativeButton(R.string.location_dialog_negative_button, 
        new OnClickListener() { 

         @Override 
         public void onClick(DialogInterface dialog, int which) { 
          Toast.makeText(getActivity(), 
           R.string.no_location_message, Toast.LENGTH_LONG) 
            .show(); 
         } 
        }); 
     return builder.create(); 
    } 
} 
+0

¡Gran solución! Solo una cosa para señalar. Puede ser debido a un cambio en la API, pero la condición '(providers! = Null)' tiene que ser '(privders.size()> 0)' porque el método 'getProviders (Criteria, boolean)' nunca regresa 'null', en su lugar puede devolver una lista con el tamaño 0. – luixal

+1

Creo que recuerdo que no era preciso cuando estaba probando esto, pero supongo que tenemos que ir por la documentación. De todos modos, todos deben saber que FusedLocationProvider en la biblioteca de servicios de Google Play es muy superior a las antiguas clases de ubicación. La implementación es muy diferente sin embargo. Esta técnica está un poco desactualizada. – jophde

+0

¿Puede mostrarnos cómo construyó el objeto 'Criterios' ... También necesito una ubicación aproximada (precisión de la ciudad) – feresr

Cuestiones relacionadas