Gute Möglichkeit, den Standort des Benutzers in Android zu ermitteln


211

Das Problem:

Erhalten Sie den aktuellen Standort des Benutzers so schnell wie möglich innerhalb eines Schwellenwerts und schonen Sie gleichzeitig die Batterie.

Warum das Problem ein Problem ist:

Zunächst einmal hat Android zwei Anbieter; Netzwerk und GPS. Manchmal ist das Netzwerk besser und manchmal ist das GPS besser.

Mit "besser" meine ich das Verhältnis von Geschwindigkeit zu Genauigkeit.
Ich bin bereit, ein paar Meter Genauigkeit zu opfern, wenn ich den Standort fast sofort und ohne Einschalten des GPS ermitteln kann.

Zweitens wird nichts gesendet, wenn Sie Aktualisierungen für Standortänderungen anfordern, wenn der aktuelle Standort stabil ist.

Google hat hier ein Beispiel für die Ermittlung des "besten" Standorts: http://developer.android.com/guide/topics/location/obtaining-user-location.html#BestEstimate
Aber ich denke, es ist nicht annähernd so gut wie es sollte /könnte sein.

Ich bin etwas verwirrt, warum Google keine normalisierte API für den Standort hat. Der Entwickler sollte sich nicht darum kümmern müssen, woher der Standort stammt. Sie sollten nur angeben, was Sie möchten, und das Telefon sollte für Sie auswählen.

Womit ich Hilfe brauche:

Ich muss einen guten Weg finden, um den "besten" Ort zu bestimmen, vielleicht durch eine Heuristik oder durch eine Bibliothek eines Drittanbieters.

Dies bedeutet nicht, den besten Anbieter zu ermitteln!
Ich werde wahrscheinlich alle Anbieter nutzen und die besten auswählen.

Hintergrund der App:

Die App sammelt den Standort des Benutzers in einem festgelegten Intervall (etwa alle 10 Minuten) und sendet ihn an einen Server.
Die App sollte so viel Batterie wie möglich sparen und der Standort sollte eine Genauigkeit von X (50-100?) Meter haben.

Das Ziel ist es, später den Pfad des Benutzers während des Tages auf einer Karte darstellen zu können, daher benötige ich dafür eine ausreichende Genauigkeit.

Sonstiges:

Was sind Ihrer Meinung nach vernünftige Werte für gewünschte und akzeptierte Genauigkeiten?
Ich habe 100 m wie akzeptiert und 30 m wie gewünscht verwendet. Ist das zu viel verlangt?
Ich möchte den Pfad des Benutzers später auf einer Karte darstellen können.
Ist 100m für gewünscht und 500m für akzeptiert besser?

Im Moment habe ich das GPS für maximal 60 Sekunden pro Standortaktualisierung eingeschaltet. Ist dies zu kurz, um einen Standort zu ermitteln, wenn Sie sich in Innenräumen mit einer Genauigkeit von etwa 200 m befinden?


Dies ist mein aktueller Code. Jedes Feedback wird geschätzt (abgesehen von der fehlenden Fehlerprüfung, die TODO ist):

protected void runTask() {
    final LocationManager locationManager = (LocationManager) context
            .getSystemService(Context.LOCATION_SERVICE);
    updateBestLocation(locationManager
            .getLastKnownLocation(LocationManager.GPS_PROVIDER));
    updateBestLocation(locationManager
            .getLastKnownLocation(LocationManager.NETWORK_PROVIDER));
    if (getLocationQuality(bestLocation) != LocationQuality.GOOD) {
        Looper.prepare();
        setLooper(Looper.myLooper());
        // Define a listener that responds to location updates
        LocationListener locationListener = new LocationListener() {

            public void onLocationChanged(Location location) {
                updateBestLocation(location);
                if (getLocationQuality(bestLocation) != LocationQuality.GOOD)
                    return;
                // We're done
                Looper l = getLooper();
                if (l != null) l.quit();
            }

            public void onProviderEnabled(String provider) {}

            public void onProviderDisabled(String provider) {}

            public void onStatusChanged(String provider, int status,
                    Bundle extras) {
                // TODO Auto-generated method stub
                Log.i("LocationCollector", "Fail");
                Looper l = getLooper();
                if (l != null) l.quit();
            }
        };
        // Register the listener with the Location Manager to receive
        // location updates
        locationManager.requestLocationUpdates(
                LocationManager.GPS_PROVIDER, 1000, 1, locationListener,
                Looper.myLooper());
        locationManager.requestLocationUpdates(
                LocationManager.NETWORK_PROVIDER, 1000, 1,
                locationListener, Looper.myLooper());
        Timer t = new Timer();
        t.schedule(new TimerTask() {

            @Override
            public void run() {
                Looper l = getLooper();
                if (l != null) l.quit();
                // Log.i("LocationCollector",
                // "Stopping collector due to timeout");
            }
        }, MAX_POLLING_TIME);
        Looper.loop();
        t.cancel();
        locationManager.removeUpdates(locationListener);
        setLooper(null);
    }
    if (getLocationQuality(bestLocation) != LocationQuality.BAD) 
        sendUpdate(locationToString(bestLocation));
    else Log.w("LocationCollector", "Failed to get a location");
}

private enum LocationQuality {
    BAD, ACCEPTED, GOOD;

    public String toString() {
        if (this == GOOD) return "Good";
        else if (this == ACCEPTED) return "Accepted";
        else return "Bad";
    }
}

private LocationQuality getLocationQuality(Location location) {
    if (location == null) return LocationQuality.BAD;
    if (!location.hasAccuracy()) return LocationQuality.BAD;
    long currentTime = System.currentTimeMillis();
    if (currentTime - location.getTime() < MAX_AGE
            && location.getAccuracy() <= GOOD_ACCURACY)
        return LocationQuality.GOOD;
    if (location.getAccuracy() <= ACCEPTED_ACCURACY)
        return LocationQuality.ACCEPTED;
    return LocationQuality.BAD;
}

private synchronized void updateBestLocation(Location location) {
    bestLocation = getBestLocation(location, bestLocation);
}

