7
votes

Comment trouver la source des plantages de type java.lang.RuntimeException: Parcel android.os.Parcel@####: Unmarshalling code de type inconnu XXXX au décalage YYY

Notre système de rapport de plantage enregistre les plantages de ce type:

Caused by java.lang.RuntimeException: Parcel android.os.Parcel@8bf0d1f: Unmarshalling unknown type code 6881391 at offset 356
   at android.os.Parcel.readValue(Parcel.java:2779)
   at android.os.Parcel.readSparseArrayInternal(Parcel.java:3148)
   at android.os.Parcel.readSparseArray(Parcel.java:2362)
   at android.os.Parcel.readValue(Parcel.java:2757)
   at android.os.Parcel.readArrayMapInternal(Parcel.java:3067)
   at android.os.BaseBundle.unparcel(BaseBundle.java:257)
   at android.os.Bundle.getSparseParcelableArray(Bundle.java:958)
   at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1329)
   at android.support.v4.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManager.java:1759)
   at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1827)
   at android.support.v4.app.FragmentManagerImpl.dispatchStateChange(FragmentManager.java:3244)
   at android.support.v4.app.FragmentManagerImpl.dispatchCreate(FragmentManager.java:3194)
   at android.support.v4.app.Fragment.restoreChildFragmentState(Fragment.java:1444)
   at android.support.v4.app.Fragment.onCreate(Fragment.java:1415)
   at com.payments.base.BaseFragment.onCreate(BaseFragment.java:68)
   at com.payments.app.fragments.TopLevelFragment.onCreate(TopLevelFragment.java:422)
   at android.support.v4.app.Fragment.performCreate(Fragment.java:2331)
   at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1386)
   at android.support.v4.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManager.java:1759)
   at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1827)
   at android.support.v4.app.FragmentManagerImpl.dispatchStateChange(FragmentManager.java:3244)
   at android.support.v4.app.FragmentManagerImpl.dispatchCreate(FragmentManager.java:3194)
   at android.support.v4.app.Fragment.restoreChildFragmentState(Fragment.java:1444)
   at android.support.v4.app.Fragment.onCreate(Fragment.java:1415)

Ce que je sais: cela se produit lorsque l'application est restaurée à partir de l'arrière-plan et onCreate est appelé. Notre application est une application à activité unique, avec toute l'interface utilisateur gérée par des fragments. Ce plantage est rare et très difficile à reproduire dans notre environnement de développement.

De plus, je ne pense pas que la cause soit un objet parcelable que nous avons créé, plutôt que le rechargement de composants Android, mais pas sûr.

Ce que j'aimerais savoir: comment analyser ces stacktraces pour en identifier la cause? comment utiliser les quelques données fournies?

Notez que le stacktrace pointe à peine vers une ligne particulière dans notre application, et où il le fait - uniquement vers les méthodes onCreate de notre activité et fragmente les classes de base


7 commentaires

Je commence une prime pour ces questions car mon application a exactement la même erreur et Google Developer Console ne donne aucune indication sur la façon de la corriger (ou même de la retracer). Cela se produit-il uniquement en production? Je soupçonne une mauvaise configuration de ProGuard ...


issuetracker.google.com/issues/37083630


Cela se produit donc lorsque les fragments ne sont pas regroupés après un changement de configuration / une restauration d'application / peu importe. Que faire si cela se produit après la mise à jour de votre application avec une nouvelle version de la bibliothèque de support? Le schéma de l'état des fragments persistants peut avoir changé entre les versions de la bibliothèque de prise en charge. Je n'ai rien pour étayer cela.


Pour aider à identifier où cela se produit, vous pouvez également enregistrer le fragment de création / démarrage / reprise / pause à l'aide des rappels de cycle de vie: developer.android.com/reference/android/support/v4/app/…


Qu'avez-vous sur: com.payments.base.BaseFragment.onCreate (BaseFragment.java:68‌) com.payments.app.fragments.TopLevelFragment.onCreate (TopLeve‌ lFragment.java:422)


TopLevelFragment, a-t-il un ViewPager avec des fragments?


J'ai une trace de pile très similaire à celle-ci: stackoverflow.com/questions/58649428/...


3 Réponses :


3
votes

Je viens d'apprendre que vous pouvez empêcher votre appareil de test d'empêcher les activités. Il y a une option dans les options pour les développeurs, section "Applications" qui, une fois activée, détruit les activités chaque fois que l'utilisateur les quitte.

Après avoir activé cette option, j'ai pu reproduire l'erreur en fermant et en rouvrant simplement mon application. Comme vous l'avez dit, c'est une erreur dans la méthode onCreate (). Dans mon cas, la raison est une exception ClassNotFoundException lors de la suppression d'un Parcelable personnalisé de mon état d'instance enregistré. Cela pourrait ne pas être une solution à votre problème, mais au moins pourrait vous aider à reproduire et à identifier l'erreur. Bonne chasse!


2 commentaires

hé @Richard R, merci. oui - j'ai utilisé l'option de développement "enregistrer aucune activité", et oui - cela m'a parfois aidé à localiser des problèmes de ce type. Ce problème provenait généralement (a) de certains objets parcellables, qui contenaient un objet imbriqué qui n'a PAS implémenté Parcelable ou (b) de vues personnalisées qui ne restauraient pas correctement les données. Le problème est toujours - cette méthode est une approche «tirée dans le noir». J'espérais un moyen de lire les données enregistrées. Je veux dire - pourquoi se donner la peine de mentionner '... tapez le code XXXX au décalage YYY' si ces nombres ne correspondent à rien d'utile pour le débogage?


