1. 程式人生 > >IOS 後臺執行介紹及解決辦法

IOS 後臺執行介紹及解決辦法

第一部分

1.先說說iOS 應用程式5個狀態:

停止執行-應用程式已經終止,或者還未啟動。

不活動-應用程式處於前臺但不再接收事件(例如,使用者在app處於活動時鎖住了裝置)。

活動-app處於“使用中”的狀態。

後臺-app不再螢幕上顯示,但它仍然執行程式碼。

掛起-app仍然駐留記憶體但不再執行程式碼。

按下Home鍵時,app從活動狀態轉入後臺,絕大部分app通常在幾秒內就從後臺變成了掛起。

在記憶體吃緊的時候,iphone會首先關閉那些掛起的app。

從 iOS 4 開始,應用就可以在退到後臺後,繼續執行一小段時間(10 分鐘);

2.還可以把自己宣告為需要在後臺執行,就能不限時地運行了。

不過限制為播放音樂、使用 GPS 、voip、。 值得一提的是,有的應用為了達到後臺不限時執行的目的,在後臺播放無聲的音樂(稽核不一定會被發現)。

iOS 5 開始又多了一種型別:下載報刊雜誌。

然後 iOS 7 則可以下載各種玩意和定時抓取。

iOS 7 需要注意的區別:iOS 7 以前,應用進入後臺繼續執行時,如果使用者鎖屏了,那麼 iOS 會等待應用執行完,才進入睡眠狀態。而在 iOS 7 上,系統會很快進入睡眠狀態,那些後臺應用也就暫停了。如果收到事件被喚醒(例如定時事件、推送、位置更新等),後臺應用才能繼續執行一會。因為處理過程變成了斷斷續續的,因此下載時也要使用 NSURLSession 來處理(即下文中的 Background Transfer Service)。

3. 在我看來,蘋果限制 app在後臺執行,是為了更有效的利用硬體使用當前的app,不然,過多的app駐留後臺,對手機資源佔用是一大問題。

二. ios7以後提供的後臺介面模式

1、Background Audio,這是後臺的音訊,這個很早之前便有,也是iOS裝置中用得最多的後臺應用,呼叫這個介面可以實現後臺的音樂播放。

2、Location Services,這是後臺的定位,系統會擁有統一頁面進行管理。

3、VoIP,後臺語音服務,類似Skype通話應用需要呼叫,可進行後臺的語音通話。

4、Newsstand,報刊雜誌後臺自動下載更新,其能夠自動實時更新。

5、Background Task  Completion,這個介面早在iOS 4時候便擁有,其可以供任意型別的APP使用,不過在舊系統中,這個介面的後臺限制執行時間僅為10分鐘,意味著當應用退至後臺,其後臺執行僅能持續10分鐘便會轉至休眠狀態。iOS 7中對這個介面作出了改變,原來的為連續10分鐘,即不論你這10分鐘內使用者是否關閉螢幕進入休眠狀態,應用仍然會在後臺等待10分鐘完結後推出,而新的改進為假如遇到關閉螢幕休眠的情況,這後臺執行的10分鐘便會跟隨一同休眠,剩餘的後臺時間將會留待使用者再一次喚醒裝置才計算。這樣後臺執行的時間仍然為10分鐘,但並不連續,這樣做的優點為省電。

如現在有一些詞典應用帶有後臺複製選詞功能,實際上其是利用了這個介面,如果使用者開啟詞典後並推出,即使螢幕關閉,但詞典仍然在後臺執行,電量消耗還是比較大的,在iOS 7上,這個問題可以得到解決。

6、 Remote Notification,這是本次較大的一個改進介面,以往聊天類應用接受推送後點進去需要再收一次資訊,這情況在QQ、微信等應用上最為明顯。不過擁有了這個介面後,這情況將不復存在,以後推送將能夠直接啟動後臺任務。值得注意的是remote notification支援silent notification(靜默推送),這樣dropbox這類同步應用可以在後臺以最節能的模式實時靜默同步了,類似布卡漫畫這種也可以推送正在追的漫畫的新章節並在後臺靜默下載,待到下載好再給使用者傳送一個本地推送,使用者點開即看無需再聯網。

7、Background Transfer Service,後臺上傳下載。iOS最接近傳統多工的後臺介面,可供任意型別的app呼叫,無時間限制。應用場景包括後臺上傳和下載資料,這使得遊戲後臺更新資料包,後臺上傳視訊等等都成為可能,但是正如其名字,它只能用於處理上傳下載這種傳輸類的任務,類似後臺剪下板監控這種它就無能為力了。

