android - GeoFence 未触发 w/模拟位置

标签 android unit-testing geolocation android-location geofencing

我正在尝试针对位置感知应用程序测试 GeoFences。我已经获得了多达 11 次连续成功,但通常随之而来的是数十次失败。重新启动设备没有帮助。卸载/重新安装没有帮助。禁用位置,重新启用位置没有帮助。更改设备的位置精度设置没有帮助。

我已经在 v2.3.4、v4.0.3、v4.4.2、v4.4.4 和 2 v5.0.1 物理设备上进行了测试。 Lollipop 设备之一是具有蜂窝服务的设备。其余设备无 SIM 卡,仅用于测试。旁注:没有服务的设备很难通过 MockLocation 设置它们的位置,但如果它们设法设置它们的位置,它们几乎从不注册栅栏入口/导出。

通过 Mock Location 可以很好地更新位置,但是 GeoFence(s) 没有被可靠地触发。

我已经用谷歌搜索过了。我查看了 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.
    }

}

最佳答案

Geofence 管理器将平等地考虑所有系统范围的位置请求,而不仅仅是您的特定应用程序发出的请求。如果您的设备上有任何其他应用程序或服务当前正在使用定位服务,则这些外部应用程序会生成触发地理围栏时考虑使用的纬度/经度位置。

鉴于您正在生成模拟位置,外部应用程序或服务可能会生成冲突的位置更新,并且模拟位置和真实位置之间的这些跳跃会导致您的地理围栏测试结果不稳定。

您可以通过在设置中禁用您的真实位置服务并仅使用模拟位置来确认这一点,或者查找当前可能正在使用位置服务的任何应用程序并在这些特定应用程序被停止或禁用后重新测试。

关于android - GeoFence 未触发 w/模拟位置,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28220366/

相关文章:

android - ANT 构建错误

java - 从处理程序调用时,asynctask 未获取 arraylist

asp.net-mvc - 如何将 Moq 与 Entity Framework IDbSet Count() 等方法一起使用

javascript - 如何使用 gulp test 和 karma 在浏览器中调试 Javascript 测试文件

java - Mockito - 模拟子类方法调用

mysql - Haversine 公式的不同结果

没有 TLS 的 Android P : network-security-config: cleartextTrafficPermitted not possible for IP (only domain)

android-layout - Android layout-sw600dp 不适用于 8"Galaxy Tab

android - Geofences 不触发(pendingintents 和 broadcastreceiver)

python - 使用 Google 从 python 查找纬度/经度