Как определить, при запуске, зависло ли приложение при последнем выходе? - PullRequest
7 голосов
/ 14 марта 2010

Одна из найденных мной сред репортеров сбоев делает это следующим образом: если в папке ~ / Library / Logs / CrashReporter для приложения существуют отчеты о сбоях, он определяет, что сбой произошел раньше. Затем он позволяет пользователю отправить разработчику журнал сбоев. И наконец, удаляет эти журналы .

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

Так что я ищу лучший способ определить аварию.

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

Есть идеи?

Ответы [ 3 ]

2 голосов
/ 14 марта 2010

Запись в файл каждый раз, когда вы выходите. Читайте этот файл каждый раз, когда вы открываете программу. Каждый раз при выходе из программы записывайте в файл определенное целое число, которое читается при каждом запуске программы.

Например, если 0 записано в файл, который может означать отсутствие сбоя. Все остальное означает крах.

1 голос
/ 09 мая 2014

Я решил это следующим образом (хотя для каждого запуска приложения необходимо обращаться к реестру):

@implementation ICNRegistry

/**
 * @public
 * @date edited 2014-04-28
 */
+ (ICNRegistry*) registry {

    static ICNRegistry* registrySingleton = nil;

    @synchronized(self) {

        if (!registrySingleton) {

            registrySingleton = [[ICNRegistry alloc] initPrivate];
        }

        return registrySingleton;
    }
}

#pragma mark -
#pragma mark Initialization

/**
 * @private
 * @date edited 2014-05-09
 */
- (id) initPrivate {

    self = [super init];

    if (self) {

        _appQuitSuccessfulIndicatorFilePath =
        [[self cacheRootForClass:[self class]] stringByAppendingPathComponent:
         @"didQuitSuccessfullyIndicator"];
            // (implementation of cacheRootForClass not included)

        // Set _lastAppQuitWasSuccessful:
        [self readPreviousAppSuccessStatusAndStartMonitoringCurrentLaunchQuit];

        // Start monitoring app quit:
        [[NSNotificationCenter defaultCenter] addObserver:self selector:
         @selector(registerAppWillQuitSuccessfully) name:
         UIApplicationWillTerminateNotification object:nil];
    }

    return self;
}

#pragma mark -
#pragma mark Monitoring App Quits

/**
 * @private
 * Checks if a file exists at a specific location. If the file exists, then
 * last app quit was successful and lastAppQuitWasSuccessful will be set to YES.
 * Otherwise, if the file does not exist, lastAppQuitWasSuccessful will be set 
 * to NO.
 *
 * @discussion
 * When app quits in a normal way, a file will be created on app termination, that
 * way indicating successful quit. If the app quits unexpectedly, then the file 
 * will not be written and thus the quit will be indicated as not being successful.
 *
 * @since 2014-05-09
 * @date edited 2014-05-09
 */
- (void) readPreviousAppSuccessStatusAndStartMonitoringCurrentLaunchQuit {

    NSFileManager* fm = [NSFileManager defaultManager];

    self.lastAppQuitWasSuccessful = NO;

    if ([fm fileExistsAtPath:self.appQuitSuccessfulIndicatorFilePath]) {

        self.lastAppQuitWasSuccessful = YES;

        NSError* error;
        [fm removeItemAtPath:self.appQuitSuccessfulIndicatorFilePath error:&error];

        if (error) {

            ICN_WARN(@"failed to delete 'app quit successful' indicator file at "
                     @"path %@; %@", self.appQuitSuccessfulIndicatorFilePath,
                     error.localizedDescription);
        }
    }
}

/**
 * @private
 * @since 2014-05-09
 * @date edited 2014-05-09
 */
