前言
一般app在按下home鍵被掛起后,這時app的 backgroundtimeremaining 也就是后臺運行時間大約只有3分鐘,如果在退出app后,過十幾二十二分鐘或者更長時間再回到app,app就會回到剛打開時的狀態,也就是首頁;有的項目在被掛起后需要在后臺運行一段時間,使有足夠的時間來完成與服務器對接的操作,或者需要一直運行的需求;如果需要,則在app被掛起后,申請后臺,來延長后臺運行時間。
app申請后臺運行的方式有幾種:
播放音樂
定位
newsstand downloads
fetch 等;
這里主要說下后臺播放無聲音樂(其實是不播放),采用哪種方式,對應勾選上圖;我的項目中有音頻播放需求,如果沒有,那就找一個播放音頻的理由,或者用其他方式實現。
實現
這里采用了兩個單例:電話監控(xktelmanager)、后臺運行(xkbgrunmanager),電話監控可以忽略,視情況而用;采用單例是為了方便管理;
xktelmanager.h
1
2
3
4
5
6
7
8
9
10
11
|
#import <foundation/foundation.h> @interface xktelmanager : nsobject ///是否在后臺運行 @property (nonatomic,assign) bool inbackgroundrun; + (xktelmanager *)sharedmanager; /** 來電監聽 */ - ( void )startmonitor; @end |
xktelmanager.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
|
#import "xktelmanager.h" #import "xkbgrunmanager.h" #import <coretelephony/ctcallcenter.h> #import <coretelephony/ctcall.h> static xktelmanager *_sharedmanger; @interface xktelmanager() @property (nonatomic, strong) ctcallcenter *callcenter; @end @implementation xktelmanager + (xktelmanager *)sharedmanager{ static dispatch_once_t oncetelsingle; dispatch_once(&oncetelsingle, ^{ if (!_sharedmanger) { _sharedmanger = [[xktelmanager alloc]init]; } }); return _sharedmanger; } - (instancetype)init{ self = [super init]; if (self) { _inbackgroundrun = no; } return self; } #pragma mark -********* 監聽電話相關 - ( void )startmonitor { __weak typeof(self) weakself = self; _callcenter = [[ctcallcenter alloc] init]; _callcenter.calleventhandler = ^(ctcall * call) { ///如果已經進入后臺了,不做任何操作 if (weakself.inbackgroundrun) { return ; } ///app未進入后臺 if ([call.callstate isequaltostring:ctcallstatedisconnected]){ nslog(@ "電話 --- 斷開連接" ); [[xkbgrunmanager sharedmanager] stopbgrun]; } else if ([call.callstate isequaltostring:ctcallstateconnected]){ nslog(@ "電話 --- 接通" ); } else if ([call.callstate isequaltostring:ctcallstateincoming]){ nslog(@ "電話 --- 待接通" ); [[xkbgrunmanager sharedmanager] startbgrun]; } else if ([call.callstate isequaltostring:ctcallstatedialing]){ nslog(@ "電話 --- 撥號中" ); [[xkbgrunmanager sharedmanager] startbgrun]; } else { nslog(@ "電話 --- 無操作" ); } }; } @end |
xkbgrunmanager.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
#import <foundation/foundation.h> @interface xkbgrunmanager : nsobject + (xkbgrunmanager *)sharedmanager; /** 開啟后臺運行 */ - ( void )startbgrun; /** 關閉后臺運行 */ - ( void )stopbgrun; @end |
xkbgrunmanager.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
|
#import "xkbgrunmanager.h" ///循環時間 static nsinteger _circuladuration = 60; static xkbgrunmanager *_sharedmanger; @interface xkbgrunmanager() @property (nonatomic,assign) uibackgroundtaskidentifier task; ///后臺播放 @property (nonatomic,strong) avaudioplayer *playerback; @property (nonatomic, strong) nstimer *timerad; ///用來打印測試 @property (nonatomic, strong) nstimer *timerlog; @property (nonatomic,assign) nsinteger count; @end @implementation xkbgrunmanager{ cfrunloopref _runloopref; dispatch_queue_t _queue; } + (xkbgrunmanager *)sharedmanager{ static dispatch_once_t oncerunsingle; dispatch_once(&oncerunsingle, ^{ if (!_sharedmanger) { _sharedmanger = [[xkbgrunmanager alloc]init]; } }); return _sharedmanger; } /// 重寫init方法,初始化音樂文件 - (instancetype)init { if (self = [super init]) { [self setupaudiosession]; _queue = dispatch_queue_create( "com.audio.inbackground" , null); //靜音文件 nsstring *filepath = [[nsbundle mainbundle] pathforresource:@ "****" oftype:@ "mp3" ]; nsurl *fileurl = [[nsurl alloc] initfileurlwithpath:filepath]; self.playerback = [[avaudioplayer alloc] initwithcontentsofurl:fileurl error:nil]; [self.playerback preparetoplay]; // 0.0~1.0,默認為1.0 self.playerback.volume = 0.01; // 循環播放 self.playerback.numberofloops = -1; } return self; } - ( void )setupaudiosession { // 新建audiosession會話 avaudiosession *audiosession = [avaudiosession sharedinstance]; // 設置后臺播放 nserror *error = nil; [audiosession setcategory:avaudiosessioncategoryplayback withoptions:avaudiosessioncategoryoptionmixwithothers error:&error]; if (error) { nslog(@ "error setcategory avaudiosession: %@" , error); } nslog(@ "%d" , audiosession.isotheraudioplaying); nserror *activeseterror = nil; // 啟動audiosession,如果一個前臺app正在播放音頻則可能會啟動失敗 [audiosession setactive:yes error:&activeseterror]; if (activeseterror) { nslog(@ "error activating avaudiosession: %@" , activeseterror); } } /** 啟動后臺運行 */ - ( void )startbgrun{ [self.playerback play]; [self applyforbackgroundtask]; ///確保兩個定時器同時進行 dispatch_async(_queue, ^{ self.timerlog = [[nstimer alloc] initwithfiredate:[nsdate date] interval:1 target:self selector:@selector( log ) userinfo:nil repeats:yes]; self.timerad = [[nstimer alloc] initwithfiredate:[nsdate date] interval:_circuladuration target:self selector:@selector(startaudioplay) userinfo:nil repeats:yes]; _runloopref = cfrunloopgetcurrent(); [[nsrunloop currentrunloop] addtimer:self.timerad formode:nsdefaultrunloopmode]; [[nsrunloop currentrunloop] addtimer:self.timerlog formode:nsdefaultrunloopmode]; cfrunlooprun(); }); } /** 申請后臺 */ - ( void )applyforbackgroundtask{ _task =[[uiapplication sharedapplication] beginbackgroundtaskwithexpirationhandler:^{ dispatch_async(dispatch_get_main_queue(), ^{ [[uiapplication sharedapplication] endbackgroundtask:_task]; _task = uibackgroundtaskinvalid; }); }]; } /** 打印 */ - ( void ) log { _count = _count + 1; nslog(@ "_count = %ld" ,_count) } /** 檢測后臺運行時間 */ - ( void )startaudioplay{ _count = 0; dispatch_async(dispatch_get_main_queue(), ^{ if ([[uiapplication sharedapplication] backgroundtimeremaining] < 61.0) { nslog(@ "后臺快被殺死了" ); [self.playerback play]; [self applyforbackgroundtask]; } else { nslog(@ "后臺繼續活躍呢" ); } ///再次執行播放器停止,后臺一直不會播放音樂文件 [self.playerback stop]; }); } /** 停止后臺運行 */ - ( void )stopbgrun{ if (self.timerad) { cfrunloopstop(_runloopref); [self.timerlog invalidate]; self.timerlog = nil; // 關閉定時器即可 [self.timerad invalidate]; self.timerad = nil; [self.playerback stop]; } if (_task) { [[uiapplication sharedapplication] endbackgroundtask:_task]; _task = uibackgroundtaskinvalid; } } @end |
最后在 appdelegate.m 對應的方法中,實現開啟和停止后臺運行即可!
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。
原文鏈接:https://www.cnblogs.com/wangkejia/archive/2018/10/11/9773285.html