2010-03-23 7 views
19

Ich habe einige Android-Code, der den besten verfügbaren Standort SCHNELL bekommen muss, von GPS, Netzwerk oder was auch immer verfügbar ist. Genauigkeit ist weniger wichtig als Geschwindigkeit.Android: Holen Sie sich den aktuellen Standort von den besten verfügbaren Anbieter

Den besten verfügbaren Standort zu bekommen ist sicherlich eine Standardaufgabe. Aber ich kann keinen Code finden, um es zu demonstrieren. Der Android-Standortcode erwartet, dass Sie Kriterien angeben, sich für Updates registrieren und warten - das ist in Ordnung, wenn Sie detaillierte Kriterien haben und es Ihnen nichts ausmacht, darauf zu warten.

Aber meine App muss ein bisschen mehr funktionieren wie die Google Maps App, wenn sie Sie zuerst findet - arbeiten Sie von jedem verfügbaren Anbieter, und prüfen Sie nur, ob der Standort nicht veraltet oder null ist.

Ich habe versucht, meinen eigenen Code zu rollen, aber ich habe Probleme. (Es befindet sich in einem IntentService, in dem ein Upload stattfindet, wenn das einen Unterschied macht. Ich habe den gesamten Code für Informationen hinzugefügt.) Was ist mit diesem Code falsch?

@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(); 
} 

Leider ist dies findet den Netzbetreiber, sondern immer nur gibt latlng null 30 mal - es scheint, nie überhaupt eine Stelle zu bekommen. Ich bekomme nie eine Log-Anweisung von locationChanged.

Es ist schon komisch, denn von DDMs kann ich eine Ausgabe wie sehen:

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

vorzuschlagen scheinen, dass der Netzbetreiber hat schließlich einige Informationen über den Standort haben, ich bin es einfach nicht zu bekommen.

Kann jemand helfen? Ich denke, Code für Arbeitsbeispiele wäre eine nützliche Ressource für die Android/StackOverflow-Community.

+0

Vielleicht kann mein Codebeispiel helfen http://stackoverflow.com/ Fragen/3145089/was-ist-der-einfachste-und-robust-Weg-zu-erhalten-die-Benutzer-aktuelle-Standort-in-einem/3145655 # 3145655 – Fedor

Antwort

5

Thread.sleep() im Produktionscode ist ein seriöser Code Geruch IMHO. Wenn Sie feststellen, dass Sie das tun müssen, tun Sie wahrscheinlich etwas, das nicht so funktionieren sollte. In diesem Fall glaube ich, dass dies die Ursache Ihres Problems ist. Sie lassen Android die Nachrichtenwarteschlange dieses Threads nicht verarbeiten, um alle gefundenen Standortaktualisierungen zu versenden. Ich vermute, dass ein IntentService einfach nicht für Ihr Szenario funktioniert.

+0

Danke. Ich schätze, ich mache es in einer normalen Aktivität und überlasse den Lat/Lon einfach dem IntentService. Ich würde trotzdem gerne ein Beispiel sehen, wie man alle LocationProvider überprüft und den besten Standort auswählt - wenn jemand einen Beispielcode hat, bitte poste ihn ... wenn nicht, werde ich versuchen, etwas zu hacken und es hier zu posten. – AP257

+0

@CommonsWare - wie schlagen Sie vor, dass ich folgendes ohne Thread.sleep() implementiere? "Halten Sie 30 Sekunden lang nach Ortsaktualisierungen Ausschau. Wenn bis dahin noch kein aktueller Standort gefunden wurde, geben Sie Dummy-Werte auf und verwenden Sie sie." – AP257

+0

Es gibt eine Million Gründe, warum Thread.sleep im Produktionscode verwendet werden kann (obwohl ich nicht unbedingt hier sage). – monkjack

6

Sie versuchen definitiv, dies auf die harte Tour zu tun. Hier sind einige Auszüge aus einer neuen App, an der ich gerade arbeite. Es verwendet Criteria, um alle Anbieter in die Lage zu versetzen, eine hohe Genauigkeit ohne Kosten zu erzielen.

Wenn keine Provider aktiviert sind, wird ein Dialogfeld angezeigt, in dem der Benutzer aufgefordert wird, seine Standorteinstellungen zu aktivieren. Wenn der Benutzer ok schlägt, wird tatsächlich eine Absicht ausgelöst, die sie an die Einstellungen auf ihrem Telefon sendet. Wenn Provider aktiviert sind, nimmt die App die neuesten last known location von einem der aktivierten Provider. Für meine App muss ich nur wissen, in welchem ​​allgemeinen Bereich sich der Benutzer befindet, und es ist wahrscheinlich, dass der letzte bekannte Standort aus seiner Heimat stammt.

Wenn Provider aktiviert sind, fordert die Schleife auch so schnell wie möglich Standortaktualisierungen an. Dies ist ideal für meine App, aber Sie können dies ändern, um Batterie zu sparen, indem ich die Argumente an die requestLocationUpdates method ändere.

Die Optimierung, die dieser Code hat, dass die Beispiele in der Android-App nicht wirklich zeigen, ist, dass alle aktivierten Provider simultan gestartet werden. Alle Anbieter werden separate Updates an die onLocationChanged method zurückgeben. In meiner App entferne ich den Standort-Listener, nachdem einer der Provider einen Standort mit einer ausreichenden Genauigkeit zurückgegeben hat.

Startort Updates:

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()); 
    } 
} 

Erhalten Location Update:

@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(); 
} 

Standorteinstellungen Dialog:

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

Große Lösung! Nur eine Sache, um darauf hinzuweisen. Dies kann auf eine Änderung in der API zurückzuführen sein, aber die Bedingung '(providers! = Null)' muss '(privders.size()> 0) sein, weil die Methode' getProviders (Criteria, boolean) 'niemals zurückkehrt 'null', stattdessen kann eine Liste mit der Größe 0 zurückgegeben werden. – luixal

+1

Ich denke, ich erinnere mich, dass ich nicht genau war, als ich das getestet habe, aber ich denke, wir müssen uns an die Dokumentation halten. Wie auch immer, jeder sollte wissen, dass der FusedLocationProvider in der Google Play Services-Bibliothek den alten Standortklassen weit überlegen ist. Die Implementierung ist jedoch sehr unterschiedlich. Diese Technik ist etwas veraltet. – jophde

+0

Können Sie uns zeigen, wie Sie das Objekt 'Kriterien' erstellt haben? Ich brauche auch einen ungefähren Standort (Genauigkeit der Stadt) – feresr

Verwandte Themen