最近項目進(jìn)行了一次target sdk的升級28版本的改造懂鸵,在處理了一些target 版本的Android9.0的兼容之后骚腥,項目整體運行起來沒有什么問題,但在之后作為SDK給另一個項目使用之后出現(xiàn)了一個比較罕見的問題-NotSerializableException: com.google.gson.internal.StringMap數(shù)據(jù)序列化問題哈恰,看似比較簡單,也是困擾了很久烟馅,下面總結(jié)一下針對這個問題的跟蹤查詢阻肿。
一傅是、問題描述
? ? 我在進(jìn)行功能驗證的過程中發(fā)現(xiàn)了一個比較奇怪的問題,當(dāng)我在頁面A的時候數(shù)據(jù)沒有任何問題构订,也可以正常顯示侮叮,但是不管我從頁面A(Fragment)跳轉(zhuǎn)到任何頁面B、C悼瘾、D(Activity囊榜,F(xiàn)ragment都行)都會崩潰审胸,控制臺輸出問題截圖上面的的異常信息。然而我從其他頁面E(Fragment)跳轉(zhuǎn)到B卸勺、C砂沛、D的時候都沒有任何問題。
????根據(jù)控制臺的異常日志輸出信息可以看出這就是一個簡單的序列化問題曙求,剛開始我是這么認(rèn)為的碍庵,日志信息明確說明了是PageBean的寫入數(shù)列化問題,那我們就找PageBean然后實現(xiàn)序列化就可以了悟狱。當(dāng)我找到PageBean的時候就懵了?public class PageBeanimplements Serializable {}静浴,明明都已經(jīng)實現(xiàn)了Serializable 接口了,為什么還是報錯了挤渐,是不是子類沒有實現(xiàn)Serializable 接口呢苹享!然后我仔細(xì)檢查了PageBean的每個子類以及子類的子類,全部都實現(xiàn)了Serializable 接口浴麻,問題開始變得復(fù)雜了得问。回頭又看錯誤日志软免,發(fā)現(xiàn)還有一個信息宫纬,就是StringMap這個類,但是我搜索了一個引用的gson庫并沒有找個這個文件或杠。
二哪怔、問題定位?
我把問題同步給了項目leader宣蔚,經(jīng)過leader跟同事的連夜查找向抢,總算是大概定位到了問題所在,而且也找到了StringMap這個類胚委。那天我早早的可恥的溜了挟鸠,事后也是感到非常的慚愧。原來StringMap是在早些的gson庫里所存在幫助json數(shù)據(jù)解析的類亩冬,而我們的項目的gson比較新艘希,所以一直找不到這個類,我們的另一個項目是早些的gson庫硅急,所以打包后到另一個項目才會出現(xiàn)此問題覆享。出問題的地方大概在下面所存在的寫入序列化對象的代碼中
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對象在當(dāng)中扮演的角色是什么?
????2.為什么頁面初始化的時候沒有問題,反而在進(jìn)入下一級頁面的時候出現(xiàn)崩潰?
三、問題分析
? ? 根據(jù)日志信息和已找到的代碼可以大概確定PageBean是在序列化的時候出現(xiàn)了問題,我找到上面的相關(guān)代碼進(jìn)行debug驗證,尋找PageBean中未實現(xiàn)序列化的StringMap對象,還真的有所發(fā)現(xiàn)
? ? 為什么PageBean里面會有StringMap對象荚板?帶著這個疑問我跟蹤查找了一個StringMap的產(chǎn)生凤壁,終于在gson庫里面的ObjectTypeAdapter對象中找到了StringMap的產(chǎn)生吩屹,原來是在數(shù)據(jù)解析的時候如果有JSONArray有未知的List<Object> list,Object會被轉(zhuǎn)化成為一個StringMap對象存儲拧抖,而StringMap是沒有序列化的對象煤搜,所以在傳遞數(shù)據(jù)的過程中會出現(xiàn)異常。那么第一個問題就找到了答案
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();
????}
}
? ? 剩下的問題就是這個方法明明是在頁面初始化的時候調(diào)用的唧席,為什么在頁面初始化的時候沒有問題擦盾,返回再頁面進(jìn)入下一級頁面的時候回出現(xiàn)崩潰?其實發(fā)現(xiàn)進(jìn)入下一個頁面的時候出現(xiàn)問題淌哟,也就發(fā)現(xiàn)了思路厌衙,進(jìn)入下一級頁面的時候上一個頁面要保存數(shù)據(jù),對绞绒,就是保存數(shù)據(jù)的時候可能會出現(xiàn)問題婶希,然后我就跟蹤onSaveInstanceState(@NonNull Bundle outState)的方法,頁面離開確實調(diào)用了該方法蓬衡,但是outState參數(shù)是空的喻杈,沒有傳遞任何數(shù)據(jù),為什么會出問題呢狰晚,經(jīng)過一系列的跟蹤筒饰,終于在FragmentState中發(fā)現(xiàn)了問題,原來在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);
}
? ? ? ? for循環(huán)寫入數(shù)據(jù)
? ? 寫入數(shù)據(jù),原來這里要求寫入的數(shù)據(jù)對象以及子對象都必須是序列化的數(shù)據(jù)壁晒,否則就會出現(xiàn)異常瓷们。為什么頁面初始化進(jìn)入寫入的數(shù)據(jù)沒有問題,反而在頁面離開保存數(shù)據(jù)的時候?qū)懭氲臄?shù)據(jù)會出現(xiàn)異常秒咐?帶著這個疑問我又看了一下bundle.putSerializable()這個方法谬晕。
? ? 看到代碼之后我釋然了,原來putSerializable這個方法值值要求傳入的對象被序列化就可以,并不要求子對象必須都實現(xiàn)數(shù)列化接口携取。所以才導(dǎo)致頁面初始化的時候并沒有問題攒钳,反而在頁面離開保存數(shù)據(jù)的時候?qū)懭霐?shù)據(jù)異常。
四雷滋、問題跟蹤解決
由于我們的數(shù)據(jù)PageBean的解析里面JSONArray出現(xiàn)的問題不撑,所以就嘗試在不改變gson庫版本號的同時解決這個問題,于是根據(jù)返回的數(shù)據(jù)結(jié)構(gòu)嘗試把JSONAarray替換成List<JSONObject>形式,于是又做了一番兼容的嘗試工作晤斩,但是由于我們的數(shù)據(jù)結(jié)構(gòu)問題,并沒有成功焕檬,最后還是報出了相同的錯誤,經(jīng)過查看還JSONObject中還是出現(xiàn)了沒有序列化的StringMap。
最后只能溝通把另一個項目的gson庫進(jìn)行升級解決這個問題澳泵。