programing

Android에서 사용자의 위치를 얻는 좋은 방법

padding 2023. 8. 6. 09:54
반응형

Android에서 사용자의 위치를 얻는 좋은 방법

문제:

사용자의 현재 위치를 가능한 한 빨리 임계값 내로 지정하고 동시에 배터리를 절약할 수 있습니다.

문제가 발생하는 이유:

먼저 안드로이드에는 네트워크와 GPS라는 두 개의 프로바이더가 있습니다. 때로는 네트워크가 더 좋고 때로는 GPS가 더 좋습니다.

"더 나은"이란 속도 대 정확도 비율을 의미합니다.
GPS를 켜지 않고 거의 즉시 위치를 파악할 수 있다면 몇 미터의 정확도를 희생할 용의가 있습니다.

둘째, 위치 변경에 대한 업데이트를 요청해도 현재 위치가 안정적이면 아무것도 전송되지 않습니다.

Google은 여기에서 "최적의" 위치를 결정하는 예를 보여줍니다. http://developer.android.com/guide/topics/location/obtaining-user-location.html#BestEstimate
하지만 저는 그것이 필요한 만큼 좋은 곳은 아니라고 생각합니다.

나는 구글이 위치에 대한 표준화된 API를 가지고 있지 않은 이유가 약간 혼란스럽습니다. 개발자는 위치가 어디인지 신경쓰지 않아도 됩니다. 당신이 원하는 것을 지정하고 전화기가 당신을 위해 선택해야 합니다.

도움이 필요한 사항:

휴리스틱이나 타사 라이브러리를 통해 "최적의" 위치를 결정할 수 있는 좋은 방법을 찾아야 합니다.

이것은 최고의 공급자를 결정하는 것을 의미하지 않습니다!
저는 아마 모든 공급자를 이용해서 그들 중에서 가장 좋은 것을 고를 것입니다.

앱 배경:

앱은 사용자의 위치를 정해진 간격(10분 정도 간격으로)으로 수집하여 서버로 보냅니다.
앱은 가능한 한 많은 배터리를 보존해야 하며 위치는 X(50-100?) 미터의 정확도를 가져야 합니다.

목표는 나중에 지도에 낮 동안 사용자의 경로를 표시할 수 있도록 하기 위해 충분한 정확도가 필요합니다.

기타:

원하는 정확도와 허용되는 정확도에 대한 합리적인 값은 무엇이라고 생각하십니까?
, 은 너무 많은 입니까? 100m 합격는, 30m 는데왔, 가요인무리해사용로희대망는?▁i요,▁30?
나중에 지도에 사용자의 경로를 표시할 수 있으면 좋겠습니다.
100m는 희망, 500m는 합격이 더 나은가요?

또한 현재 위치 업데이트당 최대 60초까지 GPS를 켜고 있는데, 실내에서 200m 정도의 정확도로 위치를 파악하기에는 너무 짧은가요?


이 코드는 현재 사용 중인 코드이며, 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);
}

하는 것 요 ;-)우리같케션코을딩하고있는것다같니습이 ;-
현재 구현한 내용은 다음과 같습니다.저는 아직 GPS 업로더 앱 베타 테스트 단계라 개선 가능한 부분이 많을 수도 있지만, 아직까지는 잘 되는 것 같습니다.

