地理围栏未触发,使用模拟位置

7
我正在尝试为一个位置感知应用程序测试地理围栏。我已经连续获得了11次成功,但通常会接连失败几十次。重新启动设备没有帮助。卸载/重新安装也没有帮助。禁用位置,重新启用位置也没有帮助。更改设备的位置精度设置也没有帮助。
我已经在v2.3.4、v4.0.3、v4.4.2、v4.4.4和2个v5.0.1物理设备上进行了测试。其中一个Lollipop设备是带有蜂窝服务的设备。其余设备没有SIM卡,仅用于测试。顺便说一句:没有服务的设备很难通过MockLocation设置它们的位置,但如果它们成功设置了它们的位置,它们几乎从不注册围栏的进入/退出。
位置通过Mock Location更新得足够好,但地理围栏没有任何可靠性地被触发。
我已经谷歌过了。我已经查看了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.
    }

}

你尝试过通过在围栏之外开始模拟位置,然后以步行/骑行/驾车的速度进入/退出来进行测试吗? - Morrison Chang
起始位置肯定在围栏区域之外。我每1000毫秒应用一次位置更新。位置正在成功更新,但是围栏很少触发其进入消息。为了可读性,我排除了日志记录。我的起始位置通常距离所需位置超过89000米。大约在第三次迭代后,我会收到一个位置更新事件,并且与所需位置的距离约为0.2米(四舍五入)。 - Bill Mote
那么你是以宇宙飞船的速度旅行,还是我误读了你的最后一条评论。我认为内部应该有一些限制来防止嘈杂的数据。另外,您能否澄清您是使用AOSP位置管理器还是Google服务。 - Morrison Chang
AOSP(问题已更新)关于我的旅行速度:这并不重要。我在围栏外,然后我进入了围栏内。 - Bill Mote
更多的是一些旁注,因为你正在使用AOSP进行测试 - 当我使用Google服务进行测试时,我发现如果我将我的模拟位置更新限制在某个“合理”的范围内,并让结果稳定下来以考虑“噪声”,我就能够获得一致的结果,但我的测试场景与你的不同(每5-10秒更新一次,单独的应用程序提供模拟,Google服务),所以也许其他人有想法。 - Morrison Chang
非常感谢。我觉得我已经找到解决方案了。如果是这样,我会在这里更新的。 - Bill Mote
1个回答

5
地理围栏管理器会平等考虑所有系统范围内的位置请求,而不仅仅是你的应用程序所做的请求。如果设备上有任何其他应用程序或服务正在使用位置服务,则这些外部应用程序生成的纬度/经度位置将在触发地理围栏时被考虑在内。
如果您正在生成模拟位置,则可能存在外部应用程序或服务正在产生冲突的位置更新,这些模拟和真实位置之间的跳跃可能会导致不稳定的地理围栏测试结果。
您可以通过在设置中禁用真实位置服务并仅使用模拟位置来确认此问题,或查找可能正在使用位置服务的任何应用程序,并在停止或禁用这些特定应用程序后重新进行测试。

实际上,这正是发生的事情。我删除了一些使用位置服务的应用程序,并强制停止了我想要保留的应用程序。自那以后,所有测试都成功了。谢谢你的帮助!!! - Bill Mote
3
完全禁用位置服务可以防止模拟位置在我的应用程序中触发。 - IgorGanapolsky

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接