iOS 7新增的background fetch,這個後臺介面在蘋果WWDC 2013上有提及,其會根據使用者行為自動調整達到效率最優的後臺模式,能夠處理不是很有時效性的資訊獲取。例如一些社交、新聞類的應用的後臺資訊更新,iOS系統便會根據應用啟動頻率、時間和當前網路和電量的狀況來智慧分配每個應用的後臺獲取頻率和啟動時長。

三 .  當前社交專案,如何使用ios後臺

1.當前專案特點:

a. 在儲存長連線的情況下,使用者一直線上,才能即時接收到訊息;

b. 在初始化連線的時候,需要做很多處理,如果經常連線,必然很耗電, 所以盡力在後臺的時候,不是時常斷開後又連線;

2. 通過以上分析,改選用何種方式來儲存app後臺執行

voip不行;

靜音播放,不清除這種方式,是否可以通過稽核;

vpns推送,可取的方式,(具體方法: 使用者在登入後,傳送一個裝置的tokenid; 在傳送訊息時,平臺根據對方是離線還是線上,來判斷要不要發推送訊息)

3.background fetch在該專案中的應用

由於該app在初始化時,需要耗點時間,最好的方式就是通過  後臺獲取  來處理該工作,這樣能保證使用者的流暢體驗。

第二部分:保持程式在後臺長時間執行

iOS為了讓裝置儘量省電,減少不必要的開銷,保持系統流暢,因而對後臺機制採用墓碑式的“假後臺”。除了系統官方極少數程式可以真後臺,一般開發者開發出來的應用程式後臺受到以下限制:

1.使用者按Home之後,App轉入後臺進行執行,此時擁有180s後臺時間(iOS7)或者600s(iOS6)執行時間可以處理後臺操作

2.當180S或者600S時間過去之後,可以告知系統未完成任務,需要申請繼續完成,系統批准申請之後,可以繼續執行,但總時間不會超過10分鐘。

3.當10分鐘時間到之後,無論怎麼向系統申請繼續後臺,系統會強制掛起App,掛起所有後臺操作、執行緒,直到使用者再次點選App之後才會繼續執行。

當然iOS為了特殊應用也保留了一些可以實現“真後臺”的方法,摘取比較常用的:

1.VOIP

2.定位服務

3.後臺下載

4.在後臺一直播放無聲音樂(容易受到電話或者其他程式影響,所以暫未考慮)

5….更多

其中VOIP需要繫結一個Socket連結並申明給系統,系統將會在後臺接管這個連線,一旦遠端資料過來,你的App將會被喚醒10s(或者更少)的時間來處理資料,超過時間或者處理完畢,程式繼續休眠。

後臺現在是iOS7引入的新API,網上實現的程式碼比較少,博主也沒有細心去找。

由於博主要做的App需要在後臺一直執行,每隔一段時間給伺服器主動傳送訊息來保持帳號登陸狀態,因而必須確保App不被系統墓碑限制。

