好让在Android的用户的位置的方法 [英] Good way of getting the user's location in Android

查看:216
本文介绍了好让在Android的用户的位置的方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

的问题:

获得内的阈值的用户的当前位置ASAP和同时节省电池

为什么这个问题是一个问题:

首先,机器人有两个供应商;网络和GPS。有时,网络是更好的,有时GPS是更好的。

按好我的意思是速度与准确率。
我愿意牺牲数米的精度,如果我能得到的位置几乎是一瞬间,并没有打开GPS。

其次,如果你要求更新位置改变不了什么,如果当前位置是稳定的发送。

谷歌已经在这里确定的最佳位置的一个例子:<一href="http://developer.android.com/guide/topics/location/obtaining-user-location.html#BestEstimate">http://developer.android.com/guide/topics/location/obtaining-user-location.html#BestEstimate
但我认为这是附近没有地方好,因为它应该/可以。

我有点困惑,为什么谷歌还没有对位置的标准化API,开发人员不应该去关心那里的位置是,你应该指定你想要的,手机应该选择适合你。

我需要哪些方面的帮助:

我需要找到一个很好的方式,确定了最佳的位置,也许尽管一些启发或者通过一些第三方库。

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

应用程序的背景:

应用程序将在一个固定的时间间隔收集用户的位置(让说,每10分钟左右),并将其发送到服务器。
这个应用程序应该保存尽可能多的电池,尽可能的位置应该有X(50-100?)米的精度。

我们的目标是以后能在白天绘制在地图上用户的路径,所以我需要足够的精度为

其他:

你认为什么是对期望和接受的精度合理的值?
我一直在使用百米的接受,并根据需要30M,这是过分的要求么?
我希望能够在稍后绘制在地图上用户的路径。
为100m的期望和500米为接受更好的?

另外,现在我有一个最大的每个位置更新60秒的GPS,这是太短得到一个位置,如果你是在室内使用的可能200米的准确性?


这是我目前的code,任何反馈是AP preciated(除了缺乏错误检查是TODO):

 保护无效runTask(){
    最后LocationManager locationManager =(LocationManager)上下文
            .getSystemService(Context.LOCATION_SERVICE);
    updateBestLocation(locationManager
            .getLastKnownLocation(LocationManager.GPS_PROVIDER));
    updateBestLocation(locationManager
            .getLastKnownLocation(LocationManager.NETWORK_PROVIDER));
    如果(getLocationQuality(bestLocation)!= LocationQuality.GOOD){
        活套prepare()。
        setLooper(Looper.myLooper());
        //定义一个监听器,响应位置更新
        LocationListener的LocationListener的=新LocationListener的(){

            公共无效onLocationChanged(位置定位){
                updateBestLocation(位置);
                如果(getLocationQuality(bestLocation)!= LocationQuality.GOOD)
                    返回;
                // 做完了
                活套L = getLooper();
                如果(升!= NULL)l.quit();
            }

            公共无效onProviderEnabled(字符串提供商){}

            公共无效onProviderDisabled(字符串提供商){}

            公共无效onStatusChanged(字符串商,INT地位,
                    捆绑演员){
                // TODO自动生成方法存根
                Log.i(LocationCollector,失败);
                活套L = getLooper();
                如果(升!= NULL)l.quit();
            }
        };
        //注册的位置管理器监听器接收
        //位置更新
        locationManager.requestLocationUpdates(
                LocationManager.GPS_PROVIDER,1000,1,LocationListener的,
                Looper.myLooper());
        locationManager.requestLocationUpdates(
                LocationManager.NETWORK_PROVIDER,1000,1,
                LocationListener的,Looper.myLooper());
        计时器t =新的Timer();
        t.schedule(新的TimerTask(){

            @覆盖
            公共无效的run(){
                活套L = getLooper();
                如果(升!= NULL)l.quit();
                // Log.i(LocationCollector
                //停止收集因超时);
            }
        },MAX_POLLING_TIME);
        Looper.loop();
        t.cancel();
        locationManager.removeUpdates(LocationListener的);
        setLooper(空);
    }
    如果(getLocationQuality(bestLocation)!= LocationQuality.BAD)
        sendUpdate(locationToString(bestLocation));
    其他Log.w(LocationCollector,无法获得一个位置);
}

