2012-09-06 43 views
26

我正在觀察將行爲數據傳遞給intent extra的行爲,這很奇怪,我只是想澄清一下我是不是錯過了。LinkedList放入Intent extra在下次活動中獲取時重新獲取ArrayList

所以我試圖做的事情是,在ActivtyA我把LinkedList實例放入我開始下一個活動產生的intent - ActivityB

LinkedList<Item> items = (some operation); 
Intent intent = new Intent(this, ActivityB.class); 
intent.putExtra(AppConstants.KEY_ITEMS, items); 

ActivityBonCreate,我試圖找回LinkedList額外如下 -

LinkedList<Item> items = (LinkedList<Item>) getIntent() 
          .getSerializableExtra(AppConstants.KEY_ITEMS); 

在運行此,我反覆在ActivityB得到了ClassCastException,在上面就行了。基本上,例外說我收到一個ArrayList。一旦我將上面的代碼更改爲接收ArrayList,則一切正常。

現在我不能從現有的文檔中弄清楚,在傳遞可串行化的List實現時,這是否是Android上的預期行爲。或者,也許,我在做什麼有一些根本性的錯誤。

謝謝。

+0

改爲使用Parcelable。 –

+0

,但是有什麼特別的原因讓'LinkedList'發生這種行爲,而如果我在'intent'上添加了'ArrayList'實例作爲額外的數據,那麼一切都會好的。而且,我不需要使用Parcelable? – anirvan

+0

對於有趣的問題+1。我花了一些時間思考這個問題,並且非常感興趣,我去了解自己。現在你和我都更聰明瞭(看我的答案)。 –

回答

49

我可以告訴你爲什麼會這樣,但你不會喜歡它;-)

首先一點背景資料:

附加在Intent基本上是一個Android Bundle這基本上是鍵/值對的HashMap。所以,當你做這樣的事情

intent.putExtra(AppConstants.KEY_ITEMS, items); 

的Android創造了額外新Bundle並添加映射條目到Bundle其中關鍵是AppConstants.KEY_ITEMS和值項目(這是您的LinkedList的對象)。

這一切都很好,如果您在執行代碼後查看附加組件,您會發現它包含一個LinkedList。現在有趣的部分...

當你打電話startActivity()與extras包含的意圖,Android需要轉換額外從鍵/值對映射到字節流。基本上它需要將Bundle序列化。它需要這樣做,因爲它可能會在另一個進程中啓動該活動,並且爲此需要對Bundle中的對象進行序列化/反序列化,以便它可以在新進程中重新創建它們。它也需要這樣做,因爲Android將Intent的內容保存在某些系統表中,以便在稍後需要時可以重新生成Intent。

爲了將Bundle序列化成字節流,它通過包中的映射並獲取每個鍵/值對。然後它將每個「值」(這是某種對象)並試圖確定它是什麼類型的對象,以便它可以以最有效的方式對其進行序列化。爲此,它會根據已知對象類型的列表檢查對象類型。「已知對象類型」的列表包含諸如Integer,LongStringMap, Bundle以及遺憾的還有List。因此,如果對象是List(其中有許多不同的種類,包括LinkedList),則它將其序列化並將其標記爲List類型的對象。

Bundle進行反序列化,即:當你這樣做:

LinkedList<Item> items = (LinkedList<Item>) 
     getIntent().getSerializableExtra(AppConstants.KEY_ITEMS); 

它產生於List類型的Bundle所有對象的ArrayList

實際上,您可以做任何事情來改變Android的這種行爲。至少現在你知道它爲什麼這樣做了。

爲了讓您知道:我實際上編寫了一個小測試程序來驗證此行爲,並且我查看了Parcel.writeValue(Object v)的源代碼,該代碼是在將映射轉換爲字節流時從Bundle調用的方法。

重要提示:由於List是一個接口,這意味着實現你放入BundleList任何類會出來作爲ArrayList。 這也是有趣的是Map也是「已知對象類型」列表中,這意味着,無論你把什麼樣的Map對象爲Bundle(例如TreeMapSortedMap,或者說實現了Map接口的任何類)一,你總是會得到一個HashMap

+3

我的,哦,我的 - 我可以先說*是的,我真的不喜歡這個*。畢竟,「*已知對象類型*」的整個概念聽起來像是某些人在構建邏輯部分時想要削減角點。而且,如果是這種情況,他們實際上應該已經做到了阻止所有「* not-known-known」對象類型實現「Serializable」,或者在意圖內部不支持「Serializable」 extras'。無論如何,我不能夠感謝你,是的,我們對你所找到的東西更聰明。 – anirvan

+3

非常感謝您的回答。爲我節省了數小時的調試時間。 – Andree

+2

謝謝大衛,這救了我......我有與savedInstanceState傳遞給我的片段相同的問題 - 我不明白爲什麼Android堅持給我一個ArrayList ... – Patrick

0

在診斷問題方面,@David Wasser的回答是正確的。這篇文章是爲了分享我如何處理它。

與任何List對象出來作爲ArrayList的問題是不可怕的,因爲你總是可以做這樣的事情

LinkedList<String> items = new LinkedList<>(
    (List<String>) intent.getSerializableExtra(KEY)); 

將反序列化的列表中的所有元素添加到新LinkedList

當涉及到Map時,問題更嚴重,因爲您可能試圖序列化LinkedHashMap,並且現在已經丟失了元素排序。

幸運的是,這裏有一個(相對)無痛的方式:定義您自己的可序列化包裝類。你可以做到這一點的具體類型或做一般:

public class <T extends Serializable> Wrapper implements Serializable { 
    private T wrapped; 

    public Wrapper(T wrapped) { 
     this.wrapped = wrapped; 
    } 

    public T get() { 
     return wrapped; 
    } 
} 

然後你可以用它來隱藏自己的List,從Android的類型檢查Map,或其他數據類型:

intent.putExtra(KEY, new Wrapper<>(items)); 

及更高版本:

items = ((Wrapper<LinkedList<String>>) intent.getSerializableExtra(KEY)).get();