Как найти источник сбоев типа java.lang.RuntimeException: Parcel android.os.Parcel@####: Unmarshalling unknown type code XXXX at offset YYY

Наша система отчетов о сбоях регистрирует сбои такого типа:

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)

Что я знаю: это происходит, когда приложение восстанавливается из фона и вызывается onCreate. Наше приложение представляет собой приложение с одним действием, в котором весь пользовательский интерфейс управляется фрагментами. Этот сбой — нечастый, и его очень сложно воспроизвести в нашей среде разработки.

Кроме того, я не верю, что причиной является какой-то объект, который мы создали, а скорее перезагрузка компонента Android, но не уверен.

Что я хотел бы знать: как проанализировать эти трассировки стека, чтобы точно определить причину? как использовать предоставленные небольшие данные?

Обратите внимание, что трассировка стека вряд ли указывает на какую-то конкретную строку в нашем приложении, а там, где она указывает, — только на методы onCreate нашей активности и базовые классы фрагментов.

Я начинаю вознаграждение за эти вопросы, так как в моем приложении точно такая же ошибка, и консоль разработчика Google не предлагает подсказок о том, как ее исправить (или даже отследить). Это происходит только на производстве? Я подозреваю неправильную конфигурацию ProGuard...

Richard R 20.04.2019 13:54
issuetracker.google.com/issues/37083630
hushed_voice 22.04.2019 09:37

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

Eugen Pechanec 22.04.2019 09:37

Чтобы помочь определить, где это происходит, вы можете дополнительно зарегистрировать создание/запуск/возобновление/приостановку фрагмента с помощью обратных вызовов жизненного цикла: developer.android.com/reference/android/support/v4/app/…

Eugen Pechanec 22.04.2019 09:41

Что у вас есть по адресу: com.payments.base.BaseFragment.onCreate(BaseFragment.java:68‌​) com.payments.app.fragments.TopLevelFragment.onCreate(TopLeve‌​lFragment.java:422)

Carnal 25.04.2019 16:41

TopLevelFragment, у него есть ViewPager с фрагментами?

Carnal 25.04.2019 16:42

У меня очень похожая трассировка стека: stackoverflow.com/questions/58649428/…

VIN 31.10.2019 21:51
7
7
881
3

Ответы 3

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

После включения этой опции я смог воспроизвести ошибку, просто закрыв и снова открыв приложение. Как вы сказали, это ошибка в методе onCreate(). В моем случае причиной является ClassNotFoundException при удалении пользовательского Parcelable из моего сохраненного состояния экземпляра. Это может не быть решением вашей проблемы, но, по крайней мере, может помочь вам воспроизвести и определить ошибку. Хорошей охоты!

эй @Richard R, спасибо. да — я использовал опцию разработчика «сохранять без действий», и да — иногда это помогало мне обнаруживать проблемы такого типа. Эта проблема обычно возникает из-за (а) некоторых объектов, которые могут быть разделены, которые содержат вложенный объект, который НЕ реализует Parcelable, или (б) пользовательских представлений, которые не восстанавливают данные должным образом. Проблема все еще в том, что этот метод является подходом «выстрел в темноте». Я надеялся каким-то образом прочитать зарегистрированные данные. Я имею в виду - зачем упоминать «... введите код XXXX со смещением YYY», если эти числа не соответствуют чему-либо полезному для отладки?

Mardann 22.04.2019 15:45

Да, вы правы, это вариант (b) в моем случае: я использую пользовательские представления и получил ошибку в одном из методов onRestoreInstanceState(). Мне потребовалось некоторое время, чтобы найти класс ошибок, но, наконец, решил. Спасибо за подсказку @Mardann

Richard R 27.04.2019 13:47

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

Но начнем с того, что показывает трассировка стека. TopLevelFragment восстанавливается из предыдущего состояния. Во время этого восстановления восстанавливаются дочерние фрагменты TopLevelFragment. При восстановлении дочерних фрагментов сохраненное состояние фрагмента одного из фрагментов пытается распаковать себя при вызове getSparseParcelableArray() в Bundle для получения сохраненного состояния представления фрагмента. Это происходит в FragmentManager.java, строка 1329. В сохраненном состоянии фрагмента есть что-то, с чем Parcel не знает, что делать, поскольку он сам распаковывает.

Чтобы определить, на каком дочернем фрагменте TopLevelFragment следует сосредоточиться, я бы поставил точку останова в строке 1329 FragmentManager.java и проверил тип Fragment f. Имейте в виду, что у вас может быть несколько восстановленных дочерних фрагментов, поэтому вы хотите увидеть, какой фрагмент не может пройти дальше строки 1329.

Но, конечно, вы можете заставить эту логику восстановления выполняться последовательно. Если вы просто отправите свое приложение в фоновый режим и вернете его на передний план, этого, вероятно, не произойдет. Таким образом, вы можете сделать, как предлагает Ричард Р., и использовать опцию разработчика «Не сохранять действия», чтобы заставить Android уничтожать и восстанавливать действия.

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

Если это проблема proguard, вы должны увидеть, что ошибка исчезнет, ​​если вы отключите минимизацию. И если это так, вам может понадобиться правило proguard или аннотация @Keep для одного из ваших пользовательских типов Parcelable. Если ошибка возникает с минификацией и без нее, вероятно, она не связана с proguard.

Это точно не ProGuard. Бывает и с отладочной сборкой. Это проблема с Bundle, когда Android пытается восстановить сохраненное состояние экземпляра. Будет расследовать дальше как можно скорее.

Richard R 22.04.2019 08:39

Получая этот тип трассировки стека, мы можем использовать crashlytics или вручную установить обработчик исключений по умолчанию.

    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, ""));
    }
    }

Для хранения журнала в пути к файлу...

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

Но у них уже есть трассировка стека. Сосредоточьтесь на вопросе: "как проанализировать эти трассировки стека, чтобы точно определить причину? как использовать предоставленные небольшие данные?"

Eugen Pechanec 22.04.2019 09:31

Другие вопросы по теме