私人枚举LocationQuality {
    坏了,接受了,好;

    公共字符串的toString(){
        如果(这种== GOOD)返回良好;
        否则,如果(这种==接收)返回接受;
        否则返回坏;
    }
}

私人LocationQuality getLocationQuality(位置定位){
    如果(位置== NULL)返回LocationQuality.BAD;
    如果返回LocationQuality.BAD(location.hasAccuracy()!);
    长currentTime的= System.currentTimeMillis的();
    如果(currentTime的 -  location.getTime()&其中; MAX_AGE
            &功放;&安培; location.getAccuracy()&其中; = GOOD_ACCURACY)
        返回LocationQuality.GOOD;
    如果(location.getAccuracy()&其中; = ACCEPTED_ACCURACY)
        返回LocationQuality.ACCEPTED;
    返回LocationQuality.BAD;
}

私人同步无效updateBestLocation(位置定位){
    bestLocation = getBestLocation(位置,bestLocation);
}

谷歌的例子// pretty的多未修改版本
受保护的位置getBestLocation(地点的位置,
        地点currentBestLocation){
    如果(currentBestLocation == NULL){
        //一个新的位置总是比没有位置更好
        返回的位置;
    }
    如果(位置== NULL)返回currentBestLocation;
    //检查新的位置锁定是否是新还是旧,
    长timeDelta = location.getTime() -  currentBestLocation.getTime();
    布尔isSignificantlyNewer = timeDelta&GT; TWO_MINUTES;
    布尔isSignificantlyOlder = timeDelta&LT; -TWO_MINUTES;
    布尔isNewer = timeDelta&GT; 0;
    //如果它已经超过两分钟,因为当前的位置,使用
    //新位置
    //因为用户可能已经移动
    如果(isSignificantlyNewer){
        返回的位置;
        //如果新位置是超过两分钟以上,它必须是
        //差
    }否则,如果(isSignificantlyOlder){
        返回currentBestLocation;
    }
    //检查新的位置锁定是否或多或少准确
    INT accuracyDelta =(INT)(location.getAccuracy() -  currentBestLocation
            .getAccuracy());
    布尔isLessAccurate = accuracyDelta&GT; 0;
    布尔isMoreAccurate = accuracyDelta&LT; 0;
    布尔isSignificantlyLessAccurate = accuracyDelta&GT; 200;
    //检查旧的和新的位置都来自同一个供应商
    布尔isFromSameProvider = isSameProvider(location.getProvider(),
            currentBestLocation.getProvider());
    //确定位置使用质量及时性的组合,
    // 准确性
    如果(isMoreAccurate){
        返回的位置;
    }否则如果(isNewer&安培;&安培;!isLessAccurate){
        返回的位置;
    }否则如果(isNewer&安培;&安培;!isSignificantlyLessAccurate
            &功放;&安培; isFromSameProvider){
        返回的位置;
    }
    返回bestLocation;
}

/ **两个提供检查是否相同* /
私人布尔isSameProvider(字符串provider1,字符串Provider2的){
    如果(provider1 == NULL){
        返回Provider2的== NULL;
    }
    返回provider1.equals(Provider2的);
}
 

解决方案

看起来似乎我们的编码相同的应用程序;-)
这是我目前的执行情况。我仍然在我的GPS上传应用程序的beta测试阶段,因此可能会有很多可能的改进。但它似乎工作pretty的很顺利。

  / **
 *试图让所有供应商中选择最好的位置
 * /
