iOS 后台播放静音音频保证应用不会被无端杀掉

由于苹果推送会把一些敏感类消息外流,So 我们采用长连接试的推送服务,网上有此推送的开源项目 传送门在此mpush

此开源项目很好的帮助了国内一些企业对敏感消息推送的保护,但是针对苹果端的推送有一个问题就是后台长连接的时间很短,几分钟内就会被杀掉,So 我们想到在后台放置一个循环静音(是静音啊 不是无声)音频 保证应用程序不会被杀掉。

此代码是swift版的 有兴趣的同学可以翻译成OC

AudioManager.swift

import Foundation
import AVFoundation
@objc
open class AudioManager: NSObject {
    
    public static let shared = AudioManager()
    fileprivate let audioSession = AVAudioSession.sharedInstance()
    fileprivate var backgroundAudioPlayer: AVAudioPlayer?
    fileprivate var backgroundTimeLength = 0
    fileprivate var timer: Timer?
    
    // 是否开启后台自动播放无声音乐
   public var openBackgroundAudioAutoPlay = false {
        didSet {
            if self.openBackgroundAudioAutoPlay {
                self.setupAudioSession()
                self.setupBackgroundAudioPlayer()
            } else {
                if let player = self.backgroundAudioPlayer {
                    if player.isPlaying {
                        player.stop()
                    }
                }
                self.backgroundAudioPlayer = nil
                try? self.audioSession.setActive(false, with: AVAudioSessionSetActiveOptions.notifyOthersOnDeactivation)
            }
        }
    }
    
    override init() {
        super.init()
        self.setupListener()
    }
    deinit {
        NotificationCenter.default.removeObserver(self)
    }
    private func setupAudioSession() {
        do {
            try self.audioSession.setCategory(AVAudioSessionCategoryPlayback, with: AVAudioSessionCategoryOptions.mixWithOthers)
            try self.audioSession.setActive(false)
        } catch let error {
            debugPrint("\(type(of:self)):\(error)")
        }
    }
    private func setupBackgroundAudioPlayer() {
        do {
            self.backgroundAudioPlayer = try AVAudioPlayer(contentsOf: URL(fileURLWithPath: Bundle.main.path(forResource: "wusheng1", ofType: "mp3")!))
        } catch let error {
            debugPrint("\(type(of:self)):\(error)")
        }
        self.backgroundAudioPlayer?.numberOfLoops = -1
        self.backgroundAudioPlayer?.volume = 0
        self.backgroundAudioPlayer?.delegate = self
    }
    private func setupListener() {
        NotificationCenter.default.addObserver(self, selector: #selector(didEnterBackground), name: Notification.Name.UIApplicationDidEnterBackground, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(didBecomeActive), name: Notification.Name.UIApplicationDidBecomeActive, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(audioSessionInterruption(notification:)), name: Notification.Name.AVAudioSessionInterruption, object: nil)
    }
    
}
// MARK: - 扩展 监听通知
extension AudioManager {
    /// 进入后台 播放无声音乐
    @objc fileprivate func didEnterBackground() {
        self.setupTimer()
        guard self.openBackgroundAudioAutoPlay else {return}
        
        do {
            try self.audioSession.setActive(true)
        } catch let error {
            debugPrint("\(type(of:self)):\(error))")
        }
        self.backgroundAudioPlayer?.prepareToPlay()
        self.backgroundAudioPlayer?.play()
    }
    /// 进入前台,暂停播放音乐
    @objc fileprivate func didBecomeActive() {
        self.removeTimer()
        self.hintBackgroundTimeLength()
        self.backgroundTimeLength = 0
        guard self.openBackgroundAudioAutoPlay else {return}
        
        self.backgroundAudioPlayer?.pause()
        do {
            try self.audioSession.setActive(false, with: AVAudioSessionSetActiveOptions.notifyOthersOnDeactivation)
        } catch let error {
            debugPrint("\(type(of:self)):\(error))")
        }
        
        
    }
    /// 音乐中断处理
    @objc fileprivate func audioSessionInterruption(notification: NSNotification) {
        guard self.openBackgroundAudioAutoPlay else {return}
        guard let userinfo = notification.userInfo else {return}
        guard let interruptionType: UInt = userinfo[AVAudioSessionInterruptionTypeKey] as! UInt?  else {return}
        if interruptionType == AVAudioSessionInterruptionType.began.rawValue {
            // 中断开始,音乐被暂停
            debugPrint("\(type(of:self)): 中断开始 userinfo:\(userinfo)")
        } else if interruptionType == AVAudioSessionInterruptionType.ended.rawValue {
            // 中断结束,恢复播放
            debugPrint("\(type(of:self)): 中断结束 userinfo:\(userinfo)")
            guard let player = self.backgroundAudioPlayer else {return}
            if player.isPlaying == false {
                debugPrint("\(type(of:self)): 音乐未播放,准备开始播放")
                do {
                    try self.audioSession.setActive(true)
                } catch let error {
                    debugPrint("\(type(of:self)):\(error)")
                }
                player.prepareToPlay()
                player.play()
            } else {
                debugPrint("\(type(of:self)): 音乐正在播放")
            }
        }
    }
}
// MARK: - 扩展 定时器任务
extension AudioManager {
    fileprivate func setupTimer() {
        self.removeTimer()
        self.timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(timerTask), userInfo: nil, repeats: true)
        RunLoop.main.add(self.timer!, forMode: RunLoopMode.commonModes)
    }
    fileprivate func removeTimer() {
        self.timer?.invalidate()
        self.timer = nil;
    }
    @objc func timerTask() {
        self.backgroundTimeLength += 1
    }
    fileprivate func hintBackgroundTimeLength() {
        let message = "本次后台持续时间:\(self.backgroundTimeLength)s"
        //HintTool.hint(message)
    }
}

