2012年12月24日

讓你的iOS App有條件的在背景執行一些東西

在水果的iPhoneOSProgrammingGuide中,有提到iOS4以後可以允許App在背景做一些事情
但是只限制10分鐘。10分鐘到了之後就不讓你做了。

當然還有沒限制的,ex: audio, location, voip, newsstand content, external accessory
這邊先不講這種沒限制的...

下面是範例



在AppDelegate中,找到applicationDidEnterBackground:方法 然後開始寫程式

- (void)applicationDidEnterBackground:(UIApplication *)application {
    
    // 時間到了會執行這個block
    __block UIBackgroundTaskIdentifier bgTask = [application beginBackgroundTaskWithExpirationHandler:^{
        [application endBackgroundTask:bgTask];
        bgTask = UIBackgroundTaskInvalid;
    }];
    
    
    // 在背景執行一些東西...
    // 在這邊我用一個無窮迴圈來測試
    // backgroundTimeRemaining 會開始倒數10分鐘
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        
        // run something background
        NSLog(@"App in BG!");
        int i = 0;
        while (YES) {
            NSLog(@"App in BG, backgroundTimeRemaining %f sec",
                  [UIApplication sharedApplication].backgroundTimeRemaining);
            [UIApplication sharedApplication].applicationIconBadgeNumber = ++i;
            [NSThread sleepForTimeInterval:1.0f];
        }
        
        // when finish, call this method
        [application endBackgroundTask:bgTask];
        bgTask = UIBackgroundTaskInvalid;
    });
}

如果做超久的工作,使用者跳出程式後又回來時還沒做完
這個task會一直跑,跑到結束為止。

但是如果使用者又離開App了,那就會再new一個thread給他跑
所以用簡單的flag去控制他。大學OS都有教...

@interface AppDelegate () {
    BOOL mFlag;
}
@end


- (void)applicationDidEnterBackground:(UIApplication *)application {
    if (!mFlag) {
        mFlag = YES;
        
        // 時間到了會執行這個block
        __block UIBackgroundTaskIdentifier bgTask = [application beginBackgroundTaskWithExpirationHandler:^{
            [application endBackgroundTask:bgTask];
            bgTask = UIBackgroundTaskInvalid;
            mFlag = NO;
        }];
        
        // 在背景執行一些東西...
        // 在這邊我用一個無窮迴圈來測試
        // backgroundTimeRemaining 會開始倒數10分鐘
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            
            // run something background
            NSLog(@"App in BG!");
            int i = 0;
            while (YES) {
                NSLog(@"App in BG, backgroundTimeRemaining %f sec",
                      [UIApplication sharedApplication].backgroundTimeRemaining);
                [UIApplication sharedApplication].applicationIconBadgeNumber = ++i;
                [NSThread sleepForTimeInterval:0.1];
                
            }
            
            // when finish, call this method
            [application endBackgroundTask:bgTask];
            bgTask = UIBackgroundTaskInvalid;
            mFlag = NO;
        });
    }
}


那有沒有辦法不管10分鐘限制?
算是有吧。在那個handler的block裡面動手腳就可以了XDDDD
但是阿婆說,這只是讓你苟延慘喘而已
You must call this method to end a task that was started using the
beginBackgroundTaskWithExpirationHandler: method.
If you do not, the system may kill your application.

翻譯:朕不給的,你不能搶

Code請到我的GitHub抓,按這邊連過去
我commit上去的code是不管貧果限制的版本。
乖小孩不要學喔~

8 則留言:

  1. github 只有到 Create README.md 的記錄啊~ 沒有看見 block 寫法。(敲碗~)

    回覆刪除
  2. XD 沒commit好,local的code被洗掉了。晚點補上

    回覆刪除
  3. 想请问。。。这能通过水果的审核吗?

    回覆刪除
    回覆
    1. 可以喔!
      這是iOS SDK本來就提供的功能。
      另外,iOS7的background mode跟以前版本有差異
      下面連結是網路上找到的投影片。請參考
      http://www.slideshare.net/mremond/multitasking-in-ios-7

      刪除
    2. 作者已經移除這則留言。

      刪除
  4. hi,大大請再指點一下關鍵點,是remain time結束,還在做事就不會被kill了嗎? 感恩捏

    回覆刪除
    回覆
    1. 實測remain time結束後,task沒有馬上被kill掉
      但是不知道何時會被系統kill掉就是了
      而且在iOS7之後,有background fetch功能。
      建議直接用background fetch

      刪除