2010-03-13 6 views
7

L'un des frameworks de rapport de crash que j'ai trouvé le fait comme ceci: S'il existe des rapports de crash dans le dossier ~/Library/Logs/CrashReporter pour l'application, il détermine qu'un crash s'est produit avant. Ensuite, il permet à l'utilisateur d'envoyer au développeur le journal des pannes. Et enfin, il supprime ces journaux.Comment déterminer, au lancement, si l'application s'est plantée la dernière fois qu'elle a quitté?

C'est cette suppression qui me dérange. Ce n'est pas acceptable pour moi. Peut-être que l'utilisateur veut regarder ces journaux plus tard. Il est simplement impoli pour l'utilisateur de simplement supprimer ses journaux de plantage. Donc, je cherche un meilleur moyen de déterminer le crash. Il ne fonctionne pas vraiment pour stocker les derniers journaux de plantage lus dans les valeurs par défaut utilisateur ou les données utilisateur, car cela signifierait que si l'utilisateur supprime les données et les valeurs par défaut (qu'il a le droit de faire quand il ou elle souhaite), puis lance l'application, il sera détecté comme écrasé la dernière fois qu'il a été abandonné. Donc ça ne marche pas.

Des idées?

Répondre

2

Écrire dans un fichier chaque fois que vous quittez. Lisez ce fichier chaque fois que vous ouvrez le programme. Ecrire un entier spécifique au fichier chaque fois que le programme est quitté qui est lu chaque fois que le programme est démarré.

Par exemple, si un 0 est écrit dans un fichier pouvant signifier un non-crash. Tout le reste signifie un crash.

+1

Hmm? Et où pourrais-je écrire ce fichier? Êtes-vous sûr que l'application est envoyée willQuit ou de telles méthodes de notification avant qu'elle ne se bloque *? Et même si c'était le cas, est-ce que l'une de ces méthodes willQuit possède un paramètre indiquant que l'application plante ou ne plante pas? – Enchilada

+1

Oooh, je pense que je t'ai. Vous voulez juste mettre un SuccessfulQuit dans les valeurs par défaut de l'utilisateur, qui est toujours défini sur 1 lors de la fermeture réussie, est 0 par défaut et est défini sur -1 après le lancement de l'application. Et puis, si c'est effectivement -1 au lancement, cela signifie que je dois avoir crashé. Quelque chose comme ca? – Enchilada

+0

Oui, et la lecture/écriture du fichier dans un emplacement vous appartient. –

0

Ceci est un ancien article, mais je cherchais un bon moyen de le faire et je n'ai pas rencontré un.

Peut-être que cela va aider quelqu'un dans une situation similaire ..

Je fais cela sur Windows, mais peut-être il est également possible sur Mac OS X/Linux en utilisant pthread mutex nommé, bien que je ne connais pas la politique de comment ces mutex sont traités si une application est terminée, ce qui est essentiel à cette technique.

Le problème avec l'aide d'un drapeau unique est qu'il ne fonctionne pas dans la situation où il peut y avoir plusieurs instances de l'application:

  • App 1 fonctionne. Définit le drapeau 'N'a pas arrêté correctement'.
  • L'application 2 s'exécute. Voit qu'il ne s'est pas arrêté parce que le drapeau est positionné et pense qu'il y a eu un crash alors qu'il n'y en avait pas.

C'est un peu difficile de se déplacer et il y a probablement plusieurs façons de le faire, mais cette méthode semble fonctionner OK jusqu'à présent (sous Windows, au moins):

Créer un mutex global nommé avec un GUID/UUID nouvellement généré comme nom.
Le GUID est différent pour chaque instance de l'application, chacun aura donc un mutex nommé unique. Le GUID est écrit dans un fichier contenant une liste de GUID. Si l'application s'arrête correctement, vous supprimez le GUID du fichier et le mutex est fermé. Si l'application tombe en panne ou est terminée, le GUID n'est pas supprimé du fichier MAIS le mutex est détruit par le système d'exploitation sans que l'application ne doive le faire.

Lorsque vous démarrez l'application, vous pouvez courir à travers les GUID dans la liste et appeler OpenMutex sur eux et les mutex qui n'existent pas (GetLastError renvoie ERROR_FILE_NOT_FOUND).Tout mutex inexistant indique qu'un incident/une interruption s'est produit. Tous les GUID qui ont cette propriété peuvent être supprimés de la liste à ce stade.

Une autre chose que vous pouvez faire est de supprimer les mauvais GUID de la liste lorsque vous arrêtez correctement. Cela contourne la situation suivante:

  • App 1 commence
  • App 2 commence
  • App 1 plante
  • App 2 est arrêté avec succès
  • App 3 est commencé. Lorsque App 3 est démarré, il ne devrait pas signaler qu'il y a eu un plantage, car le dernier arrêt était bon.

J'ai également implémenté une sorte de spinlock sur l'accès au fichier contenant les GUID qui continueront après une période de temporisation et ne pas ajouter l'application guid au fichier. Dans de tels cas, il est probablement préférable de laisser l'application s'exécuter de toute façon et de perdre la détection de l'accident, plutôt que de ne pas exécuter l'application du tout.

D'autres mises en garde sont à conserver sur le verrou de fichier lors de l'interrogation et la mise à jour du fichier pour éviter les conditions de course. Sur Windows, cela empêche l'utilisation des flux de fichiers stl, car vous aurez besoin de tronquer le fichier après l'avoir lu tout en conservant un accès exclusif au fichier.

1

Je résolus de cette façon (le registre doit être accessible pour chaque lancement de l'application bien):

@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 

Puis, à titre d'illustration, dans mon délégué app je fais quelque chose comme ceci:

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

Il convient de noter qu'avec cette solution, la première fois que l'application est lancée, elle sera détectée comme si elle s'était déjà arrêtée. S'il s'agit d'un problème, l'indicateur de fichier peut être créé la première fois que l'application est lancée.

Questions connexes