我的一个iOS应用程序似乎出现了典型的Heisenbug症状。该应用程序跟踪用户的家庭位置,因此当用户进入和离开他们的家庭位置时,会发生某些事件。
当我测试这个应用程序时,它工作得很好。我走进和走出一个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");
}
}
}我知道这对任何人来说都有很多代码要筛选,但我现在真的没有好的理论来解释是什么导致了这个bug,或者是否有可能修复iOS的局限性。
我有几个理论,如果CLLocationManager。PausesLocationUpdatesAutomatically属性可能与它有关,或者CLLocationManager的其他属性,如ActivityType、DesiredAccuracy或DistanceFilter。我已经将所有这些都保留为默认设置,我认为这会很好,但我不是很确定。
另一种理论是,在“服务”在后台运行了一段时间后,会抛出一个未捕获的异常。如果是这样的话,iOS有没有提供堆栈跟踪之类的功能呢?在我的所有测试中,我从未遇到过任何从这段代码抛出的异常,所以我有点怀疑这就是问题所在。不过,在这一点上,我愿意考虑任何想法或建议。
此外,请记住,为了使此应用程序按预期方式工作,位置更新事件必须在用户进入或存在CLCircularRegion时立即发生(至少在一分钟左右)。显然,我必须让用户保持其位置服务的启用状态,并允许应用程序具有适当的权限。
发布于 2016-02-02 01:38:23
你的诊断很可能是对的--这是典型的观察者效应。
当你测试这个应用程序时,当用户玩一个新的应用程序时,iphone正在被活跃地使用。它没有入睡的机会。第二天,当用户回家时会发生什么-他们的手机很可能在到达家之前的很长一段时间内没有使用:通常情况下,我们在离开公共交通工具后的最后一英里步行期间,或者在开车回家的时候不会使用手机。iOS注意到这种延长的不活动时间,并调整自己的行为以优化电池寿命。
观察这一点最简单的方法是在你所在的位置放一个简单的面包屑应用程序-设置geofence,并在每次你得到退出事件时继续这样做。根据你使用(或不使用)手机的方式,在走同一条路线时,结果会非常不同。
当你回到家时,手机通常也是你最后一件要拿的东西。
你可能想让用户提供更多细节,比如他们在回家之前和之后的15分钟里到底是如何使用手机的,他们使用了哪些其他应用程序,如果他们开车了,他们是否会一直轮流运行导航应用程序,等等。你会发现这种模式。
re.此外,请记住,为了使此应用程序按预期方式工作,位置更新事件必须在用户输入或存在CLCircularRegion时立即发生(至少在一分钟左右的时间内)。
你不能只使用地理围栏来做到这一点,特别是考虑到不同的到达/出发模式-步行和驾驶,“下降”路径(例如,U型掉头到达)。你必须预料到延迟超过1分钟和“过早”触发。恐怕没有解决办法。
发布于 2016-02-02 04:44:27
以下是需要检查的内容:
locationManager.distanceFilter = kCLDistanceFilterNone locationManager.desiredAccuracy = kCLLocationAccuracyBest //或kCLLocationAccuracyBestForNavigation locationManager.activityType = true //如果没有其他有效方法,请尝试false locationManager.allowsBackgroundLocationUpdates = true locationManager.activityType= CLActivityType.AutomotiveNavigation
https://stackoverflow.com/questions/35134729
复制相似问题