2015-01-29 5 views
0

Я работаю над приложением, где я синхронизую данные с сервером и с него, когда приложение входит в фон и приложение становится активным. Поскольку при синхронизации с несколькими устройствами данные должны обновляться на всех устройствах.Эффективно синхронизировать данные в iOS

Так вот как я это делаю,

- (void)applicationDidBecomeActive:(UIApplication *)application 
{ 
    if([[AppManager instance].globalManager activeSyncCounter] == 0) 
    { 
      [_webService callServerAPI];   
    } 
} 


- (void)applicationDidEnterBackground:(UIApplication *)application 
{ 
    if([[AppManager instance].globalManager activeSyncCounter] == 0) 
    { 
      [_webService callServerAPI];   
    } 
} 

Позвольте мне объяснить код, указанный выше

1) Я звоню API, чтобы синхронизировать свои данные, я итерацию через цикл для синхронизации мои данные (все вызовы являются асинхронными)

2) Как вы можете видеть, я использовал activeSyncCounter. Я использовал это, потому что пользователь может немедленно открыть и закрыть приложение, чтобы серверные API не вызывались снова, до тех пор, пока первый не завершится.

Теперь вот мои сомнения.

1) Во-первых, когда я получаю ответ от сервера, когда данные больше, требуется время для обновления, проходящего через цикл for, и мой пользовательский интерфейс становится невосприимчивым. Чтобы улучшить это, мне нужно запустить код цикла for внутри очереди отправки. Или это их другой лучший способ сделать это?

Кроме того, поскольку обновление выполняется, когда приложение входит в фоновый режим, так мне нужна диспетчерская очередь, когда приложение входит в фон?

2) Во-вторых, их любая альтернатива для использования activeSyncCounter

ответ

1

Вы (почти) никогда не хочу делать каких-либо синхронных операций ввода/вывода (в том числе сетевых операций) на главном потоке, поэтому к вам первый вопрос , да, вы должны либо использовать асинхронные сетевые API, либо перевести синхронную операцию в другой поток - очереди отправки - это один из способов сделать это.

Что касается вашего второго вопроса, я не знаю, что есть определенно «лучший» способ, но есть разные способы. Одна вещь, которую я мог бы предложить, - это переместить эту проверку в callServerAPI, чтобы упростить код, вызывающий этот метод. (т. е. написать код проверки один раз, а не везде вы вызываете API-интерфейс сервера.)

+0

Здравствуйте @ipmcc, я использую только асинхронные сетевые API. Поэтому я должен использовать очереди отправки, когда я обрабатываю ответ с сервера правильно? Но обработка ответа с сервера происходит, когда приложение вводит фон. Итак, нужны ли очереди отправки, когда приложение входит в фон или только когда приложение становится активным? – Ranjit

+0

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

+0

Да, после обработки занимает время – Ranjit

0

Важно, чтобы ваши сетевые операции выполнялись asynch и заканчивались вызовом блока завершения. NSURLConnection предоставляет удобный метод, называемый sendAsynchronousRequest:completionHandler:, который делает именно это.

Поскольку часть работы, которую вы хотите сделать, должна быть выполнена при входе в фоновое изображение, ваше приложение должно оставаться в живых достаточно долго, чтобы закончить. Apple provides a complete article here, но суть (настройки их примеры кода для вашей ситуации) ...

- (void)applicationDidEnterBackground:(UIApplication *)application 
{ 
    bgTask = [application beginBackgroundTaskWithName:@"MyTask" expirationHandler:^{ 
     // Clean up any unfinished task business by marking where you 
     // stopped or ending the task outright. 
     [application endBackgroundTask:bgTask]; 
     bgTask = UIBackgroundTaskInvalid; 
    }]; 

    // this is your call, modified to use a completion block 
    [_webService callServerAPIWithCompletion:^(id result, NSError *error) { 
     // set state to indicate that the synch finished...see below 
     [application endBackgroundTask:bgTask]; 
     bgTask = UIBackgroundTaskInvalid;   
    }]; 
} 

Так что я думаю, что у вас есть два бита работы, чтобы сделать: (1) реорганизовать сетевой код для запуска асинхронных и заканчиваться вызывая блок и (2) придумать схему дроссельной заслонки, как часто вы запускаете сетевой запрос.

В последнем случае рассмотрите возможность сохранения NSDate в NSUserDefaults.Тогда вы можете спросить:

NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 
NSDate *lastSynch = [defaults objectForKey:@"MyApp_lastSynch"]; 

NSTimeInterval sinceLastSynch = -[lastSynch timeIntervalSinceNow]; 
if (sinceLastCheck < 3600) { 
    // we did the last synch less than an hour ago. skip it for now 
} else { 
    // do it 
} 

В завершение блока синхронизации, запишите текущее время, что вы закончили:

NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 
[defaults setObject:[NSDate date] forKey:@"MyApp_lastSynch"]; 
[defaults synchronize]; 

EDIT - вот как ваш сетевой код может выглядеть, если он используется блок-:

- (void)pushDataWithCompletion:(void (^)(BOOL, NSError))completion; 

Собираем все это вместе, ваше приложение код делегат может выглядеть лучше, как это:

- (void)applicationDidEnterBackground:(UIApplication *)application 
{ 
    // don't bother if we did this recently (60 sec in this e.g. 
    if ([self timeSinceLastPushData] < 60) return; 

    bgTask = [application beginBackgroundTaskWithName:@"MyTask" expirationHandler:^{ 
     [application endBackgroundTask:bgTask]; 
     bgTask = UIBackgroundTaskInvalid; 
    }]; 

    // this is your call, modified to use a completion block 
    [pushDataWithCompletion:^(BOOL success, NSError *error) { 
     if (success) { 
      NSLog(@"success. right down the time"); 
      [self pushDataComplete]; 
     } else { 
      NSLog(@"error %@, but we'll still tell os that we're done", error); 
     } 
     [application endBackgroundTask:bgTask]; 
     bgTask = UIBackgroundTaskInvalid;   
    }]; 
} 

- (void)pushDataComplete { 
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 
    [defaults setObject:[NSDate date] forKey:@"MyApp_lastSynch"]; 
    [defaults synchronize]; 
} 

- (NSTimeInterval)timeSinceLastPushData { 
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 
    NSDate *lastSynch = [defaults objectForKey:@"MyApp_lastSynch"]; 

    return -[lastSynch timeIntervalSinceNow]; 
} 
+0

Так как же мой пользовательский интерфейс, который застревает, когда их много данных для синхронизации? Мои все сетевые вызовы только async, хотя я не использую sendAsynchronousRequest: completeHandler: но я использую connectionDidFinishLoading: (NSURLConnection *). Также вы говорите, вместо того, чтобы использовать счетчик, я должен проверить со временем, не так ли? – Ranjit

+0

Эй, @danh, я использую NSUrlConnection и связанные с ним функции делегата. – Ranjit

+0

Ключ в том, что ваш делегатский код приложения должен быть проинформирован, когда callServerAPI закончен, чтобы он мог указать ОС, что фоновая работа выполнена (и не забудьте снова не звонить, если пользователь закрывается, а затем быстро открывает приложение). Вызов блочного стиля делает это очень простым, но его также можно использовать с помощью подхода делегирования соединения. Добавьте свой код 'callServerAPI' к вашему вопросу, и я попытаюсь дать вам несколько советов о том, как вернуть статус завершения вызывающему. – danh

Смежные вопросы