获取用户在Android中的位置的好方法

问题描述 投票:209回答:12

问题:

尽快将用户的当前位置置于阈值内,同时节省电池电量。

为什么问题是一个问题:

首先,android有两个提供商;网络和GPS。有时网络更好,有时GPS更好。

“更好”是指速度与准确度之比。 如果我几乎可以立即获得位置并且不打开GPS,我愿意牺牲几米精度。

其次,如果您要求更新位置更改,则在当前位置稳定时不会发送任何内容。

谷歌有一个确定“最佳”位置的例子:http://developer.android.com/guide/topics/location/obtaining-user-location.html#BestEstimate 但我认为它不应该接近它应该/可能的好。

我有点困惑为什么谷歌没有标准化的位置API,开发者不应该关心位置来自哪里,你应该只指定你想要的东西,手机应该为你选择。

我需要帮助的是:

我需要找到一个很好的方法来确定“最佳”位置,也许是一些启发式或者可能通过某些第三方库。

这并不意味着确定最好的提供商! 我可能会使用所有提供商并选择其中最好的。

应用程序的背景:

该应用程序将以固定间隔收集用户的位置(假设每10分钟左右)并将其发送到服务器。 该应用程序应尽可能节省电池,并且位置应具有X(50-100?)米的精度。

目标是以后能够在地图上绘制白天用户的路径,因此我需要足够的准确性。

其他:

您认为对于期望和接受的准确度的合理价值是什么? 我一直在使用100米接受,并且根据需要使用30米,这要问多少? 我希望以后能够在地图上绘制用户的路径。 期望100米,接受500米更好吗?

此外,现在我每次更新GPS的时间最长为60秒,如果你在室内的准确度可能超过200米,这个位置太短了吗?


这是我目前的代码,感谢任何反馈(除了缺少错误检查,这是TODO):

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);
}
android geolocation android-location
12个回答
162
投票

看起来我们正在编写相同的应用程序;-) 这是我目前的实施。我仍处于GPS上传应用程序的beta测试阶段,因此可能会有许多改进。但到目前为止似乎工作得很好。

