【发布时间】:2015-03-29 00:17:54
【问题描述】:
我正在尝试为位置感知应用程序测试 GeoFences。我已经获得了多达 11 次连续的成功,但之后通常会出现数十次失败。重新启动设备没有帮助。卸载/重新安装没有帮助。禁用位置,重新启用位置没有帮助。更改设备的位置精度设置没有帮助。
我已经在 v2.3.4、v4.0.3、v4.4.2、v4.4.4 和 2 个 v5.0.1 物理设备上进行了测试。 Lollipop 设备之一是具有蜂窝服务的设备。其余设备是无 SIM 卡的,仅用于测试。旁注:没有服务的设备很难通过 MockLocation 设置它们的位置,但如果它们设法设置它们的位置,它们几乎不会注册栅栏入口/出口。
已通过模拟位置很好地更新了位置,但没有以任何可靠性触发 GeoFence。
我已经用谷歌搜索过了。我查看了 developer.android.com 网站上的文档。我已经搜索过 StackOverflow。其实很多人提出的建议已经在下面的代码中实现了。
那么,如何使用 MockLocation 可靠地触发 GeoFence?
import android.location.Location;
import android.location.LocationManager;
public class GeoFenceTester extends ActivityInstrumentationTestCase2<GeoFenceTesterActivity> {
public static final String TAG = GeoFenceTester.class.getSimpleName();
private LocationManager locationManager;
private Activity activityUnderTest;
protected void setUp() throws Exception {
super.setUp();
activityUnderTest = getActivity();
/*
MOCK Locations are required for these tests. If the user has not enabled MOCK Locations
on the device or emulator these tests cannot work.
*/
if (Settings.Secure.getInt(activityUnderTest.getContentResolver(), Settings.Secure.ALLOW_MOCK_LOCATION, 0) == 0) {
throw new RuntimeException("Mock locations are currently disabled in Settings - These tests require mock locations");
}
Log.i(TAG, "Setup MOCK Location Providers");
locationManager = (LocationManager) activityUnderTest.getSystemService(Context.LOCATION_SERVICE);
Log.i(TAG, "GPS Provider");
locationManager.addTestProvider(LocationManager.GPS_PROVIDER, false, true, false, false, false, false, false, Criteria.POWER_HIGH, Criteria.ACCURACY_FINE);
locationManager.setTestProviderEnabled(LocationManager.GPS_PROVIDER, true);
Log.i(TAG, "Network Provider");
locationManager.addTestProvider(LocationManager.NETWORK_PROVIDER, true, false, true, false, false, false, false, Criteria.POWER_MEDIUM, Criteria.ACCURACY_FINE);
locationManager.setTestProviderEnabled(LocationManager.NETWORK_PROVIDER, true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
Log.wtf(TAG, String.format("Location Accuracy: %1$d", Settings.Secure.getInt(activityUnderTest.getContentResolver(), Settings.Secure.LOCATION_MODE)));
}
/* Our EventBus for onEvent() callbacks */
EventBus.getDefault().register(this);
}
protected void tearDown() {
/* Our EventBus for onEvent() callbacks */
EventBus.getDefault().unregister(this);
locationManager.removeTestProvider(LocationManager.GPS_PROVIDER);
locationManager.removeTestProvider(LocationManager.NETWORK_PROVIDER);
locationManager = null;
activityUnderTest = null;
super.tearDown();
}
public void testGeoFence() {
/*
Using one or the other or both of these makes no difference.
*/
Location mockGpsLocation = new Location(LocationManager.GPS_PROVIDER);
mockGpsLocation.setLatitude(32.652411);
mockGpsLocation.setLongitude(-79.938063);
mockGpsLocation.setAccuracy(1.0f);
mockGpsLocation.setTime(System.currentTimeMillis());
Location mockNetworkLocation = new Location(LocationManager.NETWORK_PROVIDER);
mockNetworkLocation.setLatitude(32.652411);
mockNetworkLocation.setLongitude(-79.938063);
mockNetworkLocation.setAccuracy(1.0f);
mockNetworkLocation.setTime(System.currentTimeMillis());
/*
setElapsedRealtimeNanos() was added in API 17
*/
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
mockGpsLocation.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
mockNetworkLocation.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
}
try {
Method locationJellyBeanFixMethod = Location.class.getMethod("makeComplete");
if (locationJellyBeanFixMethod != null) {
locationJellyBeanFixMethod.invoke(mockGpsLocation);
locationJellyBeanFixMethod.invoke(mockNetworkLocation);
}
} catch (Exception e) {
// There's no action to take here. This is a fix for Jelly Bean and no reason to report a failure.
}
for (int i = 0; i < 30; i++){
Log.i(TAG, String.format("Iterating over our location ... (%1$d)", i));
locationManager.setTestProviderLocation(LocationManager.GPS_PROVIDER, mockGpsLocation);
locationManager.setTestProviderLocation(LocationManager.NETWORK_PROVIDER, mockNetworkLocation);
try {
Thread.sleep(1000);
} catch (Exception e) {
Log.e(TAG, e.getMessage(), e);
}
}
}
public void onEvent(final GeoFenceUpdateEvent event) {
// This doesn't get called about 9/10 times.
// I have a GeoFence with a 5000m radius around 32.652411, -79.938063
}
public void onEvent(final LocationUpdateEvent event) {
// This gets called without incident and event.getLatitude() & event.getLongitude() are correct.
}
}
【问题讨论】:
-
您是否尝试过通过在围栏外启动模拟位置然后以适合步行/骑自行车/驾驶的速度进出进行测试?
-
起始位置肯定在围栏区域之外。我每 1000 毫秒应用一次位置更新。位置已成功更新,但是栅栏很少触发其进入消息。为了便于阅读,我已经排除了日志记录。我的起始位置通常距离所需位置 89000 多米。大约在第 3 次迭代之后,我收到了一个位置更新事件,我与所需位置的距离约为 0.2m(四舍五入)。
-
所以你是在以宇宙飞船的速度旅行,还是我读错了你的最后一条评论。我认为在内部会有一些限制来防止嘈杂的数据。您能否说明您使用的是 AOSP 位置管理器还是 Google 服务。
-
AOSP(问题已更新)至于我的旅行速度:没关系。我在栅栏外,然后我在栅栏内。
-
更多的旁注,因为您正在使用 AOSP 进行测试 - 当我使用 Google 服务进行测试时,我发现如果我将模拟位置更新限制为“合理”并让结果解决考虑到“噪音”,我能够获得一致的结果,但我的测试场景与您的不同(5-10 秒/更新,单独的应用程序提供模拟,Google 服务)所以也许其他人有想法。跨度>
标签: android unit-testing geolocation android-location geofencing