Android:从最佳可用提供商获取当前位置

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2504429/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 06:05:36  来源:igfitidea点击:

Android: get current location from best available provider

android

提问by AP257

I have some Android code that needs to get the best available location QUICKLY, from GPS, network or whatever is available. Accuracy is less important than speed.

我有一些 Android 代码需要从 GPS、网络或任何可用的地方快速获取最佳可用位置。准确性不如速度重要。

Getting the best available location is surely a really standard task. Yet I can't find any code to demonstrate it. The Android location code expects you to specify criteria, register for updates, and wait - which is fine if you have detailed criteria and don't mind waiting around.

获得最佳可用位置无疑是一项非常标准的任务。但是我找不到任何代码来演示它。Android 位置代码要求您指定条件、注册更新并等待 - 如果您有详细的条件并且不介意等待,这很好。

But my app needs to work a bit more like the Maps app does when it first locates you - work from any available provider, and just check the location isn't wildly out of date or null.

但是我的应用程序需要更像地图应用程序在首次定位您时所做的工作 - 从任何可用的提供程序工作,只需检查位置是否过时或为空。

I've attempted to roll my own code to do this, but am having problems. (It's inside an IntentService where an upload happens, if that makes any difference. I've included all the code for info.) What's wrong with this code?

我试图推出自己的代码来做到这一点,但遇到了问题。(它在 IntentService 中,在那里发生上传,如果这有什么不同。我已经包含了所有信息代码。)这段代码有什么问题?

