当应用程序处于后台模式时,HealthKit Observer 不工作

Posted

技术标签:

【中文标题】当应用程序处于后台模式时,HealthKit Observer 不工作【英文标题】:HealthKit Observer not working while app is in background mode 【发布时间】:2015-05-29 20:00:26 【问题描述】:

我已关注Apple Docs 和 *** 上的几个线程,了解如何实现从 Health Store 后台获取数据。 到目前为止,我有:

在我的 appID 中添加了 HealthKit 授权 添加了必需的后台模式 按照 Apple 的建议将代码添加到 AppDelegate.swift下面的 sn-p 不遵循 OOP 只是为了方便在此处进行说明

这是我的代码(快速): 如果您的答案是 Obj-C 并且有效,请同时说明,我将不得不翻译它,但这没问题。

AppDelegate.swift

var healthStore: HKHealthStore?
var bpmSamples: [HKQuantitySample]?

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool 
    let dataTypesToWrite = [ ]
    let dataTypesToRead = [
        HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeartRate),
        HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBodyMassIndex),
        HKCharacteristicType.characteristicTypeForIdentifier(HKCharacteristicTypeIdentifierDateOfBirth)
    ]
    if self.healthStore == nil 
        self.healthStore = HKHealthStore()
    
    self.healthStore?.requestAuthorizationToShareTypes(NSSet(array: dataTypesToWrite as [AnyObject]) as Set<NSObject>,
        readTypes: NSSet(array: dataTypesToRead) as Set<NSObject>, completion: 
            (success, error) in
            if success 
                self.addQueryObserver()
                println("User completed authorisation request.")
             else 
                println("The user cancelled the authorisation request. \(error)")
            
    )
    return true


func addQueryObserver()
    let sampleType =
    HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeartRate)

    let query = HKObserverQuery(sampleType: sampleType, predicate: nil) 
        query, completionHandler, error in
        if error != nil 
            // Perform Proper Error Handling Here...
            println("*** An error occured while setting up the stepCount observer. \(error.localizedDescription) ***")
            abort()
        
        println("query is running")

        self.performQueryForHeartBeatSamples()
        completionHandler()
    
    healthStore?.executeQuery(query)
    healthStore?.enableBackgroundDeliveryForType(sampleType, frequency:.Immediate, withCompletion:
        (success:Bool, error:NSError!) -> Void in
        let authorized = self.healthStore!.authorizationStatusForType(sampleType)
        println("HEALTH callback success", success)
        println("HEALTH callback authorized", sampleType)
    )
    if HKHealthStore.isHealthDataAvailable() == false 
        println("HEALTH data not available")
        return
     else 
        println("HEALTH OK")
        self.performQueryForHeartBeatSamples()
    

 // MARK: - HealthStore utility methods
func performQueryForHeartBeatSamples() 
    let endDate = NSDate()
    let startDate = NSCalendar.currentCalendar().dateByAddingUnit(.CalendarUnitMonth, value: -2, toDate: endDate, options: nil)

    var heartRate : HKQuantityType = HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeartRate)

    let predicate = HKQuery.predicateForSamplesWithStartDate(startDate, endDate: endDate, options: .None)
    let query = HKSampleQuery(sampleType: heartRate, predicate: predicate, limit: 0, sortDescriptors: nil, resultsHandler: 
        (query, results, error) in
        if results == nil 
            println("There was an error running the query: \(error)")
        
        dispatch_async(dispatch_get_main_queue()) 
            self.bpmSamples = results as? [HKQuantitySample]
            let heartRateUnit: HKUnit = HKUnit.countUnit().unitDividedByUnit(HKUnit.minuteUnit())
            if self.bpmSamples?.count > 0 
                if let sample = self.bpmSamples?[self.bpmSamples!.count - 1] 
                    println(sample.quantity!.description)
                    let quantity = sample.quantity
                    var value = quantity.doubleValueForUnit(heartRateUnit)
                    println("bpm: \(value)")
                
            
            else 
                println("No Data")
            
        
    )
    self.healthStore?.executeQuery(query)

所以,问题是我只有在我手动将应用程序从后台恢复到活动状态时才会收到更新。HKObserverQuery 在后台模式下似乎对我不起作用。

有什么建议吗?

【问题讨论】:

您是否在设备上进行调试,并且是否从“调试”菜单手动模拟了后台提取? 我在真实设备和模拟器设备上都对其进行了测试,在这两种情况下,我都将新数据手动添加到 HealthKit 中,并等待HKObserverQuery 发挥作用。我刚刚测试了后台获取,但它什么也没做。 模拟器不行,要在真机上模拟后台抓取,你试过吗? 问题似乎是当我进入健康应用程序或我的应用程序时,它会强制健康应用程序刷新数据。虽然我的应用程序处于打开状态,但 Health 应用程序仅在它认为正确时才加载数据(而不是在我要求 HealthKit 这样做时)..所以..我对数据的请求很遗憾地不同步,直到 Health 应用程序决定刷新或者从 Apple Watch 上的传感器获取数据后,可能需要很长时间才能从 Watch 获取数据。无论哪种方式,从开发人员的角度来看,这都不是应有的方式。我很快就会在 Watch OS 2 上进行测试。 你找到解决办法了吗@HugoAlonso? 【参考方案1】:

根据我的经验,频率“.Immediate”效果不佳。查询处理程序被插入后台队列。如果频繁添加匹配样本或ios繁忙,则立即频率效果不佳。

此外,您不能在HKObserverQuery 中使用HKSampleQueryHKObserverQueryupdateHandler 可能有效,但 HKSampleQueryresultHandler 不会。观察者查询的处理程序可以在后台模式下执行,但示例查询之一不能在后台模式下执行。

你应该知道只有HKObserverQuery可以在后台使用

【讨论】:

你在哪里找到这个信息:“你不能在 HKObserverQuery 中使用 HKSampleQuery”? @user3352495 太久以前了,所以我不记得句子的确切位置。但是,我记得所有关于 HealthKit 的 API 中只有 HKObserverQuery 可以在后台使用。当操作系统版本上升时,它可能与旧的 HealthKit 不同,但本着苹果的开发者精神,他们不希望应用程序使用后台资源。因此,我认为它可能还一样。

以上是关于当应用程序处于后台模式时,HealthKit Observer 不工作的主要内容,如果未能解决你的问题,请参考以下文章

当 Healthkit 在后台唤醒我的应用程序时,AppDelegate 是不是会初始化?

当应用程序处于后台模式时获取通知

当应用程序处于后台模式时,是不是可以在 iPhone 中闪烁手电筒?

当应用程序未使用或处于后台模式时,React Native 是不是有函数生命周期正在运行?

当应用程序处于后台模式时,电池电量通知不起作用

在几秒钟内多次调用 Healthkit 后台交付