App for jailbroken iOS device: Consistent background operation

耗尽温柔 提交于 2019-12-19 10:21:37

问题


I am the author of a Cydia tweak called AirFloat. An app that implements the AirPlay audio protocol (previously known as AirTunes), making it possible to stream audio to your iOS device. AirFloat is originally an App Store app, until it got booted by Apple from the App Store.

I've since that made it available for free in Cydia. Currently the app stands in Cydia exactly as the previous App Store version. As a result of this I get a lot of requests to make it work in the background. But I cannot get it to work.

Basically I am thinking of two approaches.

Note: AirFloat displays the currently playing track on the iOS lock screen.

  1. Create a daemon, which runs the actual AirPlay implementation, and communicates with a UI app using notify. This works. Kind of. It runs and plays the audio, but the MPNowPlayingInfoCenter does not seem to be updatable from a non-UI application. Also when the daemon is running as user mobile.

  2. Second approach is to just have it all work in a UI application. But I am having difficulties not having it be suspended. I've set the "Required Background Modes" to audio and continuous. The server might still be running, but then the Bonjour advertising is brought down, because the run loop is brought to a halt when in background. Secondly the app should be launched automatically with SpringBoard and relaunched on abnormal exit.

Personally I prefer the second approach, because I would avoid doing interprocess communication. And for this approach to work I would need full background execution (including run loops) and have it both launched on SpringBoard launch and relaunched on abnormal exit.

Anyone have any suggestions on how to solve this?


回答1:


First thank you for your amazing works with AirFlow RAOP is pretty hard thing!

So what you could do is to

1. Create a background task handlers as dispatch_block_t, let's say

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

2. Define somewhere this background and foreground tasks handler

        // 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. Now let's handle in the application delegate the background mode (As defined before you can activate background mode with different options in your app .plist):

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

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

4. Let's see what the background async task selector does

            -(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 (...) {
         }
        }

I know that this solution works nice but I know that sometimes it happens that iOS will kill the app background task anyway. Anyway if the users suddenly switches the app between foreground and background it will works indefinitely.




回答2:


Partial answer. If you add "voip" as Background Mode, it gets started automatically by SpringBoard.



来源:https://stackoverflow.com/questions/15448868/app-for-jailbroken-ios-device-consistent-background-operation

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!