Oui, vous avez raison, c'est l'option (b) dans mon cas: j'utilise des vues personnalisées et j'ai eu une erreur dans l'une des méthodes onRestoreInstanceState (). Il m'a fallu un certain temps pour trouver la classe buggy mais finalement résolu. Merci pour l'indice @Mardann



3
votes

D'après mon expérience, une erreur comme celle-ci concerne moins ce qui se passe lorsque l'erreur se produit, mais plus ce qui s'est passé plus tôt dans votre application. Plus précisément, lorsque l'application est passée en arrière-plan.

Mais en commençant par ce que la trace de pile révèle. TopLevelFragment est en cours de restauration à partir de l'état précédent. Au cours de cette restauration, les fragments enfants de TopLevelFragment sont en cours de restauration. Lors de la restauration des fragments enfants, l'état de fragment enregistré de l'une des tentatives du fragment de se décompresser lorsque getSparseParcelableArray () est appelé sur Bundle pour récupérer l'état de vue enregistré du fragment. Cela se produit dans FragmentManager.java, ligne 1329. Il y a quelque chose à propos de l'état de fragment enregistré que Parcel ne sait pas quoi faire car il se démarshaling lui-même.

Pour affiner sur quel fragment enfant de TopLevelFragment vous devriez vous concentrer, je mettrais un point d'arrêt sur la ligne 1329 de FragmentManager.java et inspecterais le type Fragment f is. Gardez à l'esprit que plusieurs fragments enfants peuvent être restaurés, vous voulez donc voir quel fragment ne peut pas dépasser la ligne 1329.

Mais bien sûr, vous avez forcé cette logique de restauration à se produire de manière cohérente. Si vous envoyez simplement votre application en arrière-plan et la ramenez au premier plan, cela ne se produira probablement pas. Vous pouvez donc faire comme le suggère Richard R et utiliser l'option développeur "Ne pas conserver les activités" pour forcer Android à détruire et restaurer les activités.

Une fois que vous avez déterminé quel fragment est le problème, vous devez revenir plus tôt dans votre application et examiner de plus près les types de données que vous mettez à l'état de sauvegarde dans Fragment.onSaveInstanceState (). Espérons que cela vous oriente dans la bonne direction.

S'il s'agit d'un problème de proguard, vous devriez voir l'erreur disparaître si vous désactivez la minification. Et si tel est le cas, vous aurez peut-être besoin d'une règle proguard ou d'une annotation @Keep sur l'un de vos types de colis personnalisés. Si l'erreur se produit avec et sans minification, elle n'est probablement pas liée à la proguard.


1 commentaires

Ce n'est certainement pas ProGuard. Cela se produit également avec la version de débogage. C'est un problème avec le bundle lorsque Android tente de restaurer un état d'instance enregistré. Enquêtera plus avant dès que possible.



-1
votes

Pour obtenir ce type de traces de pile, nous pouvons utiliser des crashlytics ou nous pouvons définir manuellement le gestionnaire d'exceptions par défaut

public class CustomExceptionHandler implements       UncaughtExceptionHandler {
private UncaughtExceptionHandler defaultUEH;
private String dirName;
private String url;

/*
* if any of the parameters is null, the respective functionality
* will not be used
*/           
public CustomExceptionHandler(String dirName, String url) {
this.dirName = dirName;
this.url = url;
this.defaultUEH = Thread.getDefaultUncaughtExceptionHandler();
}

public void uncaughtException(Thread t, Throwable e) {
final Writer result = new StringWriter();
final PrintWriter printWriter = new PrintWriter(result);
e.printStackTrace(printWriter);
String stacktrace = result.toString();
printWriter.close();           
if (dirName != null) {
writeToFile(stacktrace);
}
defaultUEH.uncaughtException(t, e);
}          
private void writeToFile(String stacktrace) {
try {
File myDir = new File(dirName.replace(" ", "") + "_Log");
if (!myDir.exists()) {
myDir.mkdir();
}
//Store only 10 file in device because of size.
if (myDir != null & myDir.isDirectory() & myDir.listFiles().length > 10) {
File[] filelist = myDir.listFiles();
for (int i = 0; i < filelist.length; i++) {
try {
filelist[i].delete();
} catch (Exception e) {}
}
}
Calendar c = Calendar.getInstance();                       c.setTimeInMillis(System.currentTimeMillis());
String fileName = "";
fileName = c.get(Calendar.SECOND) + "-" +
c.get(Calendar.MINUTE) + "-" +
c.get(Calendar.HOUR) + "-" +
(c.get(Calendar.AM) == 0 ? "AM" : "PM") + "_" +
c.get(Calendar.DAY_OF_MONTH) + "-" +
(c.get(Calendar.MONTH) + 1) + "-" +
c.get(Calendar.YEAR) + ".txt";
File f = new File(myDir, fileName);
FileWriter fr = new FileWriter(f);
BufferedWriter bos = new BufferedWriter(fr);
bos.write(stacktrace);
bos.flush();
bos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}

Pour stocker le journal dans le chemin du fichier ...

    public class BaseActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    if (!(Thread.getDefaultUncaughtExceptionHandler() instanceof CustomExceptionHandler)) {
    String path = Environment.getExternalStorageDirectory()
    + "/" + getString(R.string.app_name);
    Thread.setDefaultUncaughtExceptionHandler(new
    CustomExceptionHandler(path, ""));
    }
    }


1 commentaires

Mais ils ont déjà le stacktrace. Focus sur la question: "comment analyser ces stacktraces pour en localiser la cause? comment utiliser les quelques données fournies? "