接近警报误报

Proximity Alerts false positives

我在我的应用程序中设置了近距离警报,它能很好地捕捉到我所在的位置。但是,我注意到我收到了很多本不该收到的警报,但我不知道为什么。

误报的频率与我离接近警报的距离成反比。如果我将我的代理设置为 5 公里外,我每隔一小时就会收到误报,但如果我将其设置为加拿大北部,我将不会收到误报。

这是我的日志在 5 公里外的样子(我的范围设置为 100 米)

12:25:08 (exited) //This is correct, as I set my mock location away
3:11:45 (enter) //This and the following are all incorrect
3:12:31 (exit)
3:15:45 (enter)
3:15:45 (exit)
3:20:50 (enter)
3:21:10 (exit) 
7:43:13 (enter)
7:43:24 (exit)
8:45:21 (enter)
8:45:49 (exit)
8:54:23 (enter)
8:55:09 (exit)
10:26:07 (enter)
10:26:13 (exit)
10:35:03 (enter)
10:35:14 (exit)
2:00:13 (enter) //I sleep, so phone is not being used from here to end of logs
2:00:32 (exit)
2:04:14 (enter)
2:04:52 (exit)
2:18:16 (enter)
2:19:33 (exit)
2:32:20 (enter)
2:33:10 (exit) 
4:56:15 (enter)
4:56:25 (exit)
6:03:42 (enter)
6:04:04 (exit)
6:56:57 (enter)
6:57:08 (exit)

任何人都可以看到一个模式或怀疑我为什么会收到误报吗?(请参阅底部以获取更多可能的线索)

我自然怀疑 Prox Alerts 不是很准确,但我没有发现其他人对此抱怨的证据。此外,其他近距离警报应用程序(如 Road Rooster)在我的设备上没有出现故障。

//This is called once when when initial location is selected, the code for which I have ommitted. 
public void addProximityAlert(double lat, double lng){
    LocationManager lm=(LocationManager) getSystemService(LOCATION_SERVICE);
    SharedPreferences prefs = getApplicationContext().getSharedPreferences(Constants.SHARED_PREFS, Context.MODE_MULTI_PROCESS);
    removeAllProximityAlerts(this);
    float range = (float) prefs.getInt("range", -1);
    if (range < 0){
        prefs.edit().putFloat("range", (float) 50);
        range = 50;
    }
    Intent intent = new Intent(Constants.PROX_INTENT_FILTER);
    int alertId= (int) System.currentTimeMillis();
    PendingIntent pi = PendingIntent.getBroadcast(this, alertId , intent, 0);
    SharedPreferences.Editor editor = prefs.edit();
    editor.putInt("maxAlertId", alertId).commit();
    Util.putDouble(editor, Constants.DEST_LAT, lat);
    Util.putDouble(editor, Constants.DEST_LNG, lng);
    editor.commit();
    lm.addProximityAlert(lat, lng, range, -1, pi);
}

public void removeAllProximityAlerts(Context context) {
    LocationManager lm=(LocationManager) getSystemService(LOCATION_SERVICE);
    SharedPreferences prefs = getApplicationContext().getSharedPreferences(Constants.SHARED_PREFS, Context.MODE_MULTI_PROCESS);
    int maxAlertId = prefs.getInt("maxAlertId", 0);      

    Intent intent = new Intent(Constants.PROX_INTENT_FILTER);
    PendingIntent pendingIntent = PendingIntent.getBroadcast(this , maxAlertId, intent, 0);
    lm.removeProximityAlert(pendingIntent);
}

接收器

public class ProximityReceiver extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent arg1) {

    PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
    PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "");
    wl.acquire();

    String k = LocationManager.KEY_PROXIMITY_ENTERING;
    boolean state = arg1.getBooleanExtra(k, false);

    String enterOrLeave;
    if (state) {
        //Todo: Enter state
        enterOrLeave="entered";
        updateLastDayRecord("Went to gym");
    } else {
        //Todo: Exit state
        enterOrLeave="exited";
    }
    //Do stuff (ommitted unrelated code)
 }
}

清单

<?xml version="1.0" encoding="utf-8"?>

<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:name="GlobalState"
    android:theme="@style/AppTheme" >
    <activity
        android:name=".AllinOneActivity"
        android:label="@string/app_name" >
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    <activity
        android:name=".IntroductionActivity"
        android:label="@string/intro_label"
        android:parentActivityName=".AllinOneActivity" >
        <meta-data
            android:name="android.support.PARENT_ACTIVITY"
            android:value=".AllinOneActivity" />
    </activity>

    <activity android:name=".MessageBodyActivity"
        android:label="@string/message_label"
        android:parentActivityName=".AllinOneActivity">
        <meta-data
            android:name="android.support.PARENT_ACTIVITY"
            android:value=".AllinOneActivity" />
    </activity>


    <meta-data
        android:name="com.google.android.gms.version"
        android:value="@integer/google_play_services_version" />
    <meta-data
        android:name="com.google.android.geo.API_KEY"
        android:value="@string/GOOGLE_API_KEY" />

    <receiver
        android:name=".AlarmManagerBroadcastReceiver"
        android:process=":remote">
    </receiver>

    <receiver
        android:name=".ProximityReceiver"
        android:enabled="true"
        android:exported="true">
        <intent-filter>
            <action android:name="com.pipit.agc.agc.ProximityReceiver" />
        </intent-filter>
    </receiver>

    <receiver android:name=".BootReceiver">
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED"/>
        </intent-filter>
    </receiver>
</application>

引导接收器只是重置警报管理器。

附加信息

花了两个周末试图找出这个错误 - 我一直认为它已被修复,几个小时后我会得到另一个误报。

接近警报基于 GPS 或网络位置。这两者都有不准确之处。事实上,您在 Location 对象中传回的精度是以米为单位的。您只有 67% 的可能性在该范围内 - 有 1/3 的可能性您在该范围之外。

轻微错误比大错误更有可能。使用 GPS 偏离 10m?有道理,其实很普通。偏离 1 公里?如果卫星系统发生奇怪的事情,可能会发生,但应该迅速自行纠正。偏离100公里?不会发生,除了软件中的错误。

所以假设 GPS 位置偏离 5 公里的几率是 5%。您每分钟更新 2 个位置,或每小时更新 120 个。没有误报的可能性是 0.95^120,即 0.2%。即使只有 1% 的误报几率,也有 27% 的几率有一小时没有误报。误报只是您在执行基于位置的算法时必须处理的事情。

解决此问题的一种方法是不要触发接近警报的操作。相反,当接近警报触发请求位置更新并确保它停留在区域内进行一两次更新时。这会增加延迟,但会减少误报。另请注意,当您靠近目标区域时,误报会随着将您置于该区域所需的不准确性的减少而增加,因此它变得更有可能。