私人的位置getBestLocation(){
    位置gpslocation = getLocationByProvider(LocationManager.GPS_PROVIDER);
    位置networkLocation =
            getLocationByProvider(LocationManager.NETWORK_PROVIDER);
    //如果我们只有一个位置可用,选择很容易
    如果(gpslocation == NULL){
        Log.d(TAG,没有可用的GPS定位。);
        返回networkLocation;
    }
    如果(networkLocation == NULL){
        Log.d(TAG,没有网络位置可用);
        返回gpslocation;
    }
    //一个locationupdate被认为是'老'如果比配置的较旧的
    //更新间隔。这意味着,我们没有得到一个
    //从上次检查该供应商更新
    长老= System.currentTimeMillis的() -  getGPSCheckMilliSecsFrom preFS();
    布尔gpsIsOld =(gpslocation.getTime()&LT;年老的);
    布尔networkIsOld =(networkLocation.getTime()&LT;年老的);
    // GPS是当前和可用,GPS是不是网络更好
    如果(!gpsIsOld){
        Log.d(TAG,返回当前GPS位置);
        返回gpslocation;
    }
    // GPS是老了,我们不能信任它。使用网络位置
    如果(!networkIsOld){
        Log.d(TAG,GPS是老了,网络是最新的,返回的网络);
        返回networkLocation;
    }
    //都是老返回这两个较新的
    如果(gpslocation.getTime()&GT; networkLocation.getTime()){
        Log.d(TAG,无论是老,返回GPS(更新));
        返回gpslocation;
    } 其他 {
        Log.d(TAG,无论是老,返回网络(更新));
        返回networkLocation;
    }
}

/ **
 *从一个特定的提供者获得最后已知位置(网络/ GPS)
 * /
私人的位置getLocationByProvider(字符串提供商){
    位置位置= NULL;
    如果(!isProviderSupported(供应商)){
        返回null;
    }
    LocationManager locationManager =(LocationManager)getApplicationContext()
            .getSystemService(Context.LOCATION_SERVICE);
    尝试 {
        如果(locationManager.isProviderEnabled(供应商)){
            位置= locationManager.getLastKnownLocation(供应商);
        }
    }赶上(抛出:IllegalArgumentException E){
        Log.d(TAG,不能存取权限提供商+供应商);
    }
    返回的位置;
}
 

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

 公共无效的startRecording(){
    gpsTimer.cancel();
    gpsTimer =新的Timer();
    长checkInterval = getGPSCheckMilliSecsFrom preFS();
    长minDistance依旧= getMinDistanceFrom preFS();
    //接收更新
    LocationManager locationManager =(LocationManager)getApplicationContext()
            .getSystemService(Context.LOCATION_SERVICE);
    对于(字符串S:locationManager.getAllProviders()){
        locationManager.requestLocationUpdates(S,checkInterval,
                minDistance依旧,新LocationListener的(){

                    @覆盖
                    公共无效onStatusChanged(字符串提供商,
                            INT地位,捆绑演员){}

                    @覆盖
                    公共无效onProviderEnabled(字符串提供商){}

                    @覆盖
                    公共无效onProviderDisabled(字符串提供商){}

                    @覆盖
                    公共无效onLocationChanged(位置定位){
                        //如果这是一个全球定位系统的位置,我们可以用它
                        如果(location.getProvider()。等于(
                                LocationManager.GPS_PROVIDER)){
                            doLocationUpdate(位置,真正的);
                        }
                    }
                });
        // //Toast.makeText(this,全球定位系统服务启动,
        // Toast.LENGTH_LONG).show();
        gps_recorder_running = TRUE;
    }
    //启动GPS接收器线程
    gpsTimer.scheduleAtFixedRate(新的TimerTask(){

        @覆盖
        公共无效的run(){
            位置位置= getBestLocation();
            doLocationUpdate(位置,假);
        }
    },0,checkInterval);
}

