1. 程式人生 > >android 5.0以下系統Intent傳遞序列化物件的bug

android 5.0以下系統Intent傳遞序列化物件的bug

    專案中使用外掛框架,當外掛在Intent中傳遞Serializable物件時,在android 5.0以下系統上會出現

E/InstrumentationHacker(25176): Parcelable encounteredClassNotFoundException reading a Serializable object (name = com.qihoo.browser.imageplugin.bean.HomeListBean)

E/InstrumentationHacker(25176): java.lang.RuntimeException: Parcelable encounteredClassNotFoundException reading a Serializable object (name = com.qihoo.browser.imageplugin.bean.HomeListBean)

E/InstrumentationHacker(25176): 	at android.os.Parcel.readSerializable(Parcel.java:2251)

E/InstrumentationHacker(25176): 	at android.os.Parcel.readValue(Parcel.java:2096)

E/InstrumentationHacker(25176): 	at android.os.Parcel.readListInternal(Parcel.java:2375)

E/InstrumentationHacker(25176): 	at android.os.Parcel.readArrayList(Parcel.java:1735)

E/InstrumentationHacker(25176): 	at android.os.Parcel.readValue(Parcel.java:2066)

E/InstrumentationHacker(25176): 	at android.os.Parcel.readArrayMapInternal(Parcel.java:2346)

E/InstrumentationHacker(25176): 	at android.os.Bundle.unparcel(Bundle.java:249)

E/InstrumentationHacker(25176): 	at android.os.Bundle.keySet(Bundle.java:345)

E/InstrumentationHacker(25176): 	at com.qihoo.plugin.core.PluginManager.unwrapIntent(PluginManager.java:1753)

原因是序列化物件的類是存放在外掛的dex中的,因此反序列化要使用外掛的ClassLoader才行,該錯誤是Intent在進行反序列化時,使用的宿主預設的ClassLoader導致出現找不到類的異常。

而實際上在使用該Intent前,已經將外掛ClassLoader設定到Intent中了,理論上來說,不應該有問題。

activity.getIntent().setExtrasClassLoader(plugin.getCl());

這裡使用android 4.4.2_r2 原始碼分析:

跟蹤異常堆疊和系統原始碼,找到

/frameworks/base/core/java/android/os/Parcel.java

跟蹤readValue()方法,定位到讀取序列化物件的程式碼


可以發現,readSerializable()沒有將loader傳入進去,loader是通過setExtrasClassLoader()設定的外掛ClassLoader,而readSerializable()中,直接使用預設的ClassLoader來進行反序列化,程式碼如下:

2200    public final Serializable readSerializable() {
2201        String name = readString();
2202        if (name == null) {
2203
// For some reason we were unable to read the name of the Serializable (either there 2204 // is nothing left in the Parcel to read, or the next value wasn't a String), so 2205 // return null, which indicates that the name wasn't found in the parcel. 2206 return null; 2207 } 2208 2209 byte[] serializedData = createByteArray(); 2210 ByteArrayInputStream bais = new ByteArrayInputStream(serializedData); 2211 try { 2212 ObjectInputStream ois = new ObjectInputStream(bais); 2213 return (Serializable) ois.readObject(); 2214 } catch (IOException ioe) { 2215 throw new RuntimeException("Parcelable encountered " + 2216 "IOException reading a Serializable object (name = " + name + 2217 ")", ioe); 2218 } catch (ClassNotFoundException cnfe) { 2219 throw new RuntimeException("Parcelable encountered" + 2220 "ClassNotFoundException reading a Serializable object (name = " 2221 + name + ")", cnfe); 2222 } 2223 }

也就是說,我們通過setExtrasClassLoader()方法設定的ClassLoader對Serializable物件來說,是沒有作用的。

同樣的,從

可以看出,

setExtrasClassLoader()對Parcelable物件是有作用的。

從android 5.0.0_r2原始碼同樣的位置:

/frameworks/base/core/java/android/os/Parcel.java

可以發現,已經對這個問題進行了處理。

結論:

android 5.0系統以下,Intent存在一個序列化bug,通過setExtrasClassLoader()方法設定的ClassLoader物件,對Serializable物件無效,當Intent進行反序列化時,使用的依舊為預設的ClassLoader物件,如果反序列化的類在預設的ClassLoader中不存在,則會出現找不到類的異常。5.0以上系統已經修復該bug。