如何在 ios 设备中测试不使用调试功能的 bgtaskscheduler 功能

Posted

技术标签:

【中文标题】如何在 ios 设备中测试不使用调试功能的 bgtaskscheduler 功能【英文标题】:how to test in ios device about bgtaskscheduler function not using debug function 【发布时间】:2022-01-10 19:02:25 【问题描述】:

在我的 ios 设备而非模拟器中使用调试功能时没有问题。 (例如,e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"TASK_IDENTIFIER"])

但是当不使用调试功能时,按照我的代码,它将在进入后台 60 秒后播放音乐。但是设备中没有发生任何事情。

如何测试不使用调试功能的设备?

import UIKit
import BackgroundTasks
import os.log
import AVFoundation

private let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "AppDelegate")

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate 
    
    let bgTaskIdentifier = "com.hakjun.bgTest.playMusic"
    var alarmTime : Int = 0
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool 
        // Override point for customization after application launch.
        BGTaskScheduler.shared.register(forTaskWithIdentifier: bgTaskIdentifier, using: nil)  task in
            self.handleAppRefresh(task: task as! BGAppRefreshTask)
            print("test bg")
        
        return true
    
    
    func scheduleAppRefresh(time : Double) 
            let request = BGAppRefreshTaskRequest(identifier: bgTaskIdentifier)
            request.earliestBeginDate = Date(timeIntervalSinceNow: time)
            do 
                try BGTaskScheduler.shared.submit(request)
                print("schedule app refresh")
             catch 
                print("Could not schedule app refresh task \(error.localizedDescription)")
            
        
    
    func handleAppRefresh(task : BGAppRefreshTask)
        scheduleAppRefresh(time: 60)
        let queue = OperationQueue()
        queue.maxConcurrentOperationCount = 1
        let appRefreshOperation = BlockOperation 
            Singleton.sharedInstance.play()
        
//        queue.addOperation(appRefreshOperation)
        task.expirationHandler = 
            print("expire background")
            queue.cancelAllOperations()
        
        let lastOperation = queue.operations.last
        lastOperation?.completionBlock = 
            task.setTaskCompleted(success: !(lastOperation?.isCancelled ?? false))
        
        print("background handle")
        queue.addOperation(appRefreshOperation)
    
    
    // MARK: UISceneSession Lifecycle
    
    func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration 
        // Called when a new scene session is being created.
        // Use this method to select a configuration to create the new scene with.
        return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
    
    
    func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) 
        // Called when the user discards a scene session.
        // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
        // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
    
    
    func applicationDidEnterBackground(_ application: UIApplication) 
        print("test bg os log2")
        logger.log("App did enter background")
        scheduleAppRefresh(time: 60)
    


class Singleton 
    static let sharedInstance = Singleton()
    private var player: AVAudioPlayer?
    
    func play() 
        let audioSession = AVAudioSession.sharedInstance()
        guard let url = Bundle.main.url(forResource: "alarm2", withExtension: "mp3") else  return 
        do 
            try audioSession.setCategory(.playback, mode: .default, options: [])
         catch let error as  NSError 
            print("audioSession 설정 오류 : \(error.localizedDescription)")
        
        
        do 
            try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playback)
            try AVAudioSession.sharedInstance().setActive(true)

            player = try AVAudioPlayer(contentsOf: url, fileTypeHint: AVFileType.mp3.rawValue)
            
            guard let player = player else  return 
            
            player.play()

         catch let error 
            print(error.localizedDescription)
        
    

    func stop() 
        player?.stop()
    

【问题讨论】:

此音乐仅用于诊断目的吗?我问,因为这不是 BGAppRefreshTaskRequest 的用途。首先,它的运行时间由操作系统自行决定(可能要等到很久很久以后)。其次,这不是在后台播放音乐的正确工具。这只会使应用程序在最终触发时在后台运行几秒钟/几分钟。 第三,如果我没看错你的代码,看起来你是在开始歌曲后立即结束后台任务。这是你的意图吗?这似乎不对。或者您是否打开了背景音乐播放作为背景功能? 感谢您的评论,我正在制作一个闹钟应用程序,实际上音乐应该在设置为闹钟的时间播放。以上代码写成bgtaskschedular示例代码,进入后台1分钟后生成了BGAppRefreshTaskRequest。在闹钟应用程序中,它将是设置为闹钟的时间。那我应该用BG Processing Task在后台特定时间播放音乐吗? 那么,BGAppRefreshTask 肯定不是正确的工具。请参阅下面的答案。 【参考方案1】:

仅供参考,BGAppResfreshTask 用于“使用少量信息更新您的应用程序”,即执行一个小的网络请求以刷新您的应用程序,以便在用户下次启动应用程序时,您可以准备好更多当前信息并等待他们。但此应用刷新是在操作系统根据多种因素自行选择的时间执行的,但不得早于earliestBeginDate

因此不适合闹钟,因为 (a) 您没有发出网络请求来刷新您的应用; (b) 不能保证指定的“最早”日期运行,只能在之后的某个时间运行。

您可以考虑改为安排user notification。


你问:

如何测试不使用调试功能的设备?

您添加日志记录语句。但与其使用printNSLog,不如添加Logger 语句,如WWDC 2020 Explore logging in Swift 中所述。 (或者,如果支持 iOS 14 之前的 iOS 版本,请使用 os_log;这在 WWDC 2016 视频 Unified Logging and Activity Tracing 中有描述,但该视频不再可用。)这些 Logger /os_log 从 iOS 应用发出的日志记录语句可以从 macOS 控制台应用进行监控。

因此,一旦您在相关位置的代码中添加了日志消息,使用Logger(或os_log),您就可以

在您的设备上安装应用, 将设备连接到您的计算机, 直接从您的设备启动应用并 您可以在 macOS 控制台中查看您的应用发出的日志语句。

请参阅Swift: print() vs println() vs NSLog() 中的第 3 点和第 4 点。

但请注意,您不想从 Xcode 运行应用程序。您可以通过从 Xcode 运行它来安装它,然后停止执行并直接在设备上重新启动应用程序,而不是使用 Xcode。不幸的是,附加到 Xcode 调试器后,应用程序会在后台人为地运行,而在设备上独立运行时,它实际上会被挂起。因此,在物理设备上测试后台执行时,不要直接从 Xcode 调试它,而是添加日志语句,直接从设备启动应用程序,并在 macOS 控制台中查看日志语句。

另外,有时后台进程会在数小时后发生,因此我偶尔也会将日志语句写入应用程序支持目录中的文本文件,并稍后重新访问该文件(稍后将容器下载回我的 Mac)。对于后台获取和后台任务(可能在数小时或数天后发生),这可能很有用。但是,对于警报应用程序,上面概述的 macOS 控制台方法是最简单的。

【讨论】:

以上是关于如何在 ios 设备中测试不使用调试功能的 bgtaskscheduler 功能的主要内容,如果未能解决你的问题,请参考以下文章

iOS:如何从设备中完全删除应用程序以进行调试

如何使用iOS模拟器调试Unity项目?

Xcode7.2如何真机调试iOS 9.3的设备

iOS添加测试设备与调试

如何调试由于内存压力导致的 iOS 崩溃

9,如何在Xamarin中进行iOS真机调试和发布