/**
 * 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;
}

编辑:这是从位置提供者请求定期更新的部分:

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
}

需要考虑的事项:

  • 不要经常请求GPS更新,它会耗尽电池电量。我目前使用30分钟默认为我的应用程序。
  • 添加“到最后已知位置的最小距离”检查。如果没有这个,当GPS不可用时,你的点将“跳转”,并且位置正从手机信号塔进行三角测量。或者您可以检查新位置是否超出上一个已知位置的准确度值。

0
投票

根据我的经验,我发现最好使用GPS定位,除非它不可用。我不太了解其他位置提供商,但我知道对于GPS,有一些技巧可以用来给出一些贫民窟精确度量。海拔通常是一个标志,所以你可以检查荒谬的价值观。 Android位置修复程序有准确度衡量标准。此外,如果您可以看到使用的卫星数量,这也可以指示精度。

一个有趣的方法来更好地了解准确性可能是非常迅速地要求一组修复,例如~1 /秒持续10秒然后再睡一两分钟。我去过的一次谈话导致相信一些Android设备无论如何都会这样做。然后你会淘汰异常值(我听过这里提到的卡尔曼滤波器)并使用某种中心策略来获得单一修复。

显然,你到达这里的深度取决于你的要求有多难。如果您对获得最佳位置有特别严格的要求,我想您会发现GPS和网络位置与苹果和橙子相似。 GPS也可能因设备而异。


0
投票

最近重构了获取代码的位置,学习了一些好的想法,最后实现了一个比较完善的库和Demo。

@Gryphius的回答很好

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

完成实施:qazxswpoi

1.感谢@Gryphius解决方案的想法,我也分享了完整的代码。

2.每次请求完成位置,最好删除更新,否则手机状态栏将始终显示定位图标


-3
投票

Skyhook(http://www.skyhookwireless.com/)的位置提供商比Google提供的标准提供商快得多。它可能正是你要找的东西。我不隶属于他们。


33
投票

要为您的应用选择正确的位置提供商,您可以使用Criteria对象:

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

阅读requestLocationUpdates的文档,了解有关如何考虑参数的更多详细信息:

可以使用minTime和minDistance参数来控制通知的频率。如果minTime大于0,则LocationManager可能会在位置更新之间休息minTime毫秒以节省电量。如果minDistance大于0,则仅当设备移动minDistance米时才会广播位置。要尽可能频繁地获取通知,请将两个参数都设置为0。

More thoughts

  • 您可以使用Location.getAccuracy()监视Location对象的准确性,Criteria.ACCURACY_HIGH返回以米为单位的估计位置精度。
  • passive provider标准应该给你100米以下的误差,这不如GPS可以,但符合你的需要。
  • 您还需要监视位置提供程序的状态,并在用户不可用或禁用时切换到另一个提供程序。
  • getLastKnownLocation(String)也可以很好地匹配这种应用程序:想法是在其他应用程序请求并在系统范围内广播时使用位置更新。

10
投票

回答前两点:

  • 如果启用并且周围没有厚墙,GPS将始终为您提供更精确的位置。
  • 如果位置没有改变,那么你可以调用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())); } 并立即检索位置。

使用替代方法:

您可以尝试获取正在使用的单元ID或所有相邻单元格

http://www.location-api.com/

您可以通过几个开放数据库(例如,http://opencellid.org/getLastKnownLocation(String))引用单元格位置


策略是在读取位置时读取塔ID列表。然后,在下一个查询中(在您的应用中10分钟),再次阅读它们。如果至少有一些塔是相同的,那么使用onLocationChanged()是安全的。如果他们不是,那么等待this approach。这避免了对该位置的第三方数据库的需要。你也可以试试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); }


9
投票

这是我的解决方案,运作得相当好:

here

7
投票

位置准确性主要取决于所使用的位置提供商:

  1. GPS - 将为您提供几米精度(假设您有GPS接收)
  2. 无线网络 - 将为您提供几百米的精度
  3. Cell Network - 会给你带来非常不准确的结果(我已经看到了4公里的偏差...)

如果您正在寻找它的准确性,那么GPS是您唯一的选择。

我已经阅读了一篇非常有益的文章,关于它onLocationChanged

至于GPS超时 - 60秒就足够了,在大多数情况下甚至太多了。我认为30秒是可以的,有时甚至不到5秒......

如果你只需要一个位置,我建议你在https://github.com/mcharmas/Android-ReactiveLocation方法中,一旦你收到更新,你将取消注册听众,避免不必要的使用GPS。


3
投票

Android-ReactiveLocation库是另一个处理Android位置的好库。

在精彩的RxJava Observable中包含Google Play Service API的小型库,可将样板文件降至最低。

https://developers.google.com/android/reference/com/google/android/gms/location/FusedLocationProviderApi


3
投票

目前我正在使用,因为这对我的应用程序获取位置和计算距离是可靠的......我正在使用这个用于我的出租车应用程序。

使用谷歌开发人员开发的融合API,GPS传感器,磁力计,加速度计的融合也使用Wifi或小区位置来计算或估计位置。它还能够准确地在建筑物内部提供位置更新。有关详细信息,请链接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; } } }

http://code.google.com/p/mytracks/

2
投票

嗨,这是一个链接,它将能够提供整个源代码以整合gps位置的链接,该位置将能够通过gps跟踪任何人,ti将通知:

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


1
投票

我使用谷歌建议的最新位置拉动方法(使用FusedLocationProviderClient)在互联网上搜索更新(过去一年)的答案。我终于登陆了这个:

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

我创建了一个新项目并复制了大部分代码。繁荣。有用。我认为没有任何弃用的行。

此外,模拟器似乎没有获得GPS位置,我知道。它确实在日志中报告了这一点:“满足所有位置设置。”

最后,如果你想知道(我做过),你不需要谷歌开发者控制台的谷歌地图api密钥,如果你想要的只是GPS位置。

他们的教程也很有用。但我想要一个完整的单页教程/代码示例。他们的教程堆栈,但是当你刚接触到它时会感到困惑,因为你不知道从早期页面中需要哪些部分。

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

最后,记住这样的事情:

我不仅要修改mainActivity.Java。我还必须修改Strings.xml,androidmanifest.xml和正确的build.gradle。还有你的activity_Main.xml(但那部分对我来说很容易)。

我需要添加像这样的依赖项:implementation'com.google.android.gms:play-services-location:11.8.0',并更新我的android studio SDK的设置以包含google play服务。 (文件设置外观系统设置android SDK SDK工具检查google play服务)。

更新:android模拟器似乎确实获得了位置和位置更改事件(当我更改了sim设置中的值时)。但我最好的和最初的结果是在一个实际的设备上。因此,在实际设备上进行测试可能最容易。

© www.soinside.com 2019 - 2024. All rights reserved.