博主最先嚐試了很多方法,包括朋友發來的一個Demo,每180s後臺時間過期就銷燬自己然後再建立一個後臺任務,但是實際測試只有10分鐘時間。最後因為考慮到VOIP對服務端改動太大,時間又太緊,所以選擇了定位服務的方法來保持後臺。


  1. 1、需要引入標頭檔案: #import <CoreLocation/CoreLocation.h>
  2. 2、在 AppDelegate.m 中定義 CLLocationManager * locationManager;作為全域性變數方便控制
  3. 3、在程式啟動初期對定位服務進行初始化:
  4. locationManager = [[CLLocationManager alloc] init];
    locationManager.delegate = self;//or whatever class you have for managing location</pre>
  5. 4、在程式轉入後臺的時候,啟動定位服務
  6. [locationManager startUpdatingLocation]; (第一次執行這個方法的時候,如果之前使用者沒有使用過App,則會彈出是否允許位置服務,關於使用者是否允許,後面程式碼中有判斷)
  7. 這樣在定位服務可用的時候,程式會不斷重新整理後臺時間,實際測試,發現後臺180s時間不斷被重新整理,達到長久後臺的目的。
  8. 但是這樣使用也有一些問題,在部分機器上面,定位服務即使開啟也可能不能重新整理後臺時間,需要完全結束程式再執行。穩定性不知道是因為程式碼原因還是系統某些機制原因。
  9. 判斷使用者是否打開了定位服務,是否禁用了該程式的定位許可權:
  10. if(![CLLocationManager locationServicesEnabled] || ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusDenied))//判斷定位服務是否開啟
    {
        [InterfaceFuncation ShowAlertWithMessage:@"錯誤" AlertMessage:@"定位服務未開啟\n保持線上需要後臺定位服務\n請到 設定-隱私 中開啟定位服務" ButtonTitle:@"我錯了"];
        return;
    }
  11. AppDelegate.m
  12. @property (assign, nonatomic) UIBackgroundTaskIdentifier bgTask;
    @property (strong, nonatomic) dispatch_block_t expirationHandler;
    @property (assign, nonatomic) BOOL jobExpired;
    @property (assign, nonatomic) BOOL background;
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
        
        UIApplication* app = [UIApplication sharedApplication];
        
        __weak NSUAAAIOSAppDelegate* selfRef = self;
        
        self.expirationHandler = ^{  //建立後臺自喚醒,當180s時間結束的時候系統會呼叫這裡面的方法
            [app endBackgroundTask:selfRef.bgTask];
            selfRef.bgTask = UIBackgroundTaskInvalid;
            selfRef.bgTask = [app beginBackgroundTaskWithExpirationHandler:selfRef.expirationHandler];
            NSLog(@"Expired");
            selfRef.jobExpired = YES;
            while(selfRef.jobExpired)
            {
                // spin while we wait for the task to actually end.
                NSLog(@"等待180s迴圈程序的結束");
                [NSThread sleepForTimeInterval:1];
            }
            // Restart the background task so we can run forever.
            [selfRef startBackgroundTask];
        };
        
        // Assume that we're in background at first since we get no notification from device that we're in background when
        // app launches immediately into background (i.e. when powering on the device or when the app is killed and restarted)
        [self monitorBatteryStateInBackground];
        locationManager = [[CLLocationManager alloc] init];
        locationManager.delegate = self;
        //[locationManager startUpdatingLocation];
        return YES;
    }
    
    - (void)monitorBatteryStateInBackground
    {
        self.background = YES;
        [self startBackgroundTask];
    }
    
    - (void)applicationDidBecomeActive:(UIApplication *)application
    {
        // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
        NSLog(@"App is active");
        [UIApplication sharedApplication].applicationIconBadgeNumber=0;//取消應用程式通知腳標
        [locationManager stopUpdatingLocation];
        self.background = NO;
    }
    
    - (void)applicationDidEnterBackground:(UIApplication *)application
    {
        // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
        // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
        //if([self bgTask])
        if(isLogined)//當登陸狀態才啟動後臺操作
        {
            self.bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:self.expirationHandler];
            NSLog(@"Entered background");
            [self monitorBatteryStateInBackground];
        }
    }
    
    - (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error//當定位服務不可用出錯時,系統會自動呼叫該函式
    {
        NSLog(@"定位服務出錯");
        if([error code]==kCLErrorDenied)//通過error的code來判斷錯誤型別
        {
            //Access denied by user
            NSLog(@"定位服務未開啟");
            [InterfaceFuncation ShowAlertWithMessage:@"錯誤" AlertMessage:@"未開啟定位服務\n客戶端保持後臺功能需要呼叫系統的位置服務\n請到設定中開啟位置服務" ButtonTitle:@"好"];
        }
    }
    
    - (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations//當用戶位置改變時,系統會自動呼叫,這裡必須寫一點兒程式碼,否則後臺時間重新整理不管用
    {
        NSLog(@"位置改變,必須做點兒事情才能重新整理後臺時間");
        CLLocation *loc = [locations lastObject];
        //NSTimeInterval backgroundTimeRemaining = [[UIApplication sharedApplication] backgroundTimeRemaining];
        //NSLog(@"Background Time Remaining = %.02f Seconds",backgroundTimeRemaining);
        // Lat/Lon
        float latitudeMe = loc.coordinate.latitude;
        float longitudeMe = loc.coordinate.longitude;
    }
    
    - (void)startBackgroundTask
    {
        NSLog(@"Restarting task");
        if(isLogined)//當登陸狀態才進入後臺迴圈
        {
            // Start the long-running task.
            NSLog(@"登入狀態後臺程序開啟");
            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                // When the job expires it still keeps running since we never exited it. Thus have the expiration handler
                // set a flag that the job expired and use that to exit the while loop and end the task.
                NSInteger count=0;
                BOOL NoticeNoBackground=false;//只通知一次標誌位
                BOOL FlushBackgroundTime=false;//只通知一次標誌位
                locationManager.distanceFilter = kCLDistanceFilterNone;//任何運動均接受,任何運動將會觸發定位更新
                locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters;//定位精度
                while(self.background && !self.jobExpired)
                {
                    NSLog(@"進入後臺程序迴圈");
                    [NSThread sleepForTimeInterval:1];
                    count++;
                    if(count>60)//每60s進行一次開啟定位,重新整理後臺時間
                    {
                        count=0;
                        [locationManager startUpdatingLocation];
                        NSLog(@"開始位置服務");
                        [NSThread sleepForTimeInterval:1];
                        [locationManager stopUpdatingLocation];
                        NSLog(@"停止位置服務");
                        FlushBackgroundTime=false;
                    }
                    if(!isLogined)//未登入或者掉線狀態下關閉後臺
                    {
                        NSLog(@"保持線上程序失效,退出後臺程序");
                        [InterfaceFuncation ShowLocalNotification:@"保持線上失效,登入已被登出,請重新登入"];
                        [[UIApplication sharedApplication] endBackgroundTask:self.bgTask];
                        return;//退出迴圈
                    }
                    NSTimeInterval backgroundTimeRemaining = [[UIApplication sharedApplication] backgroundTimeRemaining];
                    NSLog(@"Background Time Remaining = %.02f Seconds",backgroundTimeRemaining);
                    if(backgroundTimeRemaining<30&&NoticeNoBackground==false)
                    {
                        [InterfaceFuncation ShowLocalNotification:@"向系統申請長時間保持後臺失敗,請結束客戶端重新登入"];
                        NoticeNoBackground=true;
                    }
                    //測試後臺時間重新整理
                    if(backgroundTimeRemaining>200&&FlushBackgroundTime==false)
                    {
                        [[NSNotificationCenter defaultCenter] postNotificationName:@"MessageUpdate" object:@"重新整理後臺時間成功\n"];
                        FlushBackgroundTime=true;
                        //[InterfaceFuncation ShowLocalNotification:@"重新整理後臺時間成功"];
                    }
                }
                self.jobExpired = NO;
            });
        }
    }
    //iOS9中還得加入如下設定項:
  13. (CLLocationManager *)sharedLocationManager {
        static CLLocationManager *_locationManager;
        
        @synchronized(self) {
            if (_locationManager == nil) {
                _locationManager = [[CLLocationManager alloc] init];
                
                if ([_locationManager respondsToSelector:@selector(allowsBackgroundLocationUpdates)]) {
                    [_locationManager setAllowsBackgroundLocationUpdates:YES];
                }
                _locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
                
            }
        }
        return _locationManager;
    }

    在此處感謝簡書作者 還有:最模板    我將兩篇文章合併希望能給後來者一些方便

