如何在 ios 中永远运行后台服务以同步数据
Posted
技术标签:
【中文标题】如何在 ios 中永远运行后台服务以同步数据【英文标题】:How to run background service in ios forever for syncing of data 【发布时间】:2014-05-10 11:18:11 【问题描述】:您好,我正在开发一个需要在 Web 服务器上进行同步操作(数据提交和检索)的应用程序。
用户可以离线提交表单(即将数据存储到设备上的本地数据库)。并且只要网络可用,后台服务应该将这些数据提交到 Web 服务器。
后台服务的具体要求如下:
后台服务会先检查网络是否可用 如果网络可用,它将收集本地数据库中的数据存储(SQLite) 在设备上 将数据提交到服务器 请求服务器获取任何新数据,如果可用,获取该数据 并更新设备上的本地数据库。我对 ios 和 xamarin/monotouch 还很陌生,想知道如何实现这一点?
我了解iOS中的各种后台模式,例如后台获取、nsurlsession、后台传输等。
我已尝试实施我认为适合我的情况的后台提取。但它运行在它自己的时间。
还想知道如果用户杀死了我的应用程序,那么后台提取也会调用并仍然运行我的应用程序吗?
我的 appdelegate -> PerformFetch 方法中的代码是这样的:
if(networkService.IsNetworkAvailable())
if(this.syncDataService.DownloadNewDataFromServer())
Console.WriteLine("Data downloaded successfully from server..");
if(this.syncDataService.UploadDataToServer())
Console.WriteLine("Data submitted successfully to server...");
completionHandler(UIBackgroundFetchResult.NewData);
else
completionHandler(UIBackgroundFetchResult.NoData);
更新:最后我以这种方式实现了它(希望它对某人有所帮助):
public class LocationUpdatedEventArgs : EventArgs
private CLLocation location;
public LocationUpdatedEventArgs(CLLocation location)
this.location = location;
public CLLocation Location
get return this.location;
public class LocationManager
private static DateTime lastServiceRun;
private CLLocationManager locMgr;
public LocationManager()
this.locMgr = new CLLocationManager();
this.LocationUpdated += this.PrintLocation;
this.locMgr.Failed += (object sender, NSErrorEventArgs e) =>
Console.WriteLine("didFailWithError " + e.Error);
Console.WriteLine("didFailWithError coe " + e.Error.Code);
;
public event EventHandler<LocationUpdatedEventArgs> LocationUpdated = delegate ;
public static TimeSpan TimeDiff get; set;
public CLLocationManager LocMgr
get
return this.locMgr;
public void StartLocationUpdates()
if (CLLocationManager.LocationServicesEnabled)
// sets the accuracy that we want in meters
this.LocMgr.DesiredAccuracy = 1;
//// Location updates are handled differently pre-iOS 6. If we want to support older versions of iOS,
//// we want to do perform this check and let our LocationManager know how to handle location updates.
if (UIDevice.CurrentDevice.CheckSystemVersion(6, 0))
this.LocMgr.LocationsUpdated += (object sender, CLLocationsUpdatedEventArgs e) =>
//// fire our custom Location Updated event
this.LocationUpdated(this, new LocationUpdatedEventArgs(e.Locations[e.Locations.Length - 1]));
;
else
//// this won't be called on iOS 6 (deprecated). We will get a warning here when we build.
this.LocMgr.UpdatedLocation += (object sender, CLLocationUpdatedEventArgs e) =>
this.LocationUpdated(this, new LocationUpdatedEventArgs(e.NewLocation));
;
//// Start our location updates
this.LocMgr.StartUpdatingLocation();
lastServiceRun = DateTime.Now;
// Get some output from our manager in case of failure
this.LocMgr.Failed += (object sender, NSErrorEventArgs e) =>
Console.WriteLine(e.Error);
;
else
//// Let the user know that they need to enable LocationServices
Console.WriteLine("Location services not enabled, please enable this in your Settings");
/// <summary>
/// The stop updating location.
/// </summary>
public void StopUpdatingLocation()
this.locMgr.StopUpdatingLocation();
/// <summary>
/// The print location. (This will keep going in the background)
/// </summary>
/// <param name="sender"> The sender. </param>
/// <param name="e"> Location updated event argument </param>
public void PrintLocation(object sender, LocationUpdatedEventArgs e)
CLLocation location = e.Location;
Console.WriteLine("Longitude: " + location.Coordinate.Longitude);
Console.WriteLine("Latitude: " + location.Coordinate.Latitude);
var diff = DateTime.Now - lastServiceRun;
TimeDiff = diff;
if (TimeDiff.Minutes == 2)
// RunInBackground() is my method which call the service to upload/download data from server
if (this.RunInBackground())
lastServiceRun = DateTime.Now;
【问题讨论】:
你是如何实现的?我有确切的任务(使用 Xamarin)每 5 分钟将手机与服务器同步一次,我也尝试过后台获取,但它不是每 5 分钟调用一次同步。我阅读了您使用 LocationService 的以下评论,您能否分享您的代码或一些有关您如何做到这一点的链接? @Subha 我已经用我用来调用定位服务的代码更新了我的问题。如果您有任何疑问,请告诉我。 感谢您的回复。我在论坛中问了this 问题,有人建议我使用远程推送通知。我们决定使用推送通知,因为我们的应用程序与位置服务无关。我只是想知道我是否可以使用定位服务在后台运行某些功能而不更新任何位置? @Subha - 你可以使用定位服务。由于苹果没有提供任何后台服务的方式,所以使用定位服务是一种在后台运行的方式。它会自动调用。但是,如果您在后台有更大的任务要做,那么我怀疑它可能不允许您执行它。此外,定位服务可能对电池耗尽有害。因为它继续轮询位置,所以电池可以快速耗尽。因此,记住所有这些,您就可以确定方法。 这仍然有效并且是最好的方法吗?你能告诉我们 RunInBackground() 方法吗? 【参考方案1】:计时器永远不会在后台工作,并且您不能在此方法中添加另一个线程。 DidEnterBackground
用于完成待处理的任务。这通常会持续大约 30 秒 - 1 分钟。
您可以在此方法中添加循环或添加长时间运行的任务。
【讨论】:
感谢您的回答。我知道计时器永远不会在后台工作,但我有一个要求,比如运行后台服务以从服务器同步数据(上传和下载)。我尝试了后台获取,但它不可靠。那么您能否强调一些关于如何在 iOS 中实现此类功能的要点/建议? 我有一个类似的需求,我所做的是:添加了一个在后台调用方法的计时器(但这仅在应用程序处于活动状态但不妨碍项目的正常流程时才有效) .然后在后台我调用它一次。稍后当我的应用程序再次出现在前台时,它会再次继续计时器。这是一种解决方法,因为我尝试了很多方法,我也尝试在进入后台和前台时添加通知,但无法在后台重复调用该方法 给定位服务打个电话怎么样?当位置改变事件发生时,如果我们添加一些自定义逻辑可以吗? 就我所读到的,你可以做到。具有定位服务、音频、视频、新闻的应用程序可以永远在后台运行。 iOS 不会杀死该应用程序。但是是的,如果您要将其提交到 Appstore,则最好使用这些服务。尝试并希望它有效,一旦完成不要忘记发布您的答案,同时我也会尝试。 好的,我已经实现了相同的,它工作正常。我设置了一个条件,即在定位服务中每隔 X 分钟同步一次数据。它工作正常,应用程序不会杀死或崩溃。但是我还需要检查Apple store是否会批准它?【参考方案2】:我相信解决您的问题的方法是将您的任务注册为长期运行的任务。
考虑到您使用的是 Xamarin,对于使用 Xamarin here 的后台任务也有很好的解释,这可能与您想要做的事情更相关。
另外,请查看App States and Multitasking 以了解应用程序进入后台时发生的情况:“从 applicationDidEnterBackground: 方法返回后,大多数应用程序很快就会进入暂停状态。请求特定的应用程序后台任务(例如播放音乐)或从系统请求一点额外执行时间的任务可能会继续运行一段时间。”
总的来说,查看苹果开发者网站是一件好事,因为 100% 的 iOS API 都在 Xamarin 中
【讨论】:
【参考方案3】:在 FinishedLaunching 上的应用委托中创建计时器,它将在您设置的时间间隔内调用您的代码
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
...
Timer timer = new Timer(5000); //5000 milisecs - 5 secs
timer.Elapsed += timer_Elapsed;
timer.Start();
...
private void timer_Elapsed(object sender, ElapsedEventArgs e)
//your code here
此外,如果用户将终止您的应用,则意味着即使后台提取也不会再调用任何代码
【讨论】:
这意味着,我们的应用程序应该正在运行以便在后台任务中工作。有什么办法可以处理这种情况..另外,放定时器也会耗尽电池。不是吗?是否有机会从应用商店拒绝该应用? 不,没有办法处理这种情况,不是android可以创建一些服务,如果应用程序被杀,则无法运行任何代码。只有当你的代码被调用时它才会耗尽电池,但不是一直都这样 感谢您的回答。但是如果我使用上面的代码,那么我的应用程序应该在前台运行。虽然我希望它从后台运行。那该怎么做呢? 抱歉,没听懂,“从后台运行”到底是什么意思? 我的意思是我的应用程序需要打开。如果用户打开另一个应用程序,我的应用程序将进入后台并且代码永远不会执行。【参考方案4】:iOS 的一个想法。一种方法是创建一个后台任务调度程序,可能基于 LimitedConcurrencyLevelTaskScheduler (https://msdn.microsoft.com/en-us/library/ee789351(v=vs.110).aspx),它将执行您的 IBackgroundTask 作业。这些作业可以持久化到存储中,并将按顺序执行。例如。如果你想下载很多大文件,你想一个一个地完成它们并在它们登陆你的设备时显示进度或在 UI 中呈现它们。 然后你用这个调度程序将任何下载任务排入队列,并在它即将死去时重新启动它:
void StartBackgroundTask()
BackgroundTaskId = UIApplication.SharedApplication.BeginBackgroundTask(() =>
// Handle expiration: will happen before reaching 600 secs
TaskScheduler.Stop();
UIApplication.SharedApplication.EndBackgroundTask(BackgroundTaskId);
// Start again
StartBackgroundTask();
);
Task.Run(() =>
TaskScheduler = new BackgroundTaskScheduler().Start();
);
【讨论】:
以上是关于如何在 ios 中永远运行后台服务以同步数据的主要内容,如果未能解决你的问题,请参考以下文章