// Pretty much an unmodified version of googles example
protected Location getBestLocation(Location location,
        Location currentBestLocation) {
    if (currentBestLocation == null) {
        // A new location is always better than no location
        return location;
    }
    if (location == null) return currentBestLocation;
    // Check whether the new location fix is newer or older
    long timeDelta = location.getTime() - currentBestLocation.getTime();
    boolean isSignificantlyNewer = timeDelta > TWO_MINUTES;
    boolean isSignificantlyOlder = timeDelta < -TWO_MINUTES;
    boolean isNewer = timeDelta > 0;
    // If it's been more than two minutes since the current location, use
    // the new location
    // because the user has likely moved
    if (isSignificantlyNewer) {
        return location;
        // If the new location is more than two minutes older, it must be
        // worse
    } else if (isSignificantlyOlder) {
        return currentBestLocation;
    }
    // Check whether the new location fix is more or less accurate
    int accuracyDelta = (int) (location.getAccuracy() - currentBestLocation
            .getAccuracy());
    boolean isLessAccurate = accuracyDelta > 0;
    boolean isMoreAccurate = accuracyDelta < 0;
    boolean isSignificantlyLessAccurate = accuracyDelta > 200;
    // Check if the old and new location are from the same provider
    boolean isFromSameProvider = isSameProvider(location.getProvider(),
            currentBestLocation.getProvider());
    // Determine location quality using a combination of timeliness and
    // accuracy
    if (isMoreAccurate) {
        return location;
    } else if (isNewer && !isLessAccurate) {
        return location;
    } else if (isNewer && !isSignificantlyLessAccurate
            && isFromSameProvider) {
        return location;
    }
    return bestLocation;
}

/** Checks whether two providers are the same */
private boolean isSameProvider(String provider1, String provider2) {
    if (provider1 == null) {
        return provider2 == null;
    }
    return provider1.equals(provider2);
}

7
Es kommt sehr spät, aber der "Fused Location Provider", der kürzlich auf der IO 2013 angekündigt wurde, scheint viele Ihrer Anforderungen zu erfüllen - developer.android.com/google/play-services/location.html
Matt

sollte nicht die letzte Zeile von getBestLocation () sein: return currentBestLocation; anstatt bestLocation zurückzugeben;?
Gavriel

Antworten:


164

Sieht so aus, als würden wir dieselbe Anwendung codieren ;-)
Hier ist meine aktuelle Implementierung. Ich bin noch in der Beta-Testphase meiner GPS-Uploader-App, daher gibt es möglicherweise viele mögliche Verbesserungen. aber es scheint bisher ziemlich gut zu funktionieren.

/**
 * try to get the 'best' location selected from all providers
 */
private Location getBestLocation() {
    Location gpslocation = getLocationByProvider(LocationManager.GPS_PROVIDER);
    Location networkLocation =
            getLocationByProvider(LocationManager.NETWORK_PROVIDER);
    // if we have only one location available, the choice is easy
    if (gpslocation == null) {
        Log.d(TAG, "No GPS Location available.");
        return networkLocation;
    }
    if (networkLocation == null) {
        Log.d(TAG, "No Network Location available");
        return gpslocation;
    }
    // a locationupdate is considered 'old' if its older than the configured
    // update interval. this means, we didn't get a
    // update from this provider since the last check
    long old = System.currentTimeMillis() - getGPSCheckMilliSecsFromPrefs();
    boolean gpsIsOld = (gpslocation.getTime() < old);
    boolean networkIsOld = (networkLocation.getTime() < old);
    // gps is current and available, gps is better than network
    if (!gpsIsOld) {
        Log.d(TAG, "Returning current GPS Location");
        return gpslocation;
    }
    // gps is old, we can't trust it. use network location
    if (!networkIsOld) {
        Log.d(TAG, "GPS is old, Network is current, returning network");
        return networkLocation;
    }
    // both are old return the newer of those two
    if (gpslocation.getTime() > networkLocation.getTime()) {
        Log.d(TAG, "Both are old, returning gps(newer)");
        return gpslocation;
    } else {
        Log.d(TAG, "Both are old, returning network(newer)");
        return networkLocation;
    }
}

/**
 * get the last known location from a specific provider (network/gps)
 */
private Location getLocationByProvider(String provider) {
    Location location = null;
    if (!isProviderSupported(provider)) {
        return null;
    }
    LocationManager locationManager = (LocationManager) getApplicationContext()
            .getSystemService(Context.LOCATION_SERVICE);
    try {
        if (locationManager.isProviderEnabled(provider)) {
            location = locationManager.getLastKnownLocation(provider);
        }
    } catch (IllegalArgumentException e) {
        Log.d(TAG, "Cannot acces Provider " + provider);
    }
    return location;
}

Bearbeiten: Hier ist der Teil, der die regelmäßigen Aktualisierungen von den Standortanbietern anfordert:

public void startRecording() {
    gpsTimer.cancel();
    gpsTimer = new Timer();
    long checkInterval = getGPSCheckMilliSecsFromPrefs();
    long minDistance = getMinDistanceFromPrefs();
    // receive updates
    LocationManager locationManager = (LocationManager) getApplicationContext()
            .getSystemService(Context.LOCATION_SERVICE);
    for (String s : locationManager.getAllProviders()) {
        locationManager.requestLocationUpdates(s, checkInterval,
                minDistance, new LocationListener() {

                    @Override
                    public void onStatusChanged(String provider,
                            int status, Bundle extras) {}

                    @Override
                    public void onProviderEnabled(String provider) {}

                    @Override
                    public void onProviderDisabled(String provider) {}

                    @Override
                    public void onLocationChanged(Location location) {
                        // if this is a gps location, we can use it
                        if (location.getProvider().equals(
                                LocationManager.GPS_PROVIDER)) {
                            doLocationUpdate(location, true);
                        }
                    }
                });
        // //Toast.makeText(this, "GPS Service STARTED",
        // Toast.LENGTH_LONG).show();
        gps_recorder_running = true;
    }
    // start the gps receiver thread
    gpsTimer.scheduleAtFixedRate(new TimerTask() {

        @Override
        public void run() {
            Location location = getBestLocation();
            doLocationUpdate(location, false);
        }
    }, 0, checkInterval);
}

