2015-01-25 2 views
7

Я работаю над приложением iPhone, которое отслеживает, сколько шагов занимает человек, и сколько времени они инвестируют в это. Счетчик шагов выполняется с использованием класса CMStepCounter класса . Тем не менее, временные метки предоставляются только живыми шагами, а не историческими шагами. Поэтому, чтобы проверить, сколько времени прошло во время ходьбы, я использую CMMotionActivityManager.Почему CMMotionActivityManager не возвращает никаких последних данных о деятельности?

У меня есть два метода: один для подсчета в реальном времени и один для получения исторических данных, а приложение - в фоновом режиме. Живой подсчет работает достаточно хорошо, но метод для получения данных о исторической активности не работает! Когда приложение входит в фоновый режим, я сохраняю текущую дату (через NSUserDefaults), и когда приложение снова появляется на переднем плане, я использую queryActivityStartingFromDate с указанной датой в качестве даты начала и текущей датой в качестве даты окончания. Таким образом, он должен рассказать мне о действиях, которые произошли в то же время, когда приложение было приостановлено. Но обычно множество действий оказывается пустым, даже если между ними есть движение. Если я вступлю в начальный день в качестве даты начала, я получаю огромное количество действий, но иногда бывает некоторое количество точных количеств записей.

У меня возникает ощущение, что задержка в регистрации действий. Может быть, это моя проблема? Но тогда, даже если я подожду за полчаса до возобновления приложения, правильная регистрация активности, которая произошла между ними, не гарантируется.

Метод получения данных о исторической активности находится в нижней части этой публикации. Он вызывается при запуске, а затем каждый раз, когда приложение возвращается на передний план. Это всегда вызывается с

[self calculateHistoricalActivitySince: _enteredBackgroundAt]; 

_enteredBackground является NSDate. При запуске вызывается метод восстановления некоторых NSUserDefaults, среди которых указанная дата. Каждый раз, когда приложение входит в фоновый режим, оно устанавливается на текущую дату. При восстановлении этой даты метод гарантирует, что это будет с того же дня - если это более старая дата (т. Е. Последнее приложение было использовано несколько дней назад), то оно устанавливается на начало текущего дня (0 утра сегодня). self.activityInSecondsToday - это NSInteger.

-(void)calculateHistoricalActivitySince: (NSDate *) date 
{ 

[_motionActivityManager queryActivityStartingFromDate: date 
               toDate: [NSDate date] 
               toQueue: _activityQueue 
              withHandler:^(NSArray *activities, 
                 NSError *error) 
{ 
    if ([error code] == CMErrorUnknown) 
    { 
     NSLog(@"Motion Activity Manager Error: %@", error); 
    } 

    // Fill my activity array with historical data if it was walking or running 
    else 
    { 
     NSLog(@"Historical Total Activities from %@ \rto %@:\r %i", date, [NSDate date], (int)[activities count]); 

     // Create an array for all relevant activity data 
     _activityStorage = [NSMutableArray new]; 

     for (int i = 0; i < [activities count]; i++) 
     { 
      if([[activities objectAtIndex: i] walking] || [[activities objectAtIndex: i] running] || [[activities objectAtIndex: i] unknown]) 
       [_activityStorage addObject: [activities objectAtIndex: i]]; 
     } 

     // Calculate the time interval between each entry and increase activityInSecondsToday if the interval is not too large. 
     for (int i = 1; i < [_activityStorage count]; i++) 
     { 
      NSTimeInterval timeInterval = [[[_activityStorage objectAtIndex: i] startDate] timeIntervalSinceDate:[[_activityStorage objectAtIndex: i-1] startDate]]; 

      if (timeInterval <= _maxTimeBetweenTimestampsForContinuedActivity) 
       self.activityInSecondsToday += timeInterval; 

      NSLog(@"ACTIVITY SINCE: %@ \r %ld", date, _activityInSecondsToday); 
     } 
     NSLog(@"Last activity’s timestamp: %@", [[_activityStorage objectAtIndex: [_activityStorage count]-1] startDate]); 

     [_activityStorage removeAllObjects]; 
    } 
}]; 

}

+0

Попробуйте выполнить запрос _twice_. У меня есть ощущение, что нужно «заправлять насос» - первый запрос вроде неэффективен. – matt

+0

Спасибо за предложение! Но я пробовал, и это не работает. : -/Я просто поставить запрос с пустым обработчиком выше обычного запроса: [_motionActivityManager queryActivityStartingFromDate: Дата ФОРУМА: [дата] NSDate toQueue: _activityQueue withHandler:^(NSArray * деятельность, NSError * Ошибка) {}]; – willi

+0

Возможно, проблема в очереди? Я настраиваю очередь следующим образом: _activityQueue.maxConcurrentOperationCount = 1; _activityQueue = [NSOperationQueue new]; – willi

ответ

1

Так, кажется, там не много информации по этой теме в Интернете. Но я определил решение самостоятельно, которое я хотел бы поделиться, если кто-то захочет сделать что-то подобное.

Начиная с iOS 8, CMStepCounter уже устарел. Теперь есть CMPedometer, поэтому я принял этот API. Для этого больше не требуется CMMotionActivityManager. Это облегчает! Проблема сверхъестественной задержки, описанная выше, также не возникает с CMPedometer.

Проблема аналогична предыдущей: для подсчета в реальном времени вы получаете шаги и временные метки (теперь маскируются как даты начала и окончания, и все заключено в контейнер под названием pedometerData); но для исторических данных вы получаете менее точные данные. Новый API дает вам даты начала и окончания для исторических данных, что отлично. Но я выяснил, что API очень ленив, поэтому, например, если вам нужны данные со вчерашнего дня на рассвете до вчерашнего дня в полночь, датой начала будет дата первого распознанного действия и дата окончания последнего. Все, что было между ними, было бы одной огромной часовой деятельностью! Он не будет инкапсулировать действия в отдельные объекты pedometerData с точными метками времени, как это происходит с подсчетом в реальном времени.

Так мое решение опроса API для меньших временных рамок рекурсивно. У меня есть дата начала в недавнем прошлом, и я хочу всю информацию о деятельности (шаги и время, проведенные пешком) с этого момента до сих пор. Поэтому я беру временные рамки, которые хочу охватить, и я разделил их на более короткие промежутки времени. Затем я рекурсивно спрашиваю [CMPedometer queryPedometerDataFromDate], были ли какие-либо шаги в каждом интервале. Если это так, я добавляю эти шаги к своей переменной частного шага, и я добавляю время активности (endDate - startDate) в свою переменную private seconds. Если текущий интервал все еще в прошлом, я перейду к следующему интервалу и повторю то же самое, рекурсивно.

Таким образом, вы можете получить довольно точные и надежные результаты. Конечно, чем меньше и точнее ваши интервалы, тем чаще этот метод будет выполняться, и чем дольше это потребуется. Таким образом, у меня есть три разных интервала (один час, одна минута, 5 секунд), так что время без какой-либо активности может быть пропущено быстрее. Очевидно, что существует много возможностей для оптимизации.

0

У меня была такая же проблема с моей собственной ошибкой, которая является fromDate позже, чем сейчас. Меняя значение ниже от 7 до -7, оно работает так, как ожидалось.

// in Swift 3 
let day_7daysAgo = NSCalendar.current.date(byAdding: 
    Calendar.Component.day, value: -7, to: Date())! 

motActMgr.queryActivityStarting(from: day_7daysAgo, to: Date(), to: OperationQueue.main) { (ma_: [CMMotionActivity]?, e: Error?) in 
} 
Смежные вопросы