@Override
protected void onHandleIntent(Intent arg0) {
    testProviders();
    doUpload();
}
private boolean doUpload() {
       int j = 0;
       // check if we have accurate location data yet - wait up to 30 seconds
       while (j < 30) {
           if ((latString == "") || (lonString == "")) {
               Log.d(LOG_TAG, "latlng null");
               Thread.sleep(1000);
               j++;
       } else {
                Log.d(LOG_TAG, "found lat " + latString + " and lon " + lonString);
            break;
       }
       //do the upload here anyway, with or without location data
       //[code removed for brevity]
}
public boolean testProviders() {
    Log.e(LOG_TAG, "testProviders");
    String location_context = Context.LOCATION_SERVICE;
    locationmanager = (LocationManager) getSystemService(location_context);
    List<String> providers = locationmanager.getProviders(true);
    for (String provider : providers) {
        Log.e(LOG_TAG, "registering provider " + provider);
        listener = new LocationListener() {
            public void onLocationChanged(Location location) {
                // keep checking the location - until we have
                // what we need
                //if (!checkLoc(location)) {
                Log.e(LOG_TAG, "onLocationChanged");
                locationDetermined = checkLoc(location);
                //}
            }
            public void onProviderDisabled(String provider) {
            }
            public void onProviderEnabled(String provider) {
            }
            public void onStatusChanged(String provider, int status,
                    Bundle extras) {
            }
        };
        locationmanager.requestLocationUpdates(provider, 0,
                0, listener);
    }
    Log.e(LOG_TAG, "getting updates");
    return true;
}
private boolean checkLoc(Location location) {
    float tempAccuracy = location.getAccuracy();
    int locAccuracy = (int) tempAccuracy;
    Log.d(LOG_TAG, "locAccuracy = " + locAccuracy);
    if ((locAccuracy != 0) && (locAccuracy < LOCATION_ACCURACY)) {
        latitude = location.getLatitude();
        longitude = location.getLongitude();
        latString = latitude.toString();
        lonString = longitude.toString();
        return true;
    }
    return false;
}
public void removeListeners() {
    // Log.e(LOG_TAG, "removeListeners");
    if ((locationmanager != null) && (listener != null)) {
        locationmanager.removeUpdates(listener);
    }
    locationmanager = null;
    // Log.d(LOG_TAG, "Removed " + listener.toString());
}
@Override
public void onDestroy() {
    super.onDestroy();
    removeListeners();
}

Unfortunately, this finds the network provider, but only ever outputs latlng null30 times - it never seems to get a location at all. I never even get a log statement of locationChanged.

不幸的是,这找到了网络提供商,但只输出了latlng null30 次——它似乎根本没有找到位置。我什至从未得到过locationChanged.

It's funny, because from ddms I can see output like:

这很有趣,因为从 ddms 我可以看到如下输出:

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

seeming to suggest that the network provider does have some location info after all, I'm just not getting at it.

似乎表明网络提供商毕竟有一些位置信息,我只是不明白。

Can anyone help? I think working example code would be a useful resource for the Android/StackOverflow community.

任何人都可以帮忙吗?我认为工作示例代码将是 Android/StackOverflow 社区的有用资源。

回答by jophde

You are definitely trying to do this the hard way. Here are some snippets from a new app I am working on. It uses Criteriato get all providers capable of returning a fine level of accuracy without a cost.

你肯定是在努力地做到这一点。以下是我正在开发的一个新应用程序的一些片段。它使用Criteria来让所有提供者能够免费返回良好的准确度。

If no providers are enabled a dialog is displayed that prompts the user to turn on their location settings. If the user hits ok an Intent is actually fired that sends them to the settings on their phone. If there are providers enabled the app takes the most recent last known locationfrom any of the enabled providers. For my app I just need to know what general area the user is in and it's likely that the last known location is from their home area.

如果未启用任何提供程序,则会显示一个对话框,提示用户打开其位置设置。如果用户点击确定,则实际上会触发 Intent,将他们发送到手机上的设置。如果启用了提供程序,该应用程序会从任何已启用的提供程序中获取最近的已知位置。对于我的应用程序,我只需要知道用户所在的一般区域,并且最后一个已知位置很可能来自他们的家乡区域。

If providers are enabled the loop also requests location updates as quickly as possible. This is ideal for my app but you can change this to conserve battery my modifying the arguments to the requestLocationUpdates method.

如果提供者被启用,循环也会尽快请求位置更新。这对我的应用程序来说是理想的,但您可以更改它以节省电池,我修改requestLocationUpdates 方法的参数。

The optimization that this code has that the examples on the Android app don't really show is that all of the enabled providers are started simultaneously. All of the providers will return separate updates on to the onLocationChanged method. In my app I remove the location listener after one of the providers returns a location with a good enough accuracy.

这段代码有对Android应用的例子并不真正显示的优化是所有启用的供应商启动同步。所有提供者都将向onLocationChanged 方法返回单独的更新。在我的应用程序中,我在其中一个提供者返回一个足够准确的位置后删除了位置侦听器。

Start Location Updates:

开始位置更新:

void getCurrentLocation() {
    List<String> providers = locationManager.getProviders(criteria, true);
    if (providers != null) {
        Location newestLocation = null;
        for (String provider : providers) {
            Location location = locationManager.getLastKnownLocation(provider);
            if (location != null) {
                if (newestLocation == null) {
                    newestLocation = location;
                } else {
                    if (location.getTime() > newestLocation.getTime()) {
                        newestLocation = location;
                    }
                }
                locationManager.requestLocationUpdates(provider, 0, 0, this);
            }
        }
    } else {
        LocationDialogFragment dialog = new LocationDialogFragment();
        dialog.show(getSupportFragmentManager(),
            LocationDialogFragment.class.getName());
    }
}

Receive Location Update:

接收位置更新:

@Override
public void onLocationChanged(Location location) {
    float bestAccuracy = -1f;
    if (location.getAccuracy() != 0.0f
        && (location.getAccuracy() < bestAccuracy) || bestAccuracy == -1f) {
        if (location.getAccuracy() < Const.MIN_ACCURACY) {
            locationManager.removeUpdates(this);
        }
    }
    bestAccuracy = location.getAccuracy();
}

Location Settings Dialog:

位置设置对话框:

public class LocationDialogFragment extends DialogFragment {

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        builder.setMessage(R.string.location_dialog_message)
                .setPositiveButton(R.string.location_dialog_positive_button,
                    new OnClickListener() {

                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            Intent settingsIntent = new Intent(
                                    Settings.ACTION_LOCATION_SOURCE_SETTINGS);
                            startActivity(settingsIntent);
                        }
                    })
                .setNegativeButton(R.string.location_dialog_negative_button,
                    new OnClickListener() {

                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            Toast.makeText(getActivity(),
                                R.string.no_location_message, Toast.LENGTH_LONG)
                                    .show();
                        }
                    });
        return builder.create();
    }
}

回答by CommonsWare

Thread.sleep()in production code is a serious code smell IMHO. If you find you're having to do that, you're probably doing something that's not supposed to work that way. In this case, I think it's the source of your problem -- you're not letting Android go back to process this thread's message queue to dispatch any location updates it finds. I suspect an IntentServiceis just not going to work for your scenario.

Thread.sleep()在生产代码中是一种严重的代码气味恕我直言。如果你发现你不得不这样做,你可能正在做一些不应该这样工作的事情。在这种情况下,我认为这是您的问题的根源——您不会让 Android 回去处理该线程的消息队列以调度它找到的任何位置更新。我怀疑 anIntentService不适用于您的场景。