public void doLocationUpdate(Location l, boolean force) {
    long minDistance = getMinDistanceFromPrefs();
    Log.d(TAG, "update received:" + l);
    if (l == null) {
        Log.d(TAG, "Empty location");
        if (force)
            Toast.makeText(this, "Current location not available",
                    Toast.LENGTH_SHORT).show();
        return;
    }
    if (lastLocation != null) {
        float distance = l.distanceTo(lastLocation);
        Log.d(TAG, "Distance to last: " + distance);
        if (l.distanceTo(lastLocation) < minDistance && !force) {
            Log.d(TAG, "Position didn't change");
            return;
        }
        if (l.getAccuracy() >= lastLocation.getAccuracy()
                && l.distanceTo(lastLocation) < l.getAccuracy() && !force) {
            Log.d(TAG,
                    "Accuracy got worse and we are still "
                      + "within the accuracy range.. Not updating");
            return;
        }
        if (l.getTime() <= lastprovidertimestamp && !force) {
            Log.d(TAG, "Timestamp not never than last");
            return;
        }
    }
    // upload/store your location here
}

Dinge, die man beachten muss:

  • Fordern Sie nicht zu oft GPS-Updates an, da der Akku entladen wird. Ich verwende derzeit 30 Minuten als Standard für meine Anwendung.

  • Fügen Sie eine Überprüfung der Mindestentfernung zum letzten bekannten Standort hinzu. Ohne dies "springen" Ihre Punkte herum, wenn kein GPS verfügbar ist und der Standort von den Mobilfunkmasten aus trianguliert wird. oder Sie können überprüfen, ob der neue Standort außerhalb des Genauigkeitswerts des letzten bekannten Standorts liegt.


2
Sie erhalten nie einen neuen Standort, sondern nur Standorte, die zufällig aus früheren Updates stammen. Ich denke, dieser Code würde sehr davon profitieren, wenn tatsächlich ein Listener hinzugefügt würde, der den Standort aktualisiert, indem er von Zeit zu Zeit das GPS einschaltet.
Nicklas A.

2
Entschuldigung, ich dachte, Sie interessieren sich nur für den Teil, der aus allen verfügbaren Standorten das Beste auswählt. Ich habe den obigen Code hinzugefügt, der diese ebenfalls anfordert. Wenn ein neuer GPS-Standort empfangen wird, wird dieser sofort gespeichert / hochgeladen. Wenn ich ein Netzwerkstandort-Update erhalte, speichere ich es als Referenz und hoffe, dass ich bis zur nächsten Standortprüfung auch ein GPS-Update erhalte.
Gryphius

2
Ich hatte auch eine stopRecording () -Methode, die den Timer abbrach. Ich habe schließlich von einem Timer zu einem ScheduledThreadPoolExecutor gewechselt, daher ruft stopRecording jetzt im Grunde executor.shutdown () auf und registriert alle Listener für Standortaktualisierungen ab
Gryphius,

1
Laut meinem SCM hat stopRecording nur gpsTimer.cancel () aufgerufen und gps_recorder_running = false gesetzt, also wie in Ihrem Fall damals keine Bereinigung der Listener. Der aktuelle Code verfolgt alle aktiven Listener in einem Vektor. Ich hatte dies nicht, als ich diese Antwort vor 1,5 Jahren schrieb.
Gryphius

1
Es ist bereits auf Github , aber ich bin mir nicht sicher, ob dies heutzutage immer noch der beste Weg ist, GPS-Sachen zu machen. Afaik sie haben viele Verbesserungen an der Standort-API vorgenommen, seit ich diesen Code geschrieben habe.
Gryphius

33

Um den richtigen Standortanbieter für Ihre App auszuwählen, können Sie Kriterienobjekte verwenden :

Criteria myCriteria = new Criteria();
myCriteria.setAccuracy(Criteria.ACCURACY_HIGH);
myCriteria.setPowerRequirement(Criteria.POWER_LOW);
// let Android select the right location provider for you
String myProvider = locationManager.getBestProvider(myCriteria, true); 

// finally require updates at -at least- the desired rate
long minTimeMillis = 600000; // 600,000 milliseconds make 10 minutes
locationManager.requestLocationUpdates(myProvider,minTimeMillis,0,locationListener); 

Weitere Informationen zur Berücksichtigung der Argumente finden Sie in der Dokumentation zu requestLocationUpdates :

Die Häufigkeit der Benachrichtigung kann mithilfe der Parameter minTime und minDistance gesteuert werden. Wenn minTime größer als 0 ist, kann der LocationManager möglicherweise minTime Millisekunden zwischen Standortaktualisierungen ruhen, um Strom zu sparen. Wenn minDistance größer als 0 ist, wird ein Standort nur gesendet, wenn sich das Gerät mit minDistance-Messgeräten bewegt. Um so häufig wie möglich Benachrichtigungen zu erhalten, setzen Sie beide Parameter auf 0.

Weitere Gedanken

  • Sie können die Genauigkeit der Standortobjekte mit Location.getAccuracy () überwachen , das die geschätzte Genauigkeit der Position in Metern zurückgibt.
  • Das Criteria.ACCURACY_HIGHKriterium sollte Ihnen Fehler unter 100 m geben, was nicht so gut ist wie GPS, aber Ihren Anforderungen entspricht.
  • Sie müssen auch den Status Ihres Standortanbieters überwachen und zu einem anderen Anbieter wechseln, wenn dieser vom Benutzer nicht verfügbar oder deaktiviert wird.
  • Der passive Anbieter passt möglicherweise auch gut zu dieser Art von Anwendung: Die Idee ist, Standortaktualisierungen zu verwenden, wenn sie von einer anderen App angefordert und systemweit gesendet werden.

Ich habe nachgesehen, Criteriaaber was ist, wenn der neueste Netzwerkstandort fantastisch ist (er weiß möglicherweise über WLAN Bescheid) und es keine Zeit oder Batterie braucht, um ihn zu erhalten (getLastKnown), dann werden die Kriterien dies wahrscheinlich ignorieren und stattdessen das GPS zurückgeben. Ich kann nicht glauben, dass Google es Entwicklern so schwer gemacht hat.
Nicklas A.

Zusätzlich zur Verwendung der Kriterien können Sie bei jeder Standortaktualisierung, die von dem von Ihnen ausgewählten Anbieter gesendet wird, den lastKnowLocation für den GPS-Anbieter überprüfen und ihn (Genauigkeit und Datum) mit Ihrem aktuellen Standort vergleichen. Aber dies scheint mir eher eine nette Sache zu sein als eine Anforderung aus Ihren Spezifikationen; Wenn manchmal eine etwas bessere Genauigkeit erreicht wird, ist dies für Ihre Benutzer wirklich nützlich?
Stéphane