公共无效doLocationUpdate(位置L,布尔力){
    长minDistance依旧= getMinDistanceFrom preFS();
    Log.d(TAG,接收到的更新:+ 1);
    如果(L == NULL){
        Log.d(TAG,空位置);
        如果(力)
            Toast.makeText(这一点,当前位置不可用,
                    Toast.LENGTH_SHORT).show();
        返回;
    }
    如果(lastLocation!= NULL){
        浮距离= l.distanceTo(lastLocation);
        Log.d(TAG,距离到最后:+距离);
        如果(l.distanceTo(lastLocation)&LT; minDistance依旧&安培;&安培;!力){
            Log.d(TAG,位置并没有改变);
            返回;
        }
        如果(l.getAccuracy()&GT; = lastLocation.getAccuracy()
                &功放;&安培; l.distanceTo(lastLocation)&LT; l.getAccuracy()&安培;&安培; !力) {
            Log.d(TAG,
                    准确度变得更糟,我们仍然
                      +的精度范围之内..不更新);
            返回;
        }
        如果(l.getTime()&LT; = lastprovidertimestamp和放大器;&安培;!力){
            Log.d(TAG,时间戳不是永远比去年);
            返回;
        }
    }
    //上传/这里储存您的位置
}
 

需要考虑的事项:

  • 不要求GPS更新过于频繁,这水渠电池电量。我目前 使用30分钟的默认我的应用程序。

  • 添加一个最小距离上一个已知位置'检查。不这样做,你的观点 将跳来跳去,当GPS不可用,并且该位置被三角 从手机信号塔。或者,你可以检查新的位置精度之外 值从最后一个已知的位置。

The problem:

Getting the user's current location within a threshold ASAP and at the same time conserve battery.

Why the problem is a problem:

First off, android has two providers; network and GPS. Sometimes network is better and sometimes the GPS is better.

By "better" I mean speed vs. accuracy ratio.
I'm willing to sacrifice a few meters in accuracy if I can get the location almost instant and without turning on the GPS.

Secondly, if you request updates for location changes nothing is sent if the current location is stable.

Google has an example of determining the "best" location here: http://developer.android.com/guide/topics/location/obtaining-user-location.html#BestEstimate
But I think it's no where near as good as it should/could be.

I'm kind of confused why google hasn't a normalized API for location, the developer shouldn't have to care where the location is from, you should just specify what you want and the phone should choose for you.

What I need help with:

I need to find a good way to determine the "best" location, maybe though some heuristic or maybe through some 3rd party library.

This does not mean determine the best provider!
I'm probably gonna use all providers and picking the best of them.

Background of the app:

The app will collect the user's location at a fixed interval (let say every 10 minutes or so) and send it to a server.
The app should conserve as much battery as possible and the location should have X (50-100?) meters accuracy.

The goal is to later be able to plot the user's path during the day on a map so I need sufficient accuracy for that.

Misc:

What do you think are reasonable values on desired and accepted accuracies?
I've been using 100m as accepted and 30m as desired, is this to much to ask?
I'd like to be able to plot the user's path on a map later.
Is 100m for desired and 500m for accepted better?

Also, right now I have the GPS on for a maximum of 60 seconds per location update, is this too short to get a location if you're indoors with an accuracy of maybe 200m?


This is my current code, any feedback is appreciated (apart from the lack of error checking which is 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);
}

解决方案

Looks like we're coding the same application ;-)
Here is my current implementation. I'm still in the beta testing phase of my GPS uploader app, so there might be many possible improvements. but it seems to work pretty well so far.

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

Edit: here is the part that requests the periodic updates from the location providers:

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
}

Things to consider:

  • do not request GPS updates too often, it drains battery power. I currently use 30 min as default for my application.

  • add a 'minimum distance to last known location' check. without this, your points will "jump around" when GPS is not available and the location is being triangulated from the cell towers. or you can check if the new location is outside of the accuracy value from the last known location.

这篇关于好让在Android的用户的位置的方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