没有触发的地理围栏具有模拟位置

Bil*_*ote 6 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设备是具有蜂窝服务的设备.其余设备是SIMless的,仅用于测试.旁注:没有服务的设备很难通过MockLocation设置它们的位置,但是如果他们设法设置它们的位置,他们几乎从不注册围栏入口/出口.

位置正在通过Mock Location更新,但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.
    }

}
Run Code Online (Sandbox Code Playgroud)

小智 5

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

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

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

  • 完全禁用位置服务可防止模拟位置在我的应用程序中触发。 (3认同)