【问题标题】:How to find source of crashes of type java.lang.RuntimeException: Parcel android.os.Parcel@####: Unmarshalling unknown type code XXXX at offset YYY如何查找 java.lang.RuntimeException 类型的崩溃源:Parcel android.os.Parcel@####: Unmarshalling unknown type code XXXX at offset YYY
【发布时间】:2019-04-22 15:26:57
【问题描述】:

我们的崩溃报告系统正在记录这种类型的崩溃:

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 时会发生这种情况。 我们的应用程序是一个单活动应用程序,所有 UI 都由片段管理。 这种崩溃很少发生,并且很难在我们的开发环境中重现。

另外 - 我不认为原因是我们创建了一些可打包的对象,而是 Android 组件重新加载,但不确定。

我想知道的:如何分析这些堆栈跟踪以确定原因?如何利用给定的小数据?

请注意,堆栈跟踪几乎不指向我们应用程序中的任何特定行,以及它在哪里 - 仅指向我们的活动和片段基类的 onCreate 方法

【问题讨论】:

  • 我开始为这个问题悬赏,因为我的应用程序有完全相同的错误,而谷歌开发者控制台没有提供关于如何修复(甚至跟踪)它的提示。这仅在生产中发生吗?我怀疑 ProGuard 配置错误...
  • 因此,在配置更改/应用程序恢复/其他任何情况后,碎片未打包时会发生这种情况。如果在您的应用使用新版本的支持库更新后发生这种情况怎么办?持久片段状态的模式可能在支持库版本之间发生了变化。我没有任何支持。
  • 为了帮助查明发生这种情况的位置,您可能还需要使用生命周期回调记录创建/启动/恢复/暂停片段:developer.android.com/reference/android/support/v4/app/…
  • 你有什么:com.payments.base.BaseFragment.onCreate(BaseFragment.java:68) com.payments.app.fragments.TopLevelFragment.onCreate(TopLevelFragment.java:422)跨度>

标签: android parcelable


【解决方案1】:

我刚刚了解到您可以让您的测试设备保持活动状态。开发者选项中的“应用程序”部分中有一个选项,当打开时,每次用户离开活动时都会销毁活动。

打开此选项后,我只需关闭并重新打开我的应用程序即可重现该错误。正如您所说,这是 onCreate() 方法中的错误。就我而言,原因是从我保存的实例状态中解组自定义 Parcelable 时出现 ClassNotFoundException。这可能无法解决您的问题,但至少可以帮助您重现并确定错误。狩猎愉快!

【讨论】:

  • 嘿@Richard R,谢谢。是的——我使用了“不保存活动”开发选项,是的——这有时帮助我找到了这种类型的问题。此问题通常源于 (a) 一些可打包对象,其中包含未实现 Parcelable 的嵌套对象或 (b) 未正确恢复数据的自定义视图。问题仍然是 - 这种方法是一种“在黑暗中射击”的方法。我希望有某种方法来读取记录的数据。我的意思是 - 如果这些数字没有映射到任何对调试有用的东西,为什么还要提及“......在偏移 YYY 处键入代码 XXXX”?
  • 是的,你是对的,在我的例子中是选项 (b):我使用自定义视图并且在 onRestoreInstanceState() 方法之一中出现错误。我花了一段时间才找到错误的课程,但最终解决了。感谢您的提示@Mardann
【解决方案2】:

根据我的经验,这样的错误与其说是错误发生时发生的事情,不如说是关于您的应用之前发生的事情。特别是当应用程序进入后台时。

但是从堆栈跟踪所揭示的内容开始。 TopLevelFragment 正在从以前的状态恢复。在此恢复期间,TopLevelFragment 的子片段正在恢复。在恢复子片段时,当在 Bundle 上调用 getSparseParcelableArray() 以检索片段的保存视图状态时,片段尝试解包自身的保存片段状态。这发生在 FragmentManager.java 的第 1329 行。Parcel 不知道如何处理已保存的片段状态,因为它正在解组自身。

为了缩小您应该关注的 TopLevelFragment 的哪个子片段,我将在 FragmentManager.java 的第 1329 行放置一个断点并检查 Fragment f 的类型。请记住,您可能有多个子片段正在恢复,因此您想查看哪个片段无法通过第 1329 行。

当然,您必须强制此恢复逻辑始终如一地发生。如果您只是将您的应用程序发送到后台并将其带回前台,这可能不会发生。因此,您可以按照 Richard R 的建议进行操作,并使用“不保留活动”开发人员选项来强制 Android 销毁和恢复活动。

一旦缩小了问题所在片段的范围,您需要在应用程序中更早地返回并仔细查看您在 Fragment.onSaveInstanceState() 中放入保存状态的数据类型。希望这会为您指明正确的方向。

如果这是一个 proguard 问题,如果禁用缩小,您应该会看到错误消失。如果是这种情况,您可能需要在您的自定义 Parcelable 类型之一上使用 proguard 规则或 @Keep 注释。如果在缩小和不缩小的情况下都发生错误,则可能与 proguard 无关。

【讨论】:

  • 它肯定不是 ProGuard。调试构建也会发生。当 Android 尝试恢复已保存的实例状态时,Bundle 会出现问题。将尽快进一步调查。
【解决方案3】:

获取这种类型的堆栈跟踪,我们可以使用 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();
}
}
}

【讨论】:

  • 但他们已经有了堆栈跟踪。专注于这个问题:“如何分析这些堆栈跟踪以确定原因?如何利用给定的小数据?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-03-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-26
  • 2022-08-22
  • 1970-01-01
相关资源
最近更新 更多