如何查找类型为java.lang.RuntimeException的崩溃源:包裹android.os.Parcel@####:在偏移YYY处解组未知类型代码XXXX

时间:2019-03-14 09:56:11

标签: android parcelable

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

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组件,但不确定。

我想知道的是:如何分析这些堆栈跟踪以查明原因?如何利用给出的少量数据?

请注意,stacktrace几乎不会指向应用程序中的任何特定行,并且指向该行-仅指向我们活动的onCreate方法并片段化基类

3 个答案:

答案 0 :(得分:3)

我刚刚了解到您可以使测试设备保持活动状态。开发人员选项“应用”部分中有一个选项,该选项在打开时会在用户每次离开活动时销毁活动。

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

答案 1 :(得分: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无关。

答案 2 :(得分:-1)

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