// MARK: - 扩展 播放代理
extension AudioManager: AVAudioPlayerDelegate {
    public func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
        
    }
    public func audioPlayerDecodeErrorDidOccur(_ player: AVAudioPlayer, error: Error?) {
        debugPrint("\(type(of:self))" + error.debugDescription)
    }
}

HintTool.swift

import UIKit


class HintTool {
    
    private var hintView: UIView?
    
    static let shared = HintTool()
    
    static func hint(_ message: String) {
        self.shared.showHintView(hintView: self.hintView(with: message))
    }
    
    private func showHintView(hintView: UIView) {
        guard let window = UIApplication.shared.delegate?.window else {return}
        guard self.hintView == nil else {return}
        
        window!.addSubview(hintView)
        window!.bringSubview(toFront: hintView)
        self.hintView = hintView
        
        // 消失
        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 2) {
            [weak self] in
            UIView.animate(withDuration: 0.5, animations: {
                [weak self] in
                self?.hintView?.alpha = 0.5
                }, completion: { (finished) in
                    self?.hintView?.removeFromSuperview()
                    self?.hintView = nil
            })
        }
    }
    
    private static func hintView(with message: String) -> UIView {
        let minWidth = 180.0
        let maxWidth = 260.0
        let padding = 10.0
        let font = UIFont.systemFont(ofSize: 14)
        
        let messageSize = message.ext_size(withBoundingSize: CGSize(width: maxWidth-2*padding, height: 0) , font: font)
        
        let labelFrame = CGRect(x: 0, y: 0, width: CGFloat(ceilf(Float(messageSize.width))), height: CGFloat(ceilf(Float(messageSize.height))))
        let viewFrame = CGRect(x: 0, y: 0, width: max(minWidth, Double(messageSize.width) + padding*2), height: Double(messageSize.height) + padding*2)
        
        let hintView = UIView()
        hintView.isUserInteractionEnabled = false
        hintView.backgroundColor = UIColor(white: 0, alpha: 0.7)
        hintView.layer.cornerRadius = 8
        hintView.layer.masksToBounds = true
        hintView.frame = viewFrame
        hintView.center = CGPoint(x: CGFloat(ceilf(Float(UIScreen.main.bounds.size.width*0.5))), y: CGFloat(ceilf(Float(UIScreen.main.bounds.size.height-100.0))))
        
        let hintLabel = UILabel()
        hintView.addSubview(hintLabel)
        hintView.isUserInteractionEnabled = false
        hintLabel.text = message
        hintLabel.textColor = UIColor.white
        hintLabel.textAlignment = .center
        hintLabel.font = font
        hintLabel.preferredMaxLayoutWidth = messageSize.width
        hintLabel.numberOfLines = 0
        hintLabel.frame = labelFrame
        hintLabel.center = CGPoint(x: CGFloat(ceilf(Float(hintView.bounds.size.width*0.5))), y: CGFloat(ceilf(Float(hintView.bounds.size.height*0.5))))
        
        return hintView
    }
}

extension String {
    func ext_size(withBoundingSize boundingSize: CGSize, font: UIFont) -> CGSize {
        let option = NSStringDrawingOptions.usesLineFragmentOrigin
        let attributes = [NSFontAttributeName : font]
        let contentSize = self.boundingRect(with: boundingSize, options: option, attributes: attributes, context: nil).size
        return contentSize
    }
}

使用

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

 [AudioManager shared].openBackgroundAudioAutoPlay = YES;
}

 

你可能感兴趣的:(自我学习篇)