Das ist es, was ich jetzt mache. Das Problem ist, dass es mir schwer fällt herauszufinden, ob das letzte Wissen gut genug ist. Ich kann auch hinzufügen, dass ich mich nicht auf einen Anbieter beschränken muss. Je mehr ich benutze, desto schneller bekomme ich möglicherweise eine Sperre.
Nicklas A.

Beachten Sie, dass für PASSIVE_PROVIDER API Level 8 oder höher erforderlich ist.
Eduardo

@ Stéphane Entschuldigung für die Bearbeitung. Pass nicht auf dich auf. Dein Beitrag ist korrekt. Ich habe diese Bearbeitung wegen eines Fehlers durchgeführt. Es tut uns leid. Grüße.
Gaucho

10

Beantwortung der ersten beiden Punkte :

  • GPS gibt Ihnen immer einen genaueren Standort, wenn es aktiviert ist und keine dicken Wände vorhanden sind .

  • Wenn sich der Speicherort nicht geändert hat, können Sie getLastKnownLocation (String) aufrufen und den Speicherort sofort abrufen.

Verwenden eines alternativen Ansatzes :

Sie können versuchen, die Zellen-ID oder alle benachbarten Zellen zu verwenden

TelephonyManager mTelephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
GsmCellLocation loc = (GsmCellLocation) mTelephonyManager.getCellLocation(); 
Log.d ("CID", Integer.toString(loc.getCid()));
Log.d ("LAC", Integer.toString(loc.getLac()));
// or 
List<NeighboringCellInfo> list = mTelephonyManager.getNeighboringCellInfo ();
for (NeighboringCellInfo cell : list) {
    Log.d ("CID", Integer.toString(cell.getCid()));
    Log.d ("LAC", Integer.toString(cell.getLac()));
}

