由于苹果推送会把一些敏感类消息外流,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;
}