Отслеживание iOS CLCircularRegion - Гейзенбаг
Кажется, у одного из моих iOS-приложений есть признаки классического гейзенбагга. Приложение отслеживает домашнее местоположение пользователя, поэтому определенные события происходят, когда пользователь входит и выходит из своего домашнего местоположения.
Пока я тестирую приложение, оно отлично работает. Я вхожу и выхожу из CLCircularRegion
и это работает во всех отношениях, я пытаюсь это сделать. Работает с приложением в фоновом режиме. Работает с закрытым приложением. Работает с приложением на переднем плане. Работает с зелеными яйцами и ветчиной.
К сожалению, пользователи сообщают о проблемах, когда это будет отложено примерно на 15 минут. Пользователи войдут в свои дома, но событие произойдет не позднее, чем позже. В некоторых случаях событие не происходит вообще. Кажется, что шаблон заключается в том, что когда пользователь впервые начинает использовать приложение, оно прекрасно работает. Примерно через день приложение, похоже, не работает. События задерживаются.
Я буду первым, кто признает, что я не специалист по внутренней работе CLLocationManager
а также CLCircularRegion
, Я верю, что у меня все настроено правильно, и мне очень трудно понять, как я могу отладить что-то подобное.
Во всяком случае, я покажу некоторые из моего кода здесь. Имейте в виду, что это разработано с Xamarin, так что это в C#.
AppDelegate.cs
public static AppDelegate self;
private CLLocationManager locationManager;
private CLCircularRegion[] locationFences;
private void initializeLocationManager()
{
this.locationManager = new CLLocationManager();
// iOS 8 additional permissions requirements
if (UIDevice.CurrentDevice.CheckSystemVersion(8, 0))
{
locationManager.RequestAlwaysAuthorization();
}
locationManager.AuthorizationChanged += (sender, e) =>
{
var status = e.Status;
// Location services was turned off or turned off for this specific application.
if (status == CLAuthorizationStatus.Denied)
{
stopLocationUpdates();
}
else if (status == CLAuthorizationStatus.AuthorizedAlways &&
iOSMethods.getKeyChainBool(OptionsViewController.GENERIC, OptionsViewController.SERVICE_GEOLOCATION_ENABLED))
{
startLocationUpdates();
}
};
if (CLLocationManager.IsMonitoringAvailable(typeof(CLCircularRegion)))
{
locationManager.RegionEntered += (sender, e) =>
{
setRegionStatus(e, "Inside");
};
locationManager.RegionLeft += (sender, e) =>
{
setRegionStatus(e, "Outside");
};
locationManager.DidDetermineState += (sender, e) =>
{
setRegionStatus(e);
};
}
else
{
// cant do it with this device
}
init();
}
public void init()
{
var data = SQL.query<SQLTables.RoomLocationData>("SELECT * FROM RoomLocationData").ToArray();
int dLen = data.Length;
if (dLen > 0)
{
locationFences = new CLCircularRegion[dLen];
for (int x = 0; x < dLen; x++)
{
var d = data[x];
CLCircularRegion locationFence = new CLCircularRegion(new CLLocationCoordinate2D(d.Latitude, d.Longitude), d.Radius, d.SomeID.ToString() + ":" + d.AnotherID.ToString());
locationFence.NotifyOnEntry = true;
locationFence.NotifyOnExit = true;
locationFences[x] = locationFence;
}
}
}
private void setRegionStatus(CLRegionEventArgs e, string status, bool calledFromDidDetermineState = false)
{
string identifier = e.Region.Identifier;
string lastStatus = iOSMethods.getKeyChainItem(OptionsViewController.GENERIC, OptionsViewController.SERVICE_LAST_GEO_STATUS);
if (lastStatus == status + ":" + identifier)
{
return;
}
iOSMethods.setKeychainItem(OptionsViewController.GENERIC, OptionsViewController.SERVICE_LAST_GEO_STATUS, status + ":" + identifier);
string[] split = identifier.Split(new string[] { ":" }, StringSplitOptions.RemoveEmptyEntries);
if (split.Length == 2)
{
try
{
int someID = Convert.ToInt32(split[0]);
int anotherID = Convert.ToInt32(split[1]);
// Notifies our API of a change.
updateGeofenceStatus(someID, anotherID, status);
if (iOSMethods.getKeyChainBool(OptionsViewController.GENERIC, OptionsViewController.SERVICE_GEOLOCATION_NOTIFICATIONS) &&
(status == "Inside" || status == "Outside" || status == "Unknown"))
{
var rm = SQL.query<SQLTables.KeyRoomPropertyData>("SELECT * FROM KeyRoomPropertyData WHERE SomeID ID = ? AND AnotherID = ?",
new object[] { someID, anotherID }).ToArray();
if (rm.Length > 0)
{
if (status == "Unknown")
{
return;
}
var rmD = rm[0];
UILocalNotification notification = new UILocalNotification();
notification.AlertAction = "Geolocation Event";
notification.AlertBody = status == "Inside" ? "Entered " + rmD.SomeName + ": " + rmD.AnotherName :
status == "Outside" ? "Exited " + rmD.SomeName + ": " + rmD.AnotherName :
"Geolocation update failed. If you would like to continue to use Geolocation, please make sure location services are enabled and are allowed for this application.";
notification.SoundName = UILocalNotification.DefaultSoundName;
notification.FireDate = NSDate.Now;
UIApplication.SharedApplication.ScheduleLocalNotification(notification);
}
}
}
catch (Exception er)
{
// conversion failed. we don't have ints for some reason.
}
}
}
private void setRegionStatus(CLRegionStateDeterminedEventArgs e)
{
string state = "";
if (e.State == CLRegionState.Inside)
{
state = "Inside";
}
else if (e.State == CLRegionState.Outside)
{
state = "Outside";
}
else
{
state = "Unknown";
}
CLRegionEventArgs ee = new CLRegionEventArgs(e.Region);
setRegionStatus(ee, state, true);
}
public void startLocationUpdates()
{
if (CLLocationManager.LocationServicesEnabled)
{
init();
if (locationFences != null)
{
foreach (CLCircularRegion location in locationFences)
{
locationManager.StartMonitoring(location);
Timer t = new Timer(new TimerCallback(delegate(object o) { locationManager.RequestState(location); }), null, TimeSpan.FromMilliseconds(500), TimeSpan.FromMilliseconds(-1));
}
}
}
}
public void stopLocationUpdates(bool isRestarting = false)
{
if (locationFences != null)
{
foreach (CLCircularRegion location in locationFences)
{
locationManager.StopMonitoring(location);
}
}
if (!isRestarting)
{
var rooms = SQL.query<SQLTables.KeyRoomPropertyData>("SELECT * FROM KeyRoomPropertyData").ToArray();
foreach (SQLTables.KeyRoomPropertyData room in rooms)
{
// notifies our API of a change
updateGeofenceStatus(room.SomeID, room.AnotherID, "Unknown");
}
}
}
Я знаю, что каждый может просмотреть много кода, но у меня действительно нет хорошей теории на данный момент относительно того, что является причиной этой ошибки или возможно ли ее исправить с помощью ограничений iOS.
Несколько теорий, которые у меня есть, если CLLocationManager
,PausesLocationUpdatesAutomatically
свойство может иметь какое-то отношение к нему, или какое-либо другое свойство CLLocationManager
такие как ActivityType
, DesiredAccuracy
, или же DistanceFilter
, Я оставил все это по умолчанию, которое, как я полагаю, будет в порядке, но я не совсем уверен.
Другая теория заключается в том, что существует неисследованное исключение, которое выдается через некоторое время после того, как "служба" работает в фоновом режиме в течение некоторого времени. Если это так, то есть ли что-нибудь, что может сделать iOS, что бы мне подсчитало стек? Во всех моих тестах я никогда не сталкивался с какими-либо исключениями из этого кода, поэтому я сомневаюсь, что это проблема. На данный момент, однако, я готов принять любые идеи или предложения.
Кроме того, имейте в виду, что для того, чтобы это приложение работало так, как оно было задумано, события обновления местоположения ДОЛЖНЫ происходить, как только пользователь входит или существует CLCircularRegion
(по крайней мере, в течение минуты) Очевидно, что я должен оставить это пользователю, чтобы оставить его службы определения местоположения включенными и позволить приложению иметь соответствующие разрешения.
2 ответа
Некоторые вещи, чтобы проверить:
Каковы некоторые типичные значения для радиуса? Вы можете рассмотреть возможность уменьшения этого.
Службы определения местоположения iOS обеспечат более быстрый ответ, если на устройстве включен WiFi, даже если пользователь не подключен к сети. Убедитесь, что у проблемных пользователей отключен Wi-Fi, и если вы этого еще не сделали, возможно, протестируйте свое устройство без Wi-Fi.
Есть ли задержка в уведомлении? То есть правильно ли происходит событие региона, но по какой-то причине задержка в уведомлении?
Сколько существует записей RoomLocationData? iOS ограничивает каждое приложение максимум 20 регионами.
Предполагая, что пользователи едут в / из своего дома, вы можете попробовать следующие настройки (код Swift):
locationManager.distanceFilter = kCLDistanceFilterNone locationManager.desiredAccuracy = kCLLocationAccuracyBest // or kCLLocationAccuracyBestForNavigation locationManager.pausesLocationUpdatesAutomatically = true // try false if nothing else works locationManager.allowsBackgroundLocationUpdates = true locationManager.activityType = CLActivityType.AutomotiveNavigation
Скорее всего, вы поставили диагноз точно в цель - это классический эффект наблюдателя.
При тестировании приложения, когда пользователи играют с новым приложением, iphone активно используется. Ему не дают возможности уснуть. Что происходит на следующий день, когда пользователи возвращаются домой - их телефоны, скорее всего, не будут использоваться в течение продолжительного времени непосредственно перед тем, как добраться до дома: обычно мы не используем телефоны во время прогулки "последней мили" после выхода из общественного транспорта или во время поездки назад Главная. iOS замечает этот продленный период бездействия и корректирует свое поведение, чтобы оптимизировать время автономной работы.
Самый простой способ убедиться в этом - собрать простое приложение "хлебные крошки" - установить геозону на своем месте и делать это каждый раз, когда вы получаете событие выхода. В зависимости от того, как вы используете (или не используете), результаты вашего телефона могут сильно отличаться при ходьбе по тому же маршруту.
И когда вы возвращаетесь домой, телефон, как правило, является последним, что вы достигаете также.
Возможно, вы захотите попросить пользователей предоставить более подробную информацию о том, как именно они пользовались телефонами за последние 15 минут до и после входа в дом, какие другие приложения они используют, если они едут, продолжают ли они работать приложение навигации по очереди и т. Д. Вы обнаружите шаблон,
число рейнольдса Кроме того, имейте в виду, что для того, чтобы это приложение работало так, как оно было задумано, события обновления местоположения ДОЛЖНЫ происходить, как только пользователь входит в CLCircularRegion или существует его (по крайней мере, в течение минуты или около того).
Вы не можете сделать это только с геозоной, особенно принимая во внимание различные схемы прибытия / отправления - ходьба против вождения, "спуск" (например, прибытие с разворотами). Вы должны ожидать как задержки более 1 минуты, так и преждевременное срабатывание. Я боюсь, что нет обходного пути.