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

时间:2010-03-23 23:39:26

标签: android

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

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

但我的应用程序需要更多地工作,就像地图应用程序第一次找到你时 - 从任何可用的提供商处工作,只需检查位置是否过时或过时。

我试图推出自己的代码来执行此操作,但遇到了问题。 (它在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();
}

不幸的是,这找到了网络提供商,但只输出latlng null 30次 - 它似乎永远不会得到一个位置。我甚至都没有得到locationChanged的日志声明。

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

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

似乎暗示网络提供商确实有一些位置信息,我只是没有得到它。

有人可以帮忙吗?我认为工作示例代码对于Android / StackOverflow社区来说是一个有用的资源。

2 个答案:

答案 0 :(得分:6)

你肯定是在努力做到这一点。以下是我正在开发的新应用程序的一些片段。它使用Criteria使所有提供商能够无需成本地返回精确的准确度。

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

如果启用了提供程序,则循环也会尽快请求位置更新。这是我的应用程序的理想选择,但您可以更改此设置以节省电池我修改requestLocationUpdates method的参数。

此代码所具有的优化,Android应用程序上的示例并未真正显示,所有已启用的提供程序都是同时启动。所有提供商都将在onLocationChanged method上返回单独的更新。在我的应用程序中,我在其中一个提供程序返回一个具有足够精确度的位置后删除了位置监听器。

开始位置更新:

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

接收位置更新:

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

位置设置对话框:

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

答案 1 :(得分:5)

生产代码中的

Thread.sleep()是一个严重的代码味道恕我直言。如果你发现你必须这样做,你可能正在做一些不应该那样工作的事情。在这种情况下,我认为这是您的问题的根源 - 您不会让Android返回处理此线程的消息队列以分派它找到的任何位置更新。我怀疑IntentService不适用于你的场景。