2012-09-06 6 views
26

次のアクティビティに意図余分は非常に奇妙であると私は、シリアライズデータを渡すWRTを観察してるの行動を取得する際にArrayListに改作し、私はちょうど私が上の欠落していないよ何かがあるのか​​どうかを明確にしたかっます意向余分に入れ。 ActivityB - LinkedListのは

は、だから私が何をしようとしていた事は ActivtyAに私は次のアクティビティを開始するために作成さ intentLinkedListインスタンスを置くことです。 ActivityBonCreate

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

、私は次のようにLinkedList余分を取得しようとした -

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

これを実行するには、私は繰り返し上記の行で、ActivityBClassCastExceptionを得ました。基本的に例外は私がArrayListを受け取っていると言った。前述のコードを変更してArrayListを受け取ると、すべて正常に機能しました。

今、私はちょうど、シリアライズリストの実装を渡すときに、これはAndroid上で予想される動作であるかどうか、既存のドキュメントから把握することはできません。あるいは、私がやっていることに根本的に間違っていることがあるかもしれません。

ありがとうございました。

+0

代わりにParcelableを使用してください。 –

+0

しかし、 'LinkedList'ではこの動作が発生する特別な理由がありますが、' intent'の余分なデータとして 'ArrayList'インスタンスを追加したのであれば、すべてうまくいくはずです。そして、私は 'Parcelable'を使う必要はありませんか?興味深い質問については – anirvan

+0

+1。私はこれについて考えているうちに時間を費やし、とても興味をそそられて、自分でそれを理解しようとしました。あなたと私はどちらもよりスマートです(私の答えを見てください)。 –

答えて

49

私はなぜこれが起こっているあなたを伝えることができますが、あなたが好きつもりはありません、それ;-)

まず背景情報のビット:Intent

エクストラは基本的にAndroidのですBundleは基本的にはキー/値のペアのHashMapです。あなたは

intent.putExtra(AppConstants.KEY_ITEMS, items); 

ような何かを行うときにAndroidのは、エキストラのための新しいBundleを作成し、キーがAppConstants.KEY_ITEMSで、値が(あなたのLinkedListオブジェクトである)項目あるBundleへのマップエントリを追加します。

これはすべて問題なく、コード実行後に追加バンドルを見ると、LinkedListが含まれていることがわかります。今興味深い部分が来る...

エクストラを含むインテントでを呼び出すと、Androidはキー/値のペアのマップからエフェクトをバイトストリームに変換する必要があります。基本的には、をバンドルをシリアル化する必要があります。それはそれはそれは新しいプロセスでそれらを再作成できるように、バンドル内のオブジェクトをデシリアライズ/シリアライズする必要があることを別のプロセスにして行うために活動を開始する可能性があるため、それはそれを行う必要があります。 Androidはインテントの内容をいくつかのシステムテーブルに保存して、後で必要に応じてインテントを再生成できるので、これも必要です。

バイトストリームにBundleをシリアル化するためには、バンドル内のマップを通過し、各キー/値のペアを取得します。次に、各 "値"(ある種のオブジェクト)を取り込み、最も効率的な方法でそれを直列化できるように、どのようなオブジェクトであるかを判断しようとします。これを行うには、オブジェクトタイプのの既知のオブジェクトタイプのリストに対してオブジェクトタイプをチェックします。「既知のオブジェクトタイプ」のリストには、Integer,Long,String,MapBundleなどがあり、残念なことにListも含まれます。したがって、オブジェクトがList(多くの異なる種類があり、LinkedListを含む)であれば、そのオブジェクトをシリアル化し、それをListのオブジェクトとしてマークします。あなたがこれを行うとき:

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

をそれがタイプListBundle内のすべてのオブジェクトのためのArrayListを生成Bundleはすなわち、デシリアライズされ

Androidのこの動作を変更するために実際には何もできません。少なくとも今、あなたはそれがなぜこれをするのか知っています。

私は実際にこの動作を確認するための小さなテストプログラムを作成し、Bundleからマップをバイトストリームに変換するときに呼び出されるメソッドであるParcel.writeValue(Object v)のソースコードを見てきました。

重要な注意:List以来が、これはあなたがBundleに入れListを実装するクラスがArrayListとして出てくることを意味インタフェースです。 それはMapを意味し、「知られているオブジェクトタイプ」のリストでもあることも興味深いことに関係なく、あなたがBundle(例TreeMapSortedMap、またはMapインタフェースを実装するクラス用)に入れMapオブジェクトの種類、あなたはいつもHashMapを取得します。

+3

私、ああ、私はまず、*はい、私は本当にこれが好きではないと言うかもしれません。結局のところ、 "*既知のオブジェクトタイプ*"という概念全体が、ロジックのその部分を構築するときにコーナーをカットしたがっているように聞こえます。また、もしそうであれば、実際には「* not-so-known *」オブジェクト型が 'Serializable'を実装しないようにするか、または' Serializable'をインテント 'エクストラ '。とにかく、私は十分にあなたに感謝することはできません、そして、はい、私たちはあなたが見つけたもののほうがはるかにスマートです。 – anirvan

+3

お返事ありがとうございました。何時間ものデバッグを保存しました。 – Andree

+2

Davidさん、ありがとうございました。これは私を助けました...私は、私のフラグメントに渡されたsavedInstanceStateと同じ問題を抱えていました。なぜ、Androidが私にArrayListを与えようとしていたのか理解できませんでした... – Patrick

0

@David Wasserの回答は、問題を診断する上で正しいです。この投稿は、私がどのようにそれを処理したかを共有することです。あなたは、常に新しいLinkedListにデシリアライズされたリストのすべての要素を追加します

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

ような何かを行うことができますので、

任意のListオブジェクトがArrayListとして出てくるの問題は、恐ろしいではありません。

LinkedHashMapをシリアル化しようとしている可能性があり、要素の順序が失われている可能性があるので、Mapの場合、問題はさらに悪化します。

幸いにも、これには(比較的)痛みのない方法があります。独自のシリアライズ可能なラッパークラスを定義します。あなたは、特定のタイプのためにそれを行うか、一般的にそれを行うことができます。

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

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

    public T get() { 
     return wrapped; 
    } 
} 

次に、あなたは、Androidの型チェックからあなたListMap、または他のデータ・タイプを非表示にするには、これを使用することができます。

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

以降:

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