Sie können dann über mehrere offene Datenbanken (z. B. http://www.location-api.com/ oder http://opencellid.org/ ) auf den Zellenstandort verweisen.


Die Strategie wäre, die Liste der Turm-IDs beim Lesen des Standorts zu lesen. Lesen Sie sie dann in der nächsten Abfrage (10 Minuten in Ihrer App) erneut. Wenn mindestens einige Türme gleich sind, ist die Verwendung sicher getLastKnownLocation(String). Wenn nicht, warten Sie onLocationChanged(). Dadurch wird die Notwendigkeit einer Datenbank eines Drittanbieters für den Standort vermieden. Sie können diesen Ansatz auch ausprobieren .


Ja, aber ich das Problem kommt, wenn die letzte bekannte Position wirklich schlecht ist. Ich brauche eine gute Möglichkeit, den besten von zwei Standorten zu bestimmen.
Nicklas A.

Sie können die Türme speichern und prüfen, ob sich diese Türme geändert haben. Wenn dies der Fall ist, warten Sie auf einen neuen Speicherort, falls nicht (oder wenn sich nur einige geändert haben), und verwenden Sie ihn dann erneut. Auf diese Weise vermeiden Sie den Vergleich der Turmstandorte mit einer Datenbank.
Aleadam

Die Verwendung von Türmen scheint mir ein großer Overkill zu sein, aber eine gute Idee.
Nicklas A.

@Nicklas der Code wird nicht komplizierter. Sie benötigen jedoch android.Manifest.permission # ACCESS_COARSE_UPDATES.
Aleadam

Ja, aber ich muss immer noch einen Drittanbieter-Service verwenden und ich muss auch entscheiden können, wann die Tower-Informationen über die Standortdaten verwendet werden sollen. Dies erhöht lediglich die Komplexität.
Nicklas A.

9

Dies ist meine Lösung, die ziemlich gut funktioniert:

private Location bestLocation = null;
private Looper looper;
private boolean networkEnabled = false, gpsEnabled = false;

private synchronized void setLooper(Looper looper) {
    this.looper = looper;
}

private synchronized void stopLooper() {
    if (looper == null) return;
    looper.quit();
}

@Override
protected void runTask() {
    final LocationManager locationManager = (LocationManager) service
            .getSystemService(Context.LOCATION_SERVICE);
    final SharedPreferences prefs = getPreferences();
    final int maxPollingTime = Integer.parseInt(prefs.getString(
            POLLING_KEY, "0"));
    final int desiredAccuracy = Integer.parseInt(prefs.getString(
            DESIRED_KEY, "0"));
    final int acceptedAccuracy = Integer.parseInt(prefs.getString(
            ACCEPTED_KEY, "0"));
    final int maxAge = Integer.parseInt(prefs.getString(AGE_KEY, "0"));
    final String whichProvider = prefs.getString(PROVIDER_KEY, "any");
    final boolean canUseGps = whichProvider.equals("gps")
            || whichProvider.equals("any");
    final boolean canUseNetwork = whichProvider.equals("network")
            || whichProvider.equals("any");
    if (canUseNetwork)
        networkEnabled = locationManager
                .isProviderEnabled(LocationManager.NETWORK_PROVIDER);
    if (canUseGps)
        gpsEnabled = locationManager
                .isProviderEnabled(LocationManager.GPS_PROVIDER);
    // If any provider is enabled now and we displayed a notification clear it.
    if (gpsEnabled || networkEnabled) removeErrorNotification();
    if (gpsEnabled)
        updateBestLocation(locationManager
                .getLastKnownLocation(LocationManager.GPS_PROVIDER));
    if (networkEnabled)
        updateBestLocation(locationManager
                .getLastKnownLocation(LocationManager.NETWORK_PROVIDER));
    if (desiredAccuracy == 0
            || getLocationQuality(desiredAccuracy, acceptedAccuracy,
                    maxAge, bestLocation) != LocationQuality.GOOD) {
        // Define a listener that responds to location updates
        LocationListener locationListener = new LocationListener() {

            public void onLocationChanged(Location location) {
                updateBestLocation(location);
                if (desiredAccuracy != 0
                        && getLocationQuality(desiredAccuracy,
                                acceptedAccuracy, maxAge, bestLocation)
                                == LocationQuality.GOOD)
                    stopLooper();
            }

            public void onProviderEnabled(String provider) {
                if (isSameProvider(provider,
                        LocationManager.NETWORK_PROVIDER))networkEnabled =true;
                else if (isSameProvider(provider,
                        LocationManager.GPS_PROVIDER)) gpsEnabled = true;
                // The user has enabled a location, remove any error
                // notification
                if (canUseGps && gpsEnabled || canUseNetwork
                        && networkEnabled) removeErrorNotification();
            }

            public void onProviderDisabled(String provider) {
                if (isSameProvider(provider,
                        LocationManager.NETWORK_PROVIDER))networkEnabled=false;
                else if (isSameProvider(provider,
                        LocationManager.GPS_PROVIDER)) gpsEnabled = false;
                if (!gpsEnabled && !networkEnabled) {
                    showErrorNotification();
                    stopLooper();
                }
            }

            public void onStatusChanged(String provider, int status,
                    Bundle extras) {
                Log.i(LOG_TAG, "Provider " + provider + " statusChanged");
                if (isSameProvider(provider,
                        LocationManager.NETWORK_PROVIDER)) networkEnabled = 
                        status == LocationProvider.AVAILABLE
                        || status == LocationProvider.TEMPORARILY_UNAVAILABLE;
                else if (isSameProvider(provider,
                        LocationManager.GPS_PROVIDER))
                    gpsEnabled = status == LocationProvider.AVAILABLE
                      || status == LocationProvider.TEMPORARILY_UNAVAILABLE;
                // None of them are available, stop listening
                if (!networkEnabled && !gpsEnabled) {
                    showErrorNotification();
                    stopLooper();
                }
                // The user has enabled a location, remove any error
                // notification
                else if (canUseGps && gpsEnabled || canUseNetwork
                        && networkEnabled) removeErrorNotification();
            }
        };
        if (networkEnabled || gpsEnabled) {
            Looper.prepare();
            setLooper(Looper.myLooper());
            // Register the listener with the Location Manager to receive
            // location updates
            if (canUseGps)
                locationManager.requestLocationUpdates(
                        LocationManager.GPS_PROVIDER, 1000, 1,
                        locationListener, Looper.myLooper());
            if (canUseNetwork)
                locationManager.requestLocationUpdates(
                        LocationManager.NETWORK_PROVIDER, 1000, 1,
                        locationListener, Looper.myLooper());
            Timer t = new Timer();
            t.schedule(new TimerTask() {

                @Override
                public void run() {
                    stopLooper();
                }
            }, maxPollingTime * 1000);
            Looper.loop();
            t.cancel();
            setLooper(null);
            locationManager.removeUpdates(locationListener);
        } else // No provider is enabled, show a notification
        showErrorNotification();
    }
    if (getLocationQuality(desiredAccuracy, acceptedAccuracy, maxAge,
            bestLocation) != LocationQuality.BAD) {
        sendUpdate(new Event(EVENT_TYPE, locationToString(desiredAccuracy,
                acceptedAccuracy, maxAge, bestLocation)));
    } else Log.w(LOG_TAG, "LocationCollector failed to get a location");
}

private synchronized void showErrorNotification() {
    if (notifId != 0) return;
    ServiceHandler handler = service.getHandler();
    NotificationInfo ni = NotificationInfo.createSingleNotification(
            R.string.locationcollector_notif_ticker,
            R.string.locationcollector_notif_title,
            R.string.locationcollector_notif_text,
            android.R.drawable.stat_notify_error);
    Intent intent = new Intent(
            android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS);
    ni.pendingIntent = PendingIntent.getActivity(service, 0, intent,
            PendingIntent.FLAG_UPDATE_CURRENT);
    Message msg = handler.obtainMessage(ServiceHandler.SHOW_NOTIFICATION);
    msg.obj = ni;
    handler.sendMessage(msg);
    notifId = ni.id;
}

private void removeErrorNotification() {
    if (notifId == 0) return;
    ServiceHandler handler = service.getHandler();
    if (handler != null) {
        Message msg = handler.obtainMessage(
                ServiceHandler.CLEAR_NOTIFICATION, notifId, 0);
        handler.sendMessage(msg);
        notifId = 0;
    }
}

@Override
public void interrupt() {
    stopLooper();
    super.interrupt();
}

private String locationToString(int desiredAccuracy, int acceptedAccuracy,
        int maxAge, Location location) {
    StringBuilder sb = new StringBuilder();
    sb.append(String.format(
            "qual=%s time=%d prov=%s acc=%.1f lat=%f long=%f",
            getLocationQuality(desiredAccuracy, acceptedAccuracy, maxAge,
                    location), location.getTime() / 1000, // Millis to
                                                            // seconds
            location.getProvider(), location.getAccuracy(), location
                    .getLatitude(), location.getLongitude()));
    if (location.hasAltitude())
        sb.append(String.format(" alt=%.1f", location.getAltitude()));
    if (location.hasBearing())
        sb.append(String.format(" bearing=%.2f", location.getBearing()));
    return sb.toString();
}

private enum LocationQuality {
    BAD, ACCEPTED, GOOD;

    public String toString() {
        if (this == GOOD) return "Good";
        else if (this == ACCEPTED) return "Accepted";
        else return "Bad";
    }
}

private LocationQuality getLocationQuality(int desiredAccuracy,
        int acceptedAccuracy, int maxAge, Location location) {
    if (location == null) return LocationQuality.BAD;
    if (!location.hasAccuracy()) return LocationQuality.BAD;
    long currentTime = System.currentTimeMillis();
    if (currentTime - location.getTime() < maxAge * 1000
            && location.getAccuracy() <= desiredAccuracy)
        return LocationQuality.GOOD;
    if (acceptedAccuracy == -1
            || location.getAccuracy() <= acceptedAccuracy)
        return LocationQuality.ACCEPTED;
    return LocationQuality.BAD;
}

private synchronized void updateBestLocation(Location location) {
    bestLocation = getBestLocation(location, bestLocation);
}

protected Location getBestLocation(Location location,
        Location currentBestLocation) {
    if (currentBestLocation == null) {
        // A new location is always better than no location
        return location;
    }
    if (location == null) return currentBestLocation;
    // Check whether the new location fix is newer or older
    long timeDelta = location.getTime() - currentBestLocation.getTime();
    boolean isSignificantlyNewer = timeDelta > TWO_MINUTES;
    boolean isSignificantlyOlder = timeDelta < -TWO_MINUTES;
    boolean isNewer = timeDelta > 0;
    // If it's been more than two minutes since the current location, use
    // the new location
    // because the user has likely moved
    if (isSignificantlyNewer) {
        return location;
        // If the new location is more than two minutes older, it must be
        // worse
    } else if (isSignificantlyOlder) {
        return currentBestLocation;
    }
    // Check whether the new location fix is more or less accurate
    int accuracyDelta = (int) (location.getAccuracy() - currentBestLocation
            .getAccuracy());
    boolean isLessAccurate = accuracyDelta > 0;
    boolean isMoreAccurate = accuracyDelta < 0;
    boolean isSignificantlyLessAccurate = accuracyDelta > 200;
    // Check if the old and new location are from the same provider
    boolean isFromSameProvider = isSameProvider(location.getProvider(),
            currentBestLocation.getProvider());
    // Determine location quality using a combination of timeliness and
    // accuracy
    if (isMoreAccurate) {
        return location;
    } else if (isNewer && !isLessAccurate) {
        return location;
    } else if (isNewer && !isSignificantlyLessAccurate
            && isFromSameProvider) {
        return location;
    }
    return bestLocation;
}

/** Checks whether two providers are the same */
private boolean isSameProvider(String provider1, String provider2) {
    if (provider1 == null) return provider2 == null;
    return provider1.equals(provider2);
}

Hallo Nicklas, ich habe den gleichen Anspruch, also könnte ich dir auf irgendeine Weise mitteilen. Ich wäre dir voll dankbar, wenn du uns helfen könntest.
School Boy

Könnten Sie den gesamten Code posten? Vielen Dank, sehr geschätzt
Rodi

Das ist der ganze Code. Ich habe keinen Zugang mehr zum Projekt.
Nicklas A.

1
Sie scheinen den Code dieses Projekts "android-protips-location" übernommen zu haben und er lebt noch. Die Leute können hier sehen, wie es funktioniert code.google.com/p/android-protips-location/source/browse/trunk/…
Gödel77

7

Die Standortgenauigkeit hängt hauptsächlich vom verwendeten Standortanbieter ab:

  1. GPS - bringt Ihnen eine Genauigkeit von mehreren Metern (vorausgesetzt, Sie haben GPS-Empfang)
  2. Wifi - Sie erhalten einige hundert Meter Genauigkeit
  3. Cell Network - Erhält sehr ungenaue Ergebnisse (ich habe bis zu 4 km Abweichung gesehen ...)

Wenn Sie nach Genauigkeit suchen, ist GPS Ihre einzige Option.

Ich habe einen sehr informativen Artikel darüber lesen Sie hier .

Für das GPS-Timeout sollten 60 Sekunden ausreichen und in den meisten Fällen sogar zu viel. Ich denke 30 Sekunden sind OK und manchmal sogar weniger als 5 Sekunden ...

Wenn Sie nur einen einzigen Standort benötigen, würde ich vorschlagen, dass Sie bei Ihrer onLocationChangedMethode nach Erhalt eines Updates die Registrierung des Listeners aufheben und eine unnötige Verwendung des GPS vermeiden.


Es ist mir eigentlich egal, woher ich meinen Standort habe, ich möchte mich nicht auf einen Anbieter beschränken
Nicklas A.

Sie können alle auf dem Gerät verfügbaren Standortanbieter registrieren (die Liste aller Anbieter finden Sie unter LocationManager.getProviders ()). Wenn Sie jedoch nach einer genauen Lösung suchen, ist der Netzwerkanbieter in den meisten Fällen für Sie nicht hilfreich.
Muzikant

Ja, aber es geht nicht darum, zwischen Anbietern zu wählen, sondern darum, den besten Standort im Allgemeinen zu finden (auch wenn mehrere Anbieter beteiligt sind)
Nicklas A.

4

Derzeit verwende ich, da dies vertrauenswürdig ist, um den Standort zu ermitteln und die Entfernung für meine Anwendung zu berechnen. Ich verwende dies für meine Taxi-Anwendung.

Verwenden Sie die Fusions-API, die Google-Entwickler mit der Fusion von GPS-Sensor, Magnetometer und Beschleunigungsmesser entwickelt haben. Verwenden Sie außerdem WLAN oder den Standort der Zelle, um den Standort zu berechnen oder zu schätzen. Es ist auch in der Lage, Standortaktualisierungen auch innerhalb des Gebäudes genau zu geben. Weitere Informationen finden Sie unter https://developers.google.com/android/reference/com/google/android/gms/location/FusedLocationProviderApi

import android.app.Activity;
import android.location.Location;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.TextView;
import android.widget.Toast;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesUtil;
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 java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;


public class MainActivity extends Activity implements LocationListener,
        GoogleApiClient.ConnectionCallbacks,
        GoogleApiClient.OnConnectionFailedListener {

    private static final long ONE_MIN = 500;
    private static final long TWO_MIN = 500;
    private static final long FIVE_MIN = 500;
    private static final long POLLING_FREQ = 1000 * 20;
    private static final long FASTEST_UPDATE_FREQ = 1000 * 5;
    private static final float MIN_ACCURACY = 1.0f;
    private static final float MIN_LAST_READ_ACCURACY = 1;

    private LocationRequest mLocationRequest;
    private Location mBestReading;
TextView tv;
    private GoogleApiClient mGoogleApiClient;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (!servicesAvailable()) {
            finish();
        }

        setContentView(R.layout.activity_main);
tv= (TextView) findViewById(R.id.tv1);
        mLocationRequest = LocationRequest.create();
        mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
        mLocationRequest.setInterval(POLLING_FREQ);
        mLocationRequest.setFastestInterval(FASTEST_UPDATE_FREQ);

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


        if (mGoogleApiClient != null) {
            mGoogleApiClient.connect();
        }
    }

    @Override
    protected void onResume() {
        super.onResume();

        if (mGoogleApiClient != null) {
            mGoogleApiClient.connect();
        }
    }

    @Override
    protected void onPause() {d
        super.onPause();

        if (mGoogleApiClient != null && mGoogleApiClient.isConnected()) {
            mGoogleApiClient.disconnect();
        }
    }


        tv.setText(location + "");
        // Determine whether new location is better than current best
        // estimate
        if (null == mBestReading || location.getAccuracy() < mBestReading.getAccuracy()) {
            mBestReading = location;


            if (mBestReading.getAccuracy() < MIN_ACCURACY) {
                LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
            }
        }
    }

    @Override
    public void onConnected(Bundle dataBundle) {
        // Get first reading. Get additional location updates if necessary
        if (servicesAvailable()) {

            // Get best last location measurement meeting criteria
            mBestReading = bestLastKnownLocation(MIN_LAST_READ_ACCURACY, FIVE_MIN);

            if (null == mBestReading
                    || mBestReading.getAccuracy() > MIN_LAST_READ_ACCURACY
                    || mBestReading.getTime() < System.currentTimeMillis() - TWO_MIN) {

                LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);

               //Schedule a runnable to unregister location listeners

                    @Override
                    public void run() {
                        LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, MainActivity.this);

                    }

                }, ONE_MIN, TimeUnit.MILLISECONDS);

            }

        }
    }

    @Override
    public void onConnectionSuspended(int i) {

    }


    private Location bestLastKnownLocation(float minAccuracy, long minTime) {
        Location bestResult = null;
        float bestAccuracy = Float.MAX_VALUE;
        long bestTime = Long.MIN_VALUE;

        // Get the best most recent location currently available
        Location mCurrentLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
        //tv.setText(mCurrentLocation+"");
        if (mCurrentLocation != null) {
            float accuracy = mCurrentLocation.getAccuracy();
            long time = mCurrentLocation.getTime();

            if (accuracy < bestAccuracy) {
                bestResult = mCurrentLocation;
                bestAccuracy = accuracy;
                bestTime = time;
            }
        }

        // Return best reading or null
        if (bestAccuracy > minAccuracy || bestTime < minTime) {
            return null;
        }
        else {
            return bestResult;
        }
    }

    @Override
    public void onConnectionFailed(ConnectionResult connectionResult) {

    }

    private boolean servicesAvailable() {
        int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);

        if (ConnectionResult.SUCCESS == resultCode) {
            return true;
        }
        else {
            GooglePlayServicesUtil.getErrorDialog(resultCode, this, 0).show();
            return false;
        }
    }
}

