关于NotSerializableException: com.google.gson.internal.StringMap问题总结

最近项目进行了一次target sdk的升级28版本的改造,在处理了一些target 版本的Android9.0的兼容之后,项目整体运行起来没有什么问题,但在之后作为SDK给另一个项目使用之后出现了一个比较罕见的问题-NotSerializableException: com.google.gson.internal.StringMap数据序列化问题,看似比较简单,也是困扰了很久,下面总结一下针对这个问题的跟踪查询。


错误日志

一、问题描述

    我在进行功能验证的过程中发现了一个比较奇怪的问题,当我在页面A的时候数据没有任何问题,也可以正常显示,但是不管我从页面A(Fragment)跳转到任何页面B、C、D(Activity,Fragment都行)都会崩溃,控制台输出问题截图上面的的异常信息。然而我从其他页面E(Fragment)跳转到B、C、D的时候都没有任何问题。

    根据控制台的异常日志输出信息可以看出这就是一个简单的序列化问题,刚开始我是这么认为的,日志信息明确说明了是PageBean的写入数列化问题,那我们就找PageBean然后实现序列化就可以了。当我找到PageBean的时候就懵了 public class PageBeanimplements Serializable {},明明都已经实现了Serializable 接口了,为什么还是报错了,是不是子类没有实现Serializable 接口呢!然后我仔细检查了PageBean的每个子类以及子类的子类,全部都实现了Serializable 接口,问题开始变得复杂了。回头又看错误日志,发现还有一个信息,就是StringMap这个类,但是我搜索了一个引用的gson库并没有找个这个文件。

二、问题定位 

我把问题同步给了项目leader,经过leader跟同事的连夜查找,总算是大概定位到了问题所在,而且也找到了StringMap这个类。那天我早早的可耻的溜了,事后也是感到非常的惭愧。原来StringMap是在早些的gson库里所存在帮助json数据解析的类,而我们的项目的gson比较新,所以一直找不到这个类,我们的另一个项目是早些的gson库,所以打包后到另一个项目才会出现此问题。出问题的地方大概在下面所存在的写入序列化对象的代码中

public static TemplateContainerFragment newInstance(ChannelNavBean channelNavBean, PageBean firstPageBean) {

    TemplateContainerFragment vesselFragment = new TemplateContainerFragment();

    Bundle bundle = new Bundle();

    bundle.putSerializable(AppParams.INTENT_PARAM_CHANNEL_NAV_BEAN, channelNavBean);

    if (firstPageBean != null) {

        bundle.putSerializable(AppParams.INTENT_PARAM_CHANNEL_PAGE_BEAN, firstPageBean);

    }

    vesselFragment.setArguments(bundle);

    return vesselFragment;

}

大概定到问题以后,leader为了锻炼我解决问题的能力,也是抛给我2个问题,希望我能多提高自己解决问题的能力

    1.序列化是在什么时候出问题的?出问题的序列化对象在什么位置上?SrtingMap对象在当中扮演的角色是什么?

    2.为什么页面初始化的时候没有问题,反而在进入下一级页面的时候出现崩溃?

三、问题分析

    根据日志信息和已找到的代码可以大概确定PageBean是在序列化的时候出现了问题,我找到上面的相关代码进行debug验证,寻找PageBean中未实现序列化的StringMap对象,还真的有所发现

产生StringMap的PageBean

    为什么PageBean里面会有StringMap对象?带着这个疑问我跟踪查找了一个StringMap的产生,终于在gson库里面的ObjectTypeAdapter对象中找到了StringMap的产生,原来是在数据解析的时候如果有JSONArray有未知的List<Object> list,Object会被转化成为一个StringMap对象存储,而StringMap是没有序列化的对象,所以在传递数据的过程中会出现异常。那么第一个问题就找到了答案