相關推薦

IOS 後臺執行介紹解決辦法

第一部分 1.先說說iOS 應用程式5個狀態: 停止執行-應用程式已經終止,或者還未啟動。 不活動-應用程式處於前臺但不再接收事件(例如,使用者在app處於活動時鎖住了裝置)。 活動-app處於“使用中”的狀態。 後臺-app不再螢幕上顯示,但它仍然執行程式碼。 掛起-ap

Java中的魔法值介紹解決辦法

所謂魔法值,是指在程式碼中直接出現的數值,只有在這個數值記述的那部分程式碼中才能明確瞭解其含義。 int [] array = new int[20]; for (int i = 0; i < 20; i++){ System.out.print(array

轉載:oracle執行update語句時卡住問題分析解決辦法

oracle執行update語句時卡住問題分析及解決辦法  這篇文章主要介紹了oracle執行update語句時卡住問題分析及解決辦法,涉及記錄鎖等相關知識,具有一定參考價值,需要的朋友可以瞭解。 問題 開發的時候debug到一條update的sql語句時程式就不動了,然後我就

Linux不能執行netstat命令的原因解決辦法

伺服器是阿里雲的,這是我出的錯,使用netstate命令報 -bash: netstate: command not found 出現這個錯誤的原因:由於網路工具沒有安裝 使用以下命令安裝即可: yum install net-tools 執行命令: netstat -

開發中 ios 11.0系統遇到的坑解決辦法

導讀: 之前更新iOS11.0系統後,發現原來的專案有很多變化,例如,app的圖示不顯示了,tableview的佈局變了,搜尋框的樣式也有所變化,因此,針對這些問題找到了相應的解決方案。 一、app的圖示不顯示 原因:圖示不顯示主要是cocoaPod出現了問題 解決方案:使用

iOS上架被拒原因解決辦法

簡單的記錄一下,近期APP上架所遇到的坑爹事兒吧!! 第一次提交: 第二天給了回覆,內容如下: 1、Guideline 2.5.1 - Performance - Software Requirements Your app uses the "prefs:root=" non-public U

ride.py在執行python3.×版本後導致無法執行解決辦法

最近一直在自學python自動化,網上看到rf框架挺適合初學自動化測試,於是通過蟲師的搭建了rf框架, 但是在使用過程中遇到了一個問題,在網上沒有找到明確解決辦法於是想到記錄一下 之前為了搭建rf框架下載了python2.7版本,後面又想玩下爬蟲於是下了python3.4版本結果出現了下面的問題:和往常一樣切

SimpleDateFormat執行緒不安全解決辦法

一. 為什麼SimpleDateFormat不是執行緒安全的? Java原始碼如下: /** * Date formats are not synchronized. * It is recommended to create separate format instan

ios 常見錯誤解決辦法(不定時更新)

這類錯誤是因為將專案拷貝到新的電腦造成的錯誤(原因是專案名稱不同造成的),解決辦法:更改Build Setting中的專案名稱就好了。    Build Setting ->Product Name  未完待續。。。。

執行Double DQN程式出現錯誤解決辦法

出現錯誤: ValueError: Variable Natural_DQN/eval_net/l1/w1 already exists, disallowed. Did you mean to set reuse=True or reuse=tf.AUTO_REUSE in VarSc

spark on yarn執行產生缺jar包錯誤解決辦法

1、本地執行出錯及解決辦法 當執行如下命令時: ./bin/spark-submit \ --class org.apache.spark.examples.mllib.JavaALS \ --master local[*] \ /opt/cloudera/p

kaldi平臺上aishell執行時出現的問題解決辦法

問題 1: This script is intended to be used with GPUs but you have not compiled Kaldi with CUDA If you want to use GPUs (and have them), go

手機端問題IOS解決辦法

1.解決頁面使用 overflow: scroll 在 iOS 上滑動卡頓的問題? 首先你可能會給頁面的 html 和 body 增加了 height: 100%, 然後就可能造成 IOS 上頁面滑動的卡頓問題。解決方案是: (1) 看是否能把 body 和

非VR工程執行時自動啟動steam的原因解決辦法

最近在做一個機械模型的教學系統,因為還不確定是否要做成VR的就先吧steamVR的外掛匯入工程了,但之後發現,每次執行專案的時候都會自動啟動steam,我當時並不知道是什麼原因,也沒放在心上,因為啟動了steam,工程的攝像機就會自動切換到htc的頭盔上的攝像機的視角,所以我

Mysql執行sql檔案報2013錯誤的原因解決辦法

Mysql執行sql檔案報2013錯誤的原因 一般都是版本不支援問題,我在開發過程中遇到過這個問題,同學用的mysql5.8   用他匯出的sql檔案在我的mysql5.6版本資料庫執行就報2013錯

執行緒死鎖解決辦法

死鎖是由於不同執行緒按照不同順序進行加鎖而造成的。如: 執行緒A:對lock a加鎖 => 對lock b加鎖 => dosth => 釋放lock b => 釋放lock a 執行緒B:對lock b加鎖 => 對lock a加鎖 

Java Web開發中,自定義過濾器被執行兩次的原因分析解決辦法

本文出處:http://blog.csdn.net/chaijunkun/article/details/7646338,轉載請註明。由於本人不定期會整理相關博文,會對相應內容作出完善。因此強烈建議在原始出處檢視此文。 在Java Web開發過程中,我們可以使用過濾器和Sp

關於Android中呼叫了post方法後貌似沒有執行run方法的解釋解決辦法

(真糾結,剛剛發了之後才發現排版太亂了,稍作修改再發了哈~) 哎……之前糾結過Handler的執行機制,後來貌似懂了,但是近幾天又被自己的工程繞的好像又不懂了一樣!! 其實之前理解還是對的哈~只是這次的工程裡的各個變數和物件的定義和初始化位置不適當才造成表面上貌似

nginx “403 Forbidden” 錯誤的原因解決辦法

所有 html 網上 查找 lan href 原因 我沒 分配 ————————————————————————————————首先 錯誤的原因及解決辦法 ———————————————————————————————————————————————————— ng

項目中遇到的某些問題解決辦法(一)

sql () 輸入 包含 查看 定位 管理器 顯示 分布式開發 簡介 該博文記錄了一些平時在工作中遇到的問題及解決辦法,某些問題有解決辦法,某些問題暫時沒有解決辦法,如果有大神知道的,請多多指點。 如果某些問題有更好的解決辦法,也請指教。 正文 1、在一個方