- (void) registerAppWillQuitSuccessfully {

    NSFileManager* fm = [NSFileManager defaultManager];

    // Create file path if not present:
    NSError* error;
    NSString* dir = [self.appQuitSuccessfulIndicatorFilePath
                     stringByDeletingLastPathComponent];
    [fm createDirectoryAtPath:dir withIntermediateDirectories:YES
                   attributes:nil error:&error];

    if (error) {

        ICN_WARN(@"failed to create dir %@; %@", dir, error.localizedDescription);
        return;
    }

    [fm createFileAtPath:self.appQuitSuccessfulIndicatorFilePath
                contents:nil attributes:nil];
}

#pragma mark -

@end

Затем, для иллюстрации, в моем делегате приложения я делаю что-то вроде этого:

- (BOOL) application:(UIApplication*)application
didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {

    // If app did quit unexpectedly (due to crash, reset, etc) last time it was
    // used, then clear all cache to avoid inconsistencies
    if (![[ICNRegistry registry] lastAppQuitWasSuccessful]) {

        [ICNUIViewImageCache clearCache];
    }
}

Следует отметить, что при использовании этого решения ПЕРВЫЙ раз, когда приложение запускается, оно будет обнаруживаться так, как если бы оно раньше вылетало. Если это проблема, индикатор файла может быть создан при первом запуске приложения.

0 голосов
/ 02 февраля 2012

Это старый пост, но я искал хороший способ сделать это и не нашел ни одного.

Возможно, это поможет кому-то в подобной ситуации ..

Я делаю это в Windows, но, возможно, это также возможно в osx / linux с использованием именованных мьютексов pthread, хотя я не знаю политики в отношении того, как обрабатываются такие мьютексы, если приложение завершается, что крайне важно для этого. техника.

Проблема с использованием одного флага заключается в том, что он не работает в ситуации, когда может быть несколько экземпляров приложения:

  • Приложение 1 запускается. Устанавливает флаг «не выключен должным образом».
  • Приложение 2 запускается. Видит, что он не выключился, потому что флаг установлен, и думает, что произошел сбой, когда его не было.

Это немного сложно обойти, и, возможно, есть несколько способов сделать это, но этот метод пока работает нормально (по крайней мере, в Windows):

Создать глобальный именованный мьютекс с вновь созданным GUID / UUID в качестве имени.
GUID различен для каждого экземпляра приложения, поэтому у каждого будет уникальное имя mutex. GUID записывается в файл, содержащий список GUID. Если приложение закрывается правильно, вы удаляете GUID из файла, и мьютекс закрывается. В случае сбоя или прекращения работы приложения GUID не удаляется из файла, НО мьютекс уничтожается ОС, и приложение не должно это делать.

Когда вы запускаете приложение, вы можете запустить GUID в списке и вызвать для них OpenMutex и мьютексы, которые не существуют (GetLastError возвращает ERROR_FILE_NOT_FOUND). Любые несуществующие взаимные исключения указывают, что произошел сбой / завершение. Все GUID, которые имеют это свойство, могут быть удалены из списка на этом этапе.

Еще одна вещь, которую вы можете сделать, - это удалить неправильные GUID из списка при правильном завершении работы. Это обходит следующую ситуацию:

  • Приложение 1 запускается
  • Приложение 2 запускается
  • Приложение 1 вылетает
  • Приложение 2 успешно закрыто
  • Приложение 3 запущено.

Когда приложение 3 запускается, оно не должно сообщать о том, что произошел сбой, потому что последнее завершение работы прошло успешно.

Я также реализовал своего рода спин-блокировку при получении доступа к файлу, содержащему идентификаторы GUID, которые будут выполняться после истечения времени ожидания и не будут добавлять guid приложения в файл. В таких случаях, вероятно, лучше всего запустить приложение и потерять обнаружение сбоев, а не запускать приложение вообще.

Другие предупреждения должны сохранять блокировку файла при запросе и обновлении файла, чтобы избежать условий гонки. В Windows это предотвращает использование файловых потоков stl, так как вам нужно будет обрезать файл после прочтения, при этом сохраняя монопольный доступ к файлу.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...