public Object read(JsonReader in) throws IOException {

    JsonToken token = in.peek();

    switch(token) {

        case BEGIN_ARRAY:

                List<Object> list = new ArrayList();

                in.beginArray();

        while(in.hasNext()) {

            list.add(this.read(in));

        }

        in.endArray();

        return list;

        case BEGIN_OBJECT:

                Map<String, Object> map = new StringMap();

                in.beginObject();

        while(in.hasNext()) {

            map.put(in.nextName(), this.read(in));

        }

        in.endObject();

        return map;

        case STRING:

                return in.nextString();

        case NUMBER:

                return in.nextDouble();

        case BOOLEAN:

                return in.nextBoolean();

        case NULL:

                in.nextNull();

                return null;

        default:

                throw new IllegalStateException();

    }

}

    剩下的问题就是这个方法明明是在页面初始化的时候调用的,为什么在页面初始化的时候没有问题,返回再页面进入下一级页面的时候回出现崩溃?其实发现进入下一个页面的时候出现问题,也就发现了思路,进入下一级页面的时候上一个页面要保存数据,对,就是保存数据的时候可能会出现问题,然后我就跟踪onSaveInstanceState(@NonNull Bundle outState)的方法,页面离开确实调用了该方法,但是outState参数是空的,没有传递任何数据,为什么会出问题呢,经过一系列的跟踪,终于在FragmentState中发现了问题,原来在fragment页面

@Override

public void writeToParcel(Parcel dest, int flags) {

    dest.writeString(mClassName);

    dest.writeInt(mIndex);

    dest.writeInt(mFromLayout ? 1 : 0);

    dest.writeInt(mFragmentId);

    dest.writeInt(mContainerId);

    dest.writeString(mTag);

    dest.writeInt(mRetainInstance ? 1 : 0);

    dest.writeInt(mDetached ? 1 : 0);

    dest.writeBundle(mArguments);

    dest.writeInt(mHidden ? 1 : 0);

    dest.writeBundle(mSavedFragmentState);

}

Parcel类的writeBundle
bundle类的writeToParcel
writeToParcelInner
writeArrayMapInternal

        for循环写入数据

writeValue

    写入数据,原来这里要求写入的数据对象以及子对象都必须是序列化的数据,否则就会出现异常。为什么页面初始化进入写入的数据没有问题,反而在页面离开保存数据的时候写入的数据会出现异常?带着这个疑问我又看了一下bundle.putSerializable()这个方法。

Bundle的putSerializable方法
putSerializable

    看到代码之后我释然了,原来putSerializable这个方法值值要求传入的对象被序列化就可以,并不要求子对象必须都实现数列化接口。所以才导致页面初始化的时候并没有问题,反而在页面离开保存数据的时候写入数据异常。

四、问题跟踪解决

由于我们的数据PageBean的解析里面JSONArray出现的问题,所以就尝试在不改变gson库版本号的同时解决这个问题,于是根据返回的数据结构尝试把JSONAarray替换成List<JSONObject>形式,于是又做了一番兼容的尝试工作,但是由于我们的数据结构问题,并没有成功,最后还是报出了相同的错误,经过查看还JSONObject中还是出现了没有序列化的StringMap。

仍然有问题的PageBean

最后只能沟通把另一个项目的gson库进行升级解决这个问题。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,133评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,682评论 3 390
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,784评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,508评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,603评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,607评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,604评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,359评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,805评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,121评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,280评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,959评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,588评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,206评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,442评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,193评论 2 367
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,144评论 2 352

推荐阅读更多精彩内容

  • 泛型: https://juejin.im/post/5b614848e51d45355d51f792#headi...
    RexHuang阅读 6,844评论 0 0
  • 概况 Gson是一个Java库,它可以用来把Java对象转换为JSON表达式,也可以反过来把JSON字符串转换成与...
    木豚阅读 6,788评论 0 2
  • 1.概述2.Gson的目标3.Gson的性能和扩展性4.Gson的使用者5.如何使用Gson 通过Maven来使用...
    人失格阅读 14,228评论 2 18
  • ORA-00001: 违反唯一约束条件 (.) 错误说明:当在唯一索引所对应的列上键入重复值时,会触发此异常。 O...
    我想起个好名字阅读 5,296评论 0 9
  • 为了更好的学习Gson,特将Gson User Guide翻译如下。由于本人英文水平有限,如有错误,还请指正,谢谢...
    WeberLisper阅读 6,798评论 0 6