在工作中,我们创建了一个应用程序,可以跟踪用户上下班的路线,我们内置了自动记录功能,但它的行为并不像我们想要的那样。这个自动系统似乎在一两天后被操作系统关闭了(手机在这段时间里甚至不动,基本上只用于测试)。
在iOS上,我们使用CLRegions在用户当前位置周围创建几个区域,在用户的确切位置创建了5个半径不同的区域,在10米的距离处创建了12个区域(半径为80米)。CLLocationManager对象也被指示开始监控重大更改和访问(尽管这些更改和访问甚至还需要触发一次)。
在Android上,我们通过LocationServices API使用Geofences,这里我们只有用户当前位置周围的5个增量半径。如果用户关闭安卓系统上的应用程序或重新启动手机,它将启动后台服务,重新初始化地理围栏并开始监控它们。
当我在外面散步测试它们时,这两个系统都工作得很好,但似乎在一两天后(也就是周末)它们就停止了工作。我已经调试了很长一段时间了,尽管它已经变得更好了,但它仍然不完美。
有没有可能有一个可靠的系统,在触发地理围栏/区域时,无论应用程序关闭多久,都可以在后台自动启动该应用程序?我知道两个操作系统处理后台任务的方式非常不同,但我不太确定以这种方式使用GPS的长期限制。
我们正在制作的应用程序是用Xamarin Forms编写的,但这些系统是在它们的本地项目中编写的(仍然是C#,但可以完全访问整个本地平台)
Android服务的问题是,操作系统会杀死后台服务,因为它被认为是低优先级的,然后按一定时间间隔重新启动。这种情况可能会在几天内发生几次,每次都会增加重新启动服务前的时间长度(有些手机只使用设定的时间,没有增加)。
我的建议是,如果你想要一个永远在线的服务,你需要把它变成前台服务。我会给出一些如何实现这一点的代码示例,但我对Xamarin不是很了解,所以我不想给你任何不好的示例。
另一种方法是使用带有PendingIntent的AlarmManager来检查服务是否正在运行,如果没有,则启动它。请注意,如果你经常这样做,可能会导致电池明显耗尽,但如果你做得不够频繁,你可能会错过地理围栏事件。
希望这能有所帮助,祝你好运!
更新#1
以下是在前台提供服务和运行AlarmManager的代码示例。
前景
这是保持应用程序活力的最简单方法。
public class ForegroundService extends Service{
@Override
public int onStartCommand(Intent intent, int flags, int startId){
Notification notification = new NotificationCompat.Builder(this)
.setSmallIcon(R.mipmap.icon)
.setContentTitle("TITLE")
.setContentText("This is an example notification!")
.build();
startForeground(ID, notification);
//Do your stuff here
}
@Override
public void onDestroy(){
super.onDestroy();
stopForeground(true);
//If you have anything else you want done here...
}
}
AlarmManager
这将持续尝试以您设置的时间间隔(本例为10分钟)创建此服务。不过,这里也有一些陷阱。首先,由于Android 6.0引入了Doze模式,AlarmManager可能在手机休眠时不会启动,这意味着该服务可能会在相当长的一段时间内失效。第二,这会多次调用onStartCommand
函数,因此您需要逻辑来处理它。
public class AlwaysOnService extends Service{
@Override
public int onStartCommand(Intent intent, int flags, int startId){
AlarmManager am = (AlarmManager)getSystemService(ALARM_SERVICE);
PendingIntent pi = PendingIntent.getService(this, requestCode, new Intent(this, AlwaysOnService.class), PendingIntent.FLAG_UPDATE_CURRENT);
am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + MIN_10_IN_MILLIS, pi);
//Do your stuff here
return START_STICKY;
}
@Override
public void onDestroy(){
super.onDestroy();
AlarmManager am = (AlarmManager)getSystemService(ALARM_SERVICE);
PendingIntent pi = PendingIntent.getService(this, requestCode, new Intent(this, AlwaysOnService.class), PendingIntent.FLAG_NO_CREATE);
if(pi != null){
am.cancel(pi);
pi.cancel();
}
//If you have anything else you want done here...
}
}
在这两者中,将服务设置为前台服务可能是最简单的事情,除非你真的没有,否则AlarmManager就是你要走的路。