【问题标题】:Android: get current location from best available providerAndroid:从最佳可用提供商处获取当前位置
【发布时间】:2010-03-23 23:39:26
【问题描述】:

我有一些 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();
}

不幸的是,这找到了网络提供商,但只输出了 30 次 latlng null - 它似乎根本没有找到位置。我什至从未收到locationChanged 的日志声明。

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

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

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

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

【问题讨论】:

标签: android


【解决方案1】:

你肯定在努力做到这一点。这是我正在开发的一个新应用程序的一些 sn-ps。它使用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();
    }
}

【讨论】:

  • 很好的解决方案!只需指出一件事。这可能是由于 API 的变化,但条件 (providers != null) 必须是 (privders.size() &gt; 0),因为 getProviders(Criteria, boolean) 方法永远不会返回 null,而是可以返回大小为 0 的列表。
  • 我想我记得当我测试这个时它并不准确,但我想我们必须按照文档进行。无论如何,大家应该都知道,Google Play Services 库中的 FusedLocationProvider 比旧的位置类要优越得多。但是,实现方式却大不相同。这种技术有点过时了。
  • 您能告诉我们您是如何构建“Criteria”对象的吗...我还需要一个大概位置(城市精度)
【解决方案2】:

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

【讨论】:

  • 谢谢。我想我会在正常的 Activity 中执行此操作,然后将 lat/lon 传递给 IntentService。我仍然非常想要一个如何检查所有 LocationProviders 并选择最佳位置的示例 - 如果有人有任何示例代码,请发布它......如果没有,我会尝试破解一些东西并将其发布在这里。
  • @CommonsWare - 你如何建议我在不使用 Thread.sleep() 的情况下实现以下内容? “继续检查位置更新 30 秒;如果到那时还没有找到最新位置,则放弃并使用虚拟值。”
  • Thread.sleep 可以在生产代码中使用的原因有一百万零一个(尽管我在这里不一定说)。
猜你喜欢
  • 2011-02-24
  • 2013-03-16
  • 1970-01-01
  • 2014-09-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-17
  • 1970-01-01
相关资源
最近更新 更多