启动时如何确定应用程序在上次退出时是否崩溃?

时间:2010-03-13 23:17:33

标签: objective-c cocoa macos

我发现其中一个崩溃报告框架是这样的:如果应用程序的〜/ Library / Logs / CrashReporter文件夹中存在崩溃报告,它会确定之前发生了崩溃。然后它允许用户向开发人员发送崩溃日志。最后,删除那些日志

正是这个删除困扰着我。这对我来说是不可接受的。也许用户想要稍后查看这些日志。用户只是删除他的崩溃日志是很粗鲁的。

所以我正在寻找一种更好的方法来确定崩溃。

将最后读取的崩溃日志存储在用户默认值或用户数据中并不真正起作用,因为这意味着如果用户删除了数据和默认值(他或她有权在任何时候执行此操作)希望),然后启动应用程序,它将被检测为最后一次退出时崩溃。所以这不起作用。

有什么想法吗?

3 个答案:

答案 0 :(得分:2)

每次退出时写入文件。每次打开程序时都要读取该文件。每次退出程序时都会向文件写入一个特定的整数,每次启动程序时都会读取该整数。

例如,如果将0写入可能表示非崩溃的文件。其他任何事情都意味着崩溃。

答案 1 :(得分:1)

我用这种方式解决了(虽然必须为每个应用程序启动访问注册表):

@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

然后,为了说明,在我的app委托中,我做了类似这样的事情:

- (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];
    }
}

应该注意的是,使用此解决方案,第一次启动应用程序时,它将被检测到它之前是否崩溃。如果这是一个问题,那么可以在第一次启动应用程序时创建文件指示器。

答案 2 :(得分:0)

这是一篇很老的帖子,但我一直在寻找一种很好的方法来做到这一点并没有遇到过。

也许这会帮助处于类似情况的人。

我在Windows上这样做,但也许在osx / linux上也可以使用命名的pthread互斥,虽然我不知道如果终止应用程序如何处理这些互斥锁的策略,这对此至关重要技术

使用单个标志的问题是它在可能存在多个应用程序实例的情况下不起作用:

  • App 1运行。设置'没有正确关闭标志'。
  • App 2运行。看到它没有因为标志设置而关闭,并认为没有时会发生崩溃。

这有点难以解决,并且可能有几种方法可以做到这一点,但到目前为止这种方法似乎正常(至少在Windows上):

使用新生成的GUID / UUID作为名称创建一个全局命名的互斥锁 每个应用实例的GUID都不同,因此每个实例都有一个唯一的命名互斥锁。 GUID将写入包含GUID列表的文件。 如果应用程序正确关闭,则从文件中删除GUID并关闭互斥锁。 如果应用程序崩溃或终止,则不会从文件中删除GUID但是操作系统会破坏互斥锁,而无需应用程序执行此操作。

启动应用程序时,可以运行列表中的GUID并在其上调用OpenMutex和不存在的互斥锁(GetLastError返回ERROR_FILE_NOT_FOUND)。任何不存在的互斥锁都表示发生了崩溃/终止。 此时,可以从列表中删除具有此属性的所有GUID。

您可以做的另一件事是在正确关闭时从列表中删除错误的GUID。 这可以解决以下问题:

  • App 1启动
  • App 2启动
  • App 1崩溃
  • App 2已成功关闭
  • App 3已启动。

当App 3启动时,它不应该发布崩溃,因为最后一次关闭是好的。

我还实现了一种自旋锁,以获取对包含GUID的文件的访问权限,该文件将在超时期限后继续执行并且不会将app guid添加到文件中。在这种情况下,最好让应用程序运行并丢失崩溃检测,而不是根本不运行应用程序。

其他警告是在查询和更新文件时保持文件锁定以避免竞争条件。在Windows上,这可以防止使用stl文件流,因为您需要在读取文件后截断文件,同时仍然保持对文件的独占访问。