美文网首页iOSiOS面试iOS面试
ios 程序后台保活(程序在后台持续运行)

ios 程序后台保活(程序在后台持续运行)

作者: 爱迪生的小跟班 | 来源:发表于2018-12-28 11:17 被阅读1481次

    一般APP在按下Home键被挂起后,这时APP的 backgroundTimeRemaining 也就是后台运行时间大约只有3分钟,如果在退出APP后,过十几二十二分钟或者更长时间再回到APP,APP就会回到刚打开时的状态,也就是首页;有的项目在被挂起后需要在后台运行一段时间,使有足够的时间来完成与服务器对接的操作,或者需要一直运行的需求;如果需要,则在APP被挂起后,申请后台,来延长后台运行时间。

    ·应用程序状态有以下5中状态

    1、Not Running  未运行  程序没启动
    2、Inactive      未激活 程序在前台运行,不过没有接收到事件。在没有事件处理情况下程序通常停留在这个状态
    3、Active        激活  程序在前台运行而且接收到了事件。这也是前台的一个正常的模式
    4、Backgroud 后台  程序在后台而且能执行代码,大多数程序进入这个状态后会在在这个状态上停留一会。时间到之后会进入挂起状态(Suspended)。有的程序经过特殊的请求后可以长期处于Backgroud状态
    5、Suspended 挂起  程序在后台不能执行代码。系统会自动把程序变成这个状态而且不会发出通知。当挂起时,程序还是停留在内存中的,当系统内存低时,系统就把挂起的程序清除掉,为前台程序提供更多的内存。
    

    一般常见的需要后台保活的应用场景有定位、音乐播放、下载、Fetch等等。
    所以当需要后台程序持续运行,需要在程序中打开Background Modes,如下图:


    打开程序后台运行需要的功能.png

    勾选Background Modes中需要在后台持续活跃的功能。
    下面拿音乐播放举例:

    直接上代码,最后在 AppDelegate.m 对应的方法中,实现开启和停止后台运行即可! 在退出进入后台的时候调用startBGRun(开启后台运行)方法; 回到前台时,不在需要后台活跃,调用stopBGRun(关闭后台运行)停止后台活跃

    创建后台保活管理类HCKeepBGRunManager

    HCKeepBGRunManager.h
    //
    //  HCKeepBGRunManager.h
    //  BlockRedpag
    //
    //  Created by 何其灿 on 2018/10/28.
    //  Copyright © 2018 Lixiaoqian. All rights reserved.
    //  APP保活
    
    #import <Foundation/Foundation.h>
    
    NS_ASSUME_NONNULL_BEGIN
    
    @interface HCKeepBGRunManager : NSObject
    + (HCKeepBGRunManager *)shareManager;
    
    /**
     开启后台运行
     */
    - (void)startBGRun;
    
    /**
     关闭后台运行
     */
    - (void)stopBGRun;
    
    @end
    
    NS_ASSUME_NONNULL_END
    
    HCKeepBGRunManager.m
    //
    //  HCKeepBGRunManager.m
    //  BlockRedpag
    //
    //  Created by 何其灿 on 2018/10/28.
    //  Copyright © 2018 Lixiaoqian. All rights reserved.
    //
    
    #import "HCKeepBGRunManager.h"
    #import <AVFoundation/AVFoundation.h>
    
    ///循环时间
    static NSInteger _circulaDuration = 60;
    static HCKeepBGRunManager *_sharedManger;
    
    @interface HCKeepBGRunManager ()
    @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 HCKeepBGRunManager{
        CFRunLoopRef _runloopRef;
        dispatch_queue_t _queue;
    }
    
    + (HCKeepBGRunManager *)shareManager{
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            if (!_sharedManger) {
                _sharedManger = [[HCKeepBGRunManager 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
    
    

    相关文章

      网友评论

        本文标题:ios 程序后台保活(程序在后台持续运行)

        本文链接:https://www.haomeiwen.com/subject/bakslqtx.html