/**
 * 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를 사용할 수 없고 셀 타워에서 위치를 삼각 측량할 때 포인트가 "점프"됩니다.또는 새 위치가 마지막으로 알려진 위치의 정확도 값을 벗어나는지 확인할 수 있습니다.

앱에 적합한 위치 공급자를 선택하려면 기준 개체를 사용할 수 있습니다.

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으로 설정합니다.

더 많은 생각들

  • 위치의 추정 정확도를 미터 단위로 반환하는 Location.getAccuracy()사용하여 위치 개체의 정확도를 모니터링할 수 있습니다.
  • 그자리의 Criteria.ACCURACY_HIGH기준은 GPS만큼 좋지는 않지만 필요에 맞는 100m 미만의 오류를 제공해야 합니다.
  • 또한 위치 공급자의 상태를 모니터링하고 사용자가 사용할 수 없거나 사용하지 않도록 설정한 경우 다른 공급자로 전환해야 합니다.
  • 수동 공급자는 이러한 종류의 애플리케이션에도 적합할 수 있습니다. 이 아이디어는 다른 앱에서 요청할 때마다 위치 업데이트를 사용하고 시스템 전체에서 방송하는 것입니다.

처음가지 사항에 대한 답변:

  • GPS는 활성화되어 있고 주위에 두꺼운 벽이 없는 경우 항상 더 정확한 위치를 제공합니다.

  • 위치가 변경되지 않은 경우 getLastKnownLocation(String)을 호출하여 위치를 즉시 검색할 수 있습니다.

대안적 접근법 사용:

사용 중인 ID 또는 모든 인접 셀을 가져올 수 있습니다.

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

그런 다음 여러 개의 개방형 데이터베이스(예: http://www.location-api.com/ 또는 http://opencellid.org/ )를 통해 셀 위치를 참조할 수 있습니다.


전략은 위치를 읽을 때 타워 ID 목록을 읽는 것입니다.그런 다음, 다음 질문(앱에서 10분)에서 다시 읽습니다.적어도 몇몇 타워들이 같다면, 사용하는 것이 안전합니다.getLastKnownLocation(String)그렇지 않다면, 기다리십시오.onLocationChanged()이렇게 하면 위치에 타사 데이터베이스가 필요하지 않습니다. 방법을 사용해 볼 수도 있습니다.

이 솔루션은 매우 잘 작동합니다.

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

위치 정확도는 주로 사용되는 위치 공급자에 따라 달라집니다.

  1. GPS - 몇 미터의 정확도를 얻을 수 있습니다(GPS 수신 기능이 있다고 가정).
  2. 와이파이 - 수백 미터의 정확도를 얻을 수 있습니다.
  3. 셀 네트워크 - 매우 부정확한 결과를 얻을 수 있습니다(최대 4km 편차를 보았습니다...)

당신이 찾고 있는 것이 정확성이라면, GPS가 당신의 유일한 선택입니다.

저는 여기서 그것에 대한 매우 유익한 기사를 읽었습니다.

GPS 타임아웃에 관해서는 - 60초면 충분하며, 대부분의 경우 너무 많습니다.30초 정도면 괜찮을 것 같고 가끔은 5초도 안 될 때도 있어요...

만약 당신이 하나의 장소만 필요하다면, 나는 당신에게 그것을 제안하고 싶습니다.onLocationChanged방법은 일단 업데이트를 받으면 수신기 등록을 취소하고 GPS의 불필요한 사용을 방지합니다.

이것이 위치를 파악하고 애플리케이션의 거리를 계산하는 데 신뢰할 수 있기 때문에 현재 사용하고 있습니다... 택시 애플리케이션에 사용하고 있습니다.

구글 개발자가 GPS 센서, 자력계, 가속도계를 융합하여 개발한 퓨전 API를 사용하고 와이파이나 셀 위치를 이용하여 위치를 계산하거나 추정합니다.또한 건물 내부에서도 위치 업데이트를 정확하게 제공할 수 있습니다.자세한 내용은 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;
        }
    }
}

(FusedLocationProviderClient를 사용하기 위해) Google에서 제안한 최신 위치 풀 방법을 사용하여 인터넷을 검색하여 최신(작년) 답변을 찾았습니다.마침내 이것에 도달했습니다.

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

저는 새로운 프로젝트를 만들고 이 코드의 대부분을 복사했습니다.쾅. 효과가 있어요.그리고 저는 어떤 추천할 만한 대사도 없이 생각합니다.

또한 시뮬레이터가 GPS 위치를 파악하지 못하는 것으로 알고 있습니다.로그에 "모든 위치 설정이 충족되었습니다."라고 보고하기까지 했습니다.

그리고 마지막으로, 만약 당신이 GPS 위치만 알고 싶다면, 구글 개발자 콘솔에서 구글 지도 api 키가 필요하지 않습니다.

또한 유용한 것은 그들의 튜토리얼입니다.하지만 저는 전체 한 페이지 튜토리얼/코드 예제를 원했습니다.튜토리얼이 쌓이지만 처음 사용하는 경우 이전 페이지에서 필요한 부분이 무엇인지 모르기 때문에 혼동을 참조하십시오.

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

그리고 마지막으로, 다음과 같은 것들을 기억하세요:

메인 Activity를 수정해야 할 뿐만 아니라,Java. Strings.xml, Androidmanifest.xml 및 올바른 build.gradle도 수정해야 했습니다.그리고 당신의 activity_Main.xml (그러나 그 부분은 나에게 쉬웠습니다.

구현 'com.google.android.gms:play-services-location:11.8.0'과 같은 종속성을 추가해야 했고, 구글 플레이 서비스를 포함하도록 안드로이드 스튜디오 SDK의 설정을 업데이트해야 했습니다. (파일 설정 모양 시스템 설정 안드로이드 SDK 도구는 구글 플레이 서비스를 확인하십시오.)

업데이트: 안드로이드 시뮬레이터는 (심의 설정에서 값을 변경했을 때) 위치 및 위치 변경 이벤트를 받은 것 같습니다.하지만 저의 최고의 첫 번째 결과는 실제 장치에 있었습니다.따라서 실제 장치에서 테스트하는 것이 가장 쉬울 것입니다.

최근 리팩터링을 통해 코드의 위치를 파악하고, 좋은 아이디어를 배우고, 마침내 비교적 완벽한 라이브러리와 데모를 달성했습니다.

@그리피우스의 대답은 좋습니다.

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

완벽한 구현: https://github.com/bingerz/FastLocation/blob/master/fastlocationlib/src/main/java/cn/bingerz/fastlocation/FastLocation.java

1. @Griphius 솔루션 아이디어에 감사드리며, 전체 코드도 공유합니다.

2.위치를 입력할 때마다 업데이트를 제거하는 것이 좋습니다. 그렇지 않으면 전화 상태 표시줄에 항상 위치 아이콘이 표시됩니다.

제 경험으로 볼 때, GPS가 없는 경우를 제외하고는 GPS 수정을 하는 것이 가장 좋습니다.저는 다른 위치 제공자들에 대해서는 잘 모르지만, GPS의 경우 약간의 게토 정밀도를 측정하는 데 사용할 수 있는 몇 가지 요령이 있다는 것을 알고 있습니다.고도는 종종 신호이기 때문에 터무니없는 값을 확인할 수 있습니다.Android 위치 수정에 대한 정확도 측정이 있습니다.또한 사용된 위성의 수를 볼 수 있는 경우 정밀도를 나타낼 수도 있습니다.

정확도를 더 잘 파악하는 흥미로운 방법은 10초 동안 ~1/초와 같은 일련의 수정 사항을 매우 신속하게 요청한 다음 1~2분 동안 절전 모드로 전환하는 것입니다.제가 참석한 한 강연은 어떤 안드로이드 기기들은 어쨌든 이것을 할 것이라고 믿게 만들었습니다.그런 다음 특이치를 제거하고(칼만 필터가 여기서 언급되었다고 들었습니다) 일종의 센터링 전략을 사용하여 단일 수정을 얻습니다.

여기에 도달하는 깊이는 요구사항이 얼마나 어려운지에 따라 달라집니다.만약 당신이 가능한 한 최고의 위치를 얻기 위해 특별히 엄격한 요구사항을 가지고 있다면, GPS와 네트워크 위치가 사과와 오렌지만큼 비슷하다는 것을 알게 될 것이라고 생각합니다.또한 GPS는 기기마다 크게 다를 수 있습니다.

Skyhook(http://www.skyhookwireless.com/) 에는 Google이 제공하는 표준보다 훨씬 빠른 위치 공급자가 있습니다.당신이 찾고 있는 것일 수도 있습니다.저는 그들과 관계가 없습니다.

언급URL : https://stackoverflow.com/questions/6181704/good-way-of-getting-the-users-location-in-android

반응형