2

Ich habe das Internet nach einer aktualisierten Antwort (im letzten Jahr) durchsucht und dabei die neuesten von Google vorgeschlagenen Methoden zum Abrufen von Standorten verwendet (um FusedLocationProviderClient zu verwenden). Ich bin endlich darauf gelandet:

https://github.com/googlesamples/android-play-location/tree/master/LocationUpdates

Ich habe ein neues Projekt erstellt und den größten Teil dieses Codes kopiert. Boom. Es klappt. Und ich denke ohne veraltete Zeilen.

Außerdem scheint der Simulator keinen GPS-Standort zu erhalten, von dem ich weiß. Es ging so weit, dies im Protokoll zu melden: "Alle Standorteinstellungen sind erfüllt."

Und schließlich, falls Sie es wissen wollten (ich tat es), benötigen Sie KEINEN Google Maps-API-Schlüssel von der Google-Entwicklerkonsole, wenn Sie nur den GPS-Standort wünschen.

Ebenfalls nützlich ist ihr Tutorial. Aber ich wollte ein vollständiges einseitiges Tutorial / Codebeispiel, und das. Ihr Tutorial stapelt sich, ist aber verwirrend, wenn Sie neu in diesem Bereich sind, da Sie nicht wissen, welche Teile Sie von früheren Seiten benötigen.

