【问题标题】:iOS: Keep an app running like a serviceiOS:让应用程序像服务一样运行
【发布时间】:2012-06-18 03:14:52
【问题描述】:

在 iOS 中,我将如何指示操作系统保持我的应用程序运行,即使它不再处于前台?

还有更多的应用程序这样做。

【问题讨论】:

标签: ios iphone ipad foreground background-service


【解决方案1】:

更新到 Swift 5

也许不是 OP 的正确答案。但这将帮助您在应用程序进入后台时启动background task。并在一段时间后被 iOS 杀死(希望在处理完成后)。

创建一个扩展

extension Notification.Name {
  @discardableResult
  func observe(using: @escaping (Notification) -> Void) -> NSObjectProtocol {
    return NotificationCenter.default.addObserver(forName: self,
                                                  object: nil,
                                                  queue: OperationQueue.main,
                                                  using: using)
  }
}

声明一个变量

fileprivate let backgroundTaskStarter = {
  // TODO: If this doesn't work, switch to willResignActiveNotification.
  UIApplication.didEnterBackgroundNotification.observe() { _ in
    UIApplication.shared.beginBackgroundTask {
      print("endBackgroundTask called by iOS")
    }
  }
}()

Filename.swiftinit() 方法中,执行:

_ = backgroundTaskStarter

【讨论】:

    【解决方案2】:

    基本上,iOS 中不存在服务类型的应用程序或功能。 即使是“后台”应用程序(UIBackgroundMode)也不能完全免费运行,并且不受其他操作系统上的服务或守护程序等限制。

    这是关于后台执行以及通知和计时器等的情况。

    1) 应用无法在后台执行,除非:

    a) 它要求操作系统提供额外的时间来执行此操作。这是使用 beginBackgroundTaskWithExpirationHandler 完成的。 Apple 没有(有意)指定这个额外的时间有多长,但实际上它大约是 10 分钟。

    b) 应用程序具有后台模式,这些模式是 VoIP、音频、位置、报摊。即使它具有这些类型之一,应用程序也无法不受限制地执行。本讨论的其余部分假设应用程序没有后台模式。如果您尝试使用其中一种后台模式来使您的应用能够在后台运行,但您的应用没有合法使用特定功能,那么您的应用将在应用商店提交时被拒绝(即拥有 UIBackgroundMode它必须是:VoIP 应用程序,需要持续更新位置,在后台连续播放音频的能力是一项基本功能,或者是报亭应用程序)。

    2) 当一个应用程序被暂停时,它不能做任何事情来直接唤醒自己。它以前不能安排一个 NSTimer,它不能使用像 performSelector:afterDelay 这样的东西。等等

    应用再次激活的唯一方法是用户采取措施使其激活。用户可以通过以下方式做到这一点:

    a) 直接从应用程序图标启动应用程序

    b) 启动应用以响应应用先前在其处于活动状态时安排的本地通知。

    c) 启动应用程序以响应服务器发送的远程通知。

    d) 其他一些:例如,如果应用程序注册为处理通过 URL 启动,则 URL 启动;或者如果它被注册为能够处理某种类型的内容。

    如果本地/远程通知触发时应用程序处于前台,则应用程序会直接接收它。

    如果触发本地/远程通知时应用当前不在前台,则应用不会收到它。通知触发时没有执行任何代码!

    只有当用户选择通知时,应用才会激活并执行。

    请注意,用户可以为整个设备或仅针对特定应用程序禁用通知,在这种情况下,用户将永远看不到它们。如果在通知即将触发时关闭设备,则会丢失。

    IOS 7 更新

    1) 有一些新的后台模式,例如后台获取(但是,您仍然不能被操作系统以确定性的方式唤醒)

    2) 现在有后台推送通知

    3) beginBackgroundTaskWithExpirationHandler 时间从 10 分钟减少到 3 分钟左右。

    【讨论】:

    • voip 应用程序可以在后台运行(有一定的限制)——它们只需在操作系统中注册为 voip 应用程序(并且它们实际上必须提供 voip 功能,否则它们将无法通过审批流程)
    • 现在,如果我不需要连续的位置更新,而是定期更新,是否会因为使用后台服务而拒绝应用程序,或者是否有不同的方式每 5-10 分钟唤醒应用程序或所以呢?
    • @rokjarc,是的,正如我在回答中所说:“b)应用程序具有后台模式,模式为:voip ....”
    • @Neo42 是的,情况仍然如此,关于地图,您是否阅读过 b) 的第一句话
    • “1) 应用程序无法在后台执行,除非:.... b) 应用程序具有后台模式,模式为:voip、音频、位置、报摊。”令人困惑?是什么让您认为应用显示的是蓝色条而不是操作系统。
    【解决方案3】:

    现在 iOS7 发生了变化,现在是测试版

    来自Apple's developer portal

    通过采用新的 iOS 7 中的多任务 API。新服务允许您的应用更新 信息和在后台下载内容而不会耗尽 不必要的电池。更新可能发生在机会主义 时间并根据使用情况智能安排,因此您的应用程序 可以在用户需要时在后台更新内容。

    【讨论】:

      【解决方案4】:

      是的,我们可以这样做,

      创建一个私有字段来跟踪我们的状态:

      UIBackgroundTaskIdentifier _bgTask;
      

      现在调用这个方法,像服务一样运行你的应用

      if ([[UIDevice currentDevice] respondsToSelector:@selector(isMultitaskingSupported)]) 
      {
         _bgTask = UIBackgroundTaskInvalid;
         [[NSNotificationCenter defaultCenter] addObserver:self
      selector:@selector(doBackground:)
      name:UIApplicationDidEnterBackgroundNotification object:nil];
      }
      

      现在创建一个选择器-

      - (void) doBackground:(NSNotification *)aNotification
      {
         UIApplication *app = [UIApplication sharedApplication];
      
         if ([app respondsToSelector:@selector(beginBackgroundTaskWithExpirationHandler:)])
         {
            _bgTask = [app beginBackgroundTaskWithExpirationHandler:^
            {
               dispatch_async(dispatch_get_main_queue(), ^
               {
                  if (_bgTask != UIBackgroundTaskInvalid)
                  {
                     [app endBackgroundTask:_bgTask];
                     _bgTask = UIBackgroundTaskInvalid;
                  }
               });
            }];
         }
      }
      

      完成任务后,调用以下代码-

      UIApplication *app = [UIApplication sharedApplication];
      if ([app respondsToSelector:@selector(endBackgroundTask:)]) 
      {
         if (_bgTask != UIBackgroundTaskInvalid)
         {
            [app endBackgroundTask:_bgTask];
            _bgTask = UIBackgroundTaskInvalid;
         }
      }
      

      【讨论】:

        猜你喜欢
        • 2016-03-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-01-22
        • 2012-05-27
        • 2013-02-26
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多