2

我是名为 AirFloat 的 Cydia 调整的作者。一个实现 AirPlay 音频协议(以前称为 AirTunes)的应用程序,可以将音频流式传输您的 iOS 设备。AirFloat 最初是一个 App Store 应用程序,直到它被 Apple 从 App Store 启动。

从那以后,我在 Cydia 中免费提供了它。目前,该应用程序在 Cydia 中与之前的 App Store 版本完全相同。因此,我收到了很多让它在后台工作的请求。但我无法让它工作。

基本上我在考虑两种方法。

注意:AirFloat 在 iOS 锁定屏幕上显示当前正在播放的曲目。

  1. 创建一个守护进程,它运行实际的 AirPlay 实现,并使用notify与 UI 应用程序通信。这行得通。有点儿。它运行并播放音频,但 MPNowPlayingInfoCenter 似乎不能从非 UI 应用程序更新。此外,当守护程序以用户mobile运行时。

  2. 第二种方法是让它全部在 UI 应用程序中工作。但是我很难让它被暂停。我已将“必需的背景模式”设置为audioContinuous。服务器可能仍在运行,但随后 Bonjour 广告被关闭,因为运行循环在后台停止。其次,应用程序应该使用 SpringBoard 自动启动,并在异常退出时重新启动。

我个人更喜欢第二种方法,因为我会避免进行进程间通信。对于这种工作方法,我需要完整的后台执行(包括运行循环),并让它在 SpringBoard 启动时启动并在异常退出时重新启动。

有人对如何解决这个问题有任何建议吗?

4

2 回答 2

2

首先感谢您在 AirFlow RAOP 方面的出色工作,这是一件非常困难的事情!

所以你可以做的是

1.创建一个后台任务处理程序作为dispatch_block_t,比方说

     dispatch_block_t myDummyBackgroundTaskBlock = {
        [[UIApplication sharedApplication] endBackgroundTask:myDummyBackgroundTask];
        myDummyBackgroundTask = UIBackgroundTaskInvalid;
        myDummyBackgroundTask = [app beginBackgroundTaskWithExpirationHandler:myDummyBackgroundTask];
    };

2. 在某处定义此后台和前台任务处理程序

        // foreground
        -(void)handleTasksForApplicationInForeground {
        if(myDummyBackgroundTask) { // reset that task
           [[UIApplication sharedApplication] endBackgroundTask: myDummyBackgroundTask];
           myDummyBackgroundTask = UIBackgroundTaskInvalid;
         }
        }

         // background
         -(void) handleTasksForApplicationInBackground {
             UIDevice *device = [UIDevice currentDevice];
             BOOL backgroundSupported = NO;
            if ([device respondsToSelector:@selector(isMultitaskingSupported)])
             backgroundSupported = device.multitaskingSupported;
            if(backgroundSupported && backgroundEnabled) { // perform a background task

                myDummyBackgroundTaskBlock = ^{
                    [[UIApplication sharedApplication] endBackgroundTask: myDummyBackgroundTaskBlock];
                    myDummyBackgroundTaskBlock = UIBackgroundTaskInvalid;
               };

               SEL sel = @selector(doDummyBackgroundTask);
               [self doBackgroundTaskAsync:sel];

               [self performSelector:@selector(doBackgroundTaskAsync:) withObject:nil afterDelay:500.0f]; /// LP: this is the funny part since iOS will kill the task after 500 sec.
                 }
               }

3.现在让我们在应用程序中处理委托后台模式(如之前定义的那样,您可以在应用程序.plist中使用不同的选项激活后台模式):

        -(void)applicationDidEnterBackground:(UIApplication *)application {
           [self handleTasksForApplicationInBackground];
         }

        -(void)applicationWillEnterForeground:(UIApplication *)application {
           [self handleTasksForApplicationInForeground];
         }

4. 看看后台异步任务选择器做了什么

            -(void) doBackgroundTaskAsync:(SEL)selector {

            @try {
                 if( [[UIApplication sharedApplication] backgroundTimeRemaining] < 5 ) { 
                   return;
                 }

               if(!myDummyBackgroundTaskBlock) { // need to create again on-the-fly
                    myDummyBackgroundTaskBlock = ^{
                          [[UIApplication sharedApplication] endBackgroundTask:myDummyBackgroundTask];
                         myDummyBackgroundTask = UIBackgroundTaskInvalid;
                    };
                 }

                myDummyBackgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:myDummyBackgroundTaskBlock];
              dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{

               while ([[UIApplication sharedApplication] backgroundTimeRemaining] > 5.0) {
                  int delta = 5.0;
                  [self performSelector: selector ];
                  sleep(delta);
              }
            });
          }
           @catch (...) {
         }
        }

我知道这个解决方案效果很好,但我知道有时 iOS 无论如何都会终止应用程序后台任务。无论如何,如果用户突然在前台和后台之间切换应用程序,它将无限期地工作。

于 2013-04-17T19:10:33.583 回答
1

部分回答。如果您将“voip”添加为后台模式,它会由 SpringBoard 自动启动。

于 2013-03-16T18:53:12.943 回答