https://developer.android.com/training/location/index.html

Und schließlich erinnern Sie sich an solche Dinge:

Ich musste nicht nur die mainActivity.Java ändern. Ich musste auch Strings.xml, androidmanifest.xml UND das richtige build.gradle ändern. Und auch Ihre activity_Main.xml (aber dieser Teil war einfach für mich).

Ich musste Abhängigkeiten wie diese hinzufügen: Implementierung 'com.google.android.gms: play-services-location: 11.8.0' und die Einstellungen meines Android Studio SDK so aktualisieren, dass sie Google Play Services enthalten. (Dateieinstellungen Aussehen Systemeinstellungen Android SDK SDK Tools überprüfen Google Play Services).

Update: Der Android-Simulator schien einen Standort und Standortänderungsereignisse zu erhalten (als ich den Wert in den Einstellungen der Sim änderte). Aber meine besten und ersten Ergebnisse wurden auf einem tatsächlichen Gerät erzielt. Daher ist es wahrscheinlich am einfachsten, auf tatsächlichen Geräten zu testen.


1

Kürzlich überarbeitet, um den Speicherort des Codes zu ermitteln, einige gute Ideen zu erlernen und schließlich eine relativ perfekte Bibliothek und Demo zu erhalten.

@ Gryphius 'Antwort ist gut

    //request all valid provider(network/gps)
private boolean requestAllProviderUpdates() {
    checkRuntimeEnvironment();
    checkPermission();

    if (isRequesting) {
        EasyLog.d("Request location update is busy");
        return false;
    }


    long minTime = getCheckTimeInterval();
    float minDistance = getCheckMinDistance();

    if (mMapLocationListeners == null) {
        mMapLocationListeners = new HashMap<>();
    }

    mValidProviders = getValidProviders();
    if (mValidProviders == null || mValidProviders.isEmpty()) {
        throw new IllegalArgumentException("Not available provider.");
    }

    for (String provider : mValidProviders) {
        LocationListener locationListener = new LocationListener() {
            @Override
            public void onLocationChanged(Location location) {
                if (location == null) {
                    EasyLog.e("LocationListener callback location is null.");
                    return;
                }
                printf(location);
                mLastProviderTimestamp = location.getTime();

                if (location.getProvider().equals(LocationManager.GPS_PROVIDER)) {
                    finishResult(location);
                } else {
                    doLocationResult(location);
                }

                removeProvider(location.getProvider());
                if (isEmptyValidProviders()) {
                    requestTimeoutMsgInit();
                    removeUpdates();
                }
            }

            @Override
            public void onStatusChanged(String provider, int status, Bundle extras) {
            }

            @Override
            public void onProviderEnabled(String provider) {
            }

            @Override
            public void onProviderDisabled(String provider) {
            }
        };
        getLocationManager().requestLocationUpdates(provider, minTime, minDistance, locationListener);
        mMapLocationListeners.put(provider, locationListener);
        EasyLog.d("Location request %s provider update.", provider);
    }
    isRequesting = true;
    return true;
}

//remove request update
public void removeUpdates() {
    checkRuntimeEnvironment();

    LocationManager locationManager = getLocationManager();
    if (mMapLocationListeners != null) {
        Set<String> keys = mMapLocationListeners.keySet();
        for (String key : keys) {
            LocationListener locationListener = mMapLocationListeners.get(key);
            if (locationListener != null) {
                locationManager.removeUpdates(locationListener);
                EasyLog.d("Remove location update, provider is " + key);
            }
        }
        mMapLocationListeners.clear();
        isRequesting = false;
    }
}

//Compared with the last successful position, to determine whether you need to filter
private boolean isNeedFilter(Location location) {
    checkLocation(location);

    if (mLastLocation != null) {
        float distance = location.distanceTo(mLastLocation);
        if (distance < getCheckMinDistance()) {
            return true;
        }
        if (location.getAccuracy() >= mLastLocation.getAccuracy()
                && distance < location.getAccuracy()) {
            return true;
        }
        if (location.getTime() <= mLastProviderTimestamp) {
            return true;
        }
    }
    return false;
}

private void doLocationResult(Location location) {
    checkLocation(location);

    if (isNeedFilter(location)) {
        EasyLog.d("location need to filtered out, timestamp is " + location.getTime());
        finishResult(mLastLocation);
    } else {
        finishResult(location);
    }
}

//Return to the finished position
private void finishResult(Location location) {
    checkLocation(location);

    double latitude = location.getLatitude();
    double longitude = location.getLongitude();
    float accuracy = location.getAccuracy();
    long time = location.getTime();
    String provider = location.getProvider();

    if (mLocationResultListeners != null && !mLocationResultListeners.isEmpty()) {
        String format = "Location result:<%f, %f> Accuracy:%f Time:%d Provider:%s";
        EasyLog.i(String.format(format, latitude, longitude, accuracy, time, provider));

        mLastLocation = location;
        synchronized (this) {
            Iterator<LocationResultListener> iterator =  mLocationResultListeners.iterator();
            while (iterator.hasNext()) {
                LocationResultListener listener = iterator.next();
                if (listener != null) {
                    listener.onResult(location);
                }
                iterator.remove();
            }
        }
    }
}

Vollständige Implementierung: https://github.com/bingerz/FastLocation/blob/master/fastlocationlib/src/main/java/cn/bingerz/fastlocation/FastLocation.java

1. Dank der @ Grephius-Lösungsideen teile ich auch den vollständigen Code.

2.Jede Anforderung, den Standort zu vervollständigen, ist am besten, Aktualisierungen zu entfernen, da sonst in der Statusleiste des Telefons immer das Positionierungssymbol angezeigt wird


0

Nach meiner Erfahrung ist es am besten, mit dem GPS-Fix zu arbeiten, es sei denn, er ist nicht verfügbar. Ich weiß nicht viel über andere Standortanbieter, aber ich weiß, dass es für GPS einige Tricks gibt, mit denen man ein bisschen Ghetto-Präzision messen kann. Die Höhe ist oft ein Zeichen, so dass Sie nach lächerlichen Werten suchen können. Es gibt das Genauigkeitsmaß für Android-Standortkorrekturen. Auch wenn Sie die Anzahl der verwendeten Satelliten sehen können, kann dies auch die Genauigkeit anzeigen.

Ein interessanter Weg, um eine bessere Vorstellung von der Genauigkeit zu bekommen, könnte darin bestehen, sehr schnell nach einer Reihe von Korrekturen zu fragen, z. B. ~ 1 / s für 10 Sekunden, und dann ein oder zwei Minuten zu schlafen. Ein Vortrag, an dem ich teilgenommen habe, hat zu der Annahme geführt, dass einige Android-Geräte dies sowieso tun werden. Sie würden dann die Ausreißer aussortieren (ich habe gehört, dass der Kalman-Filter hier erwähnt wird) und eine Art Zentrierungsstrategie verwenden, um eine einzelne Korrektur zu erhalten.

Offensichtlich hängt die Tiefe, die Sie hier erreichen, davon ab, wie hoch Ihre Anforderungen sind. Wenn Sie besonders strenge Anforderungen haben, um den bestmöglichen Standort zu erhalten, werden Sie feststellen, dass GPS und Netzwerkstandort so ähnlich sind wie Äpfel und Orangen. Auch GPS kann von Gerät zu Gerät sehr unterschiedlich sein.


Nun, es ist nicht wichtig, dass es das Beste ist, nur dass es gut genug ist, um auf einer Karte zu zeichnen, und dass ich den Akku nicht entleere, da dies eine Hintergrundaufgabe ist.
Nicklas A.

-3

Skyhook (http://www.skyhookwireless.com/) hat einen Standortanbieter, der viel schneller ist als der von Google bereitgestellte Standardanbieter. Es könnte das sein, wonach Sie suchen. Ich bin nicht mit ihnen verbunden.


Interessanterweise scheinen sie nur WiFi zu verwenden, was sehr schön ist, aber ich brauche es immer noch, um zu funktionieren, wenn es keine WLAN- oder 3G / 2G-Verbindung gibt, so dass dies eine weitere Abstraktionsebene hinzufügen würde. Guter Fang.
Nicklas A.

1
Skyhook scheint eine Kombination aus WLAN, GPS und Mobilfunkmasten zu verwenden. Technische Details finden Sie unter skyhookwireless.com/howitworks . Sie haben in letzter Zeit mehrere Design-Siege erzielt, zum Beispiel Mapquest, Twydroid, ShopSavvy und das Sony NGP. Beachten Sie, dass das Herunterladen und Ausprobieren des SDK kostenlos zu sein scheint. Sie müssen sich jedoch an sie wenden, um eine Lizenz für die Verteilung in Ihrer App zu erhalten. Leider geben sie den Preis nicht auf ihrer Website an.
Ed Burnette

Oh, ich verstehe. Nun, wenn es nicht kostenlos ist, kommerziell zu verwenden, kann ich es leider nicht verwenden.
Nicklas A.
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.