2010-08-23 10 views
14

Próbuję rozszerzyć HashMap jako Parcelable i mam składnię do kompilacji, jednak w czasie wykonywania wyrzuca wyjątek i zwraca wskaźnik zerowy próbujący un-marszałka danych.Czy jest możliwe utworzenie mapy HashMap na Androidzie?

Nadawca musi przesłać do (Parcelable), aby rozwiązać niejednoznaczność, jednak odbiorca skarży się, że oczekuje się, że będzie Parcelable, ale odnalazł HashMap.

Czy ktoś odniósł sukces? Czy moja składnia jest nieprawidłowa? Czy istnieje lepsze rozwiązanie?

Poniżej znajduje się kod:

  • HomeActivity.java - nadawca
  • ContentViewActivity.java - odbiornik
  • ContentItemSimple.java - jak sugeruje jego nazwa (okłady String i Integer)
  • ContentItemCollection.java - to HashMap

HomeActivity.java

package com.mobibob.android.studyparceable; 

import java.util.HashMap; 

import android.app.Activity; 
import android.content.Intent; 
import android.os.Bundle; 
import android.os.Parcel; 
import android.os.Parcelable; 
import android.text.format.Time; 
import android.util.Log; 
import android.view.View; 
import android.view.View.OnClickListener; 
import android.widget.Button; 

public class HomeActivity extends Activity implements OnClickListener { 
    private static final String TAG = "HomeActivity"; 
    private ContentItemSimple mContentItemSimple = null; 
    private ContentItemContainer mContentItemContainer = null; 

    /** Called when the activity is first created. */ 
    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.home); 

     Button click = (Button) findViewById(R.id.button_clickit); 
     click.setOnClickListener(this); 

     mContentItemSimple = new ContentItemSimple(); 
     mContentItemSimple.name = "mobibob"; 
     mContentItemSimple.year = 2010; 

     String value = "value"; 
     Time nowTime = new Time(); 
     nowTime.setToNow(); 
     mContentItemContainer = new ContentItemContainer(); 
     mContentItemContainer.put("string", new String("baseball is great!")); 
     mContentItemContainer.put("integer", new Integer(1234)); 
//  mContentItemContainer.put("boolean", new Boolean(true)); 
//  mContentItemContainer.put("date", nowTime); 
//  mContentItemContainer.put("parcel", new Bundle()); 
     Log.d(TAG, "..... " + mContentItemContainer.toString()); 
    } 

    @Override 
    public void onClick(View v) { 

     Intent i = new Intent(getBaseContext(), ContentViewActivity.class); 
     i.putExtra(ContentItemSimple.EXTRA_CONTENT_DETAIL, mContentItemSimple); 
     i.putExtra(ContentItemContainer.EXTRA_CONTENT_CONTAINER, (Parcelable) mContentItemContainer); 
     startActivityForResult(i, 0); 

    } 
} 

ContentViewActivity

package com.mobibob.android.studyparceable; 

import android.app.Activity; 
import android.os.Bundle; 
import android.util.Log; 
import android.view.View; 
import android.view.View.OnClickListener; 
import android.widget.Button; 
import android.widget.TextView; 

import com.mobibob.android.studyparceable.ContentItemSimple; 

public class ContentViewActivity extends Activity implements OnClickListener { 

    private static final String TAG = "ContentViewActivity"; 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 

     setContentView(R.layout.content_view); 

     Button gohome = (Button) findViewById(R.id.button_gohome); 

     gohome.setOnClickListener(this); 

     ContentItemSimple ci = null; 
     ContentItemContainer cx = null; 

     try { 
      ci = getIntent().getParcelableExtra(ContentItemSimple.EXTRA_CONTENT_DETAIL); 
      Log.i(TAG, "ci = " + ci.toString()); 

      cx = getIntent().getParcelableExtra(ContentItemContainer.EXTRA_CONTENT_CONTAINER); 
      Log.i(TAG, "cx = " + cx.toString()); 

      TextView tvName = (TextView) findViewById(R.id.ci_name); 
      tvName.setText(ci.name); 
      TextView tvYear = (TextView) findViewById(R.id.ci_year); 
      tvYear.setText(String.format("%d", ci.year)); 

     } catch (Exception e) { 
      Log.e(TAG, e.toString()); 
      e.printStackTrace(); 
     } 
    } 

    @Override 
    public void onClick(View v) { 
     finish(); 
    } 

} 

ContentItemSimple.java

package com.mobibob.android.studyparceable; 

import android.os.Parcel; 
import android.os.Parcelable; 
import android.util.Log; 

public class ContentItemSimple implements Parcelable { 
     public static final String TAG = "ContentItem"; 
    public static final String EXTRA_CONTENT_DETAIL = "contentDetail"; 
    public String name = "name"; 
    public Integer year = Integer.MIN_VALUE; 

    ContentItemSimple() { 
     name = new String(""); 
     year = new Integer(Integer.MIN_VALUE); 
    } 

    ContentItemSimple(Parcel in) { 
      try { 
       name = in.readString(); 
       year = in.readInt(); 
      } catch (Exception e) { 
       Log.e(TAG, e.toString()); 
       e.printStackTrace(); 
     } 
    } 

    @Override 
    public String toString() { 
     return String.format("name=%s age=%d", name, year); 
    } 

    @Override 
    public int describeContents() { 
     return 0; 
    } 

    @Override 
    public void writeToParcel(Parcel dest, int flags) { 
     dest.writeString(name); 
     dest.writeInt(year); 
    } 

    public static final Parcelable.Creator<ContentItemSimple> CREATOR = new Parcelable.Creator<ContentItemSimple>() { 
     public ContentItemSimple createFromParcel(Parcel in) { 
     return new ContentItemSimple(in); 
     } 

     public ContentItemSimple[] newArray(int size) { 
     return new ContentItemSimple[size]; 
     } 
    }; 

} 

ContentItemContainer.java

package com.mobibob.android.studyparceable; 

import java.util.HashMap; 
import java.util.Iterator; 

import android.os.Parcel; 
import android.os.Parcelable; 

public class ContentItemContainer extends HashMap<String, Object> implements Parcelable { 
    /** 
    * Container for wddx 'struct' elements. 
    */ 
    private static final long serialVersionUID = 1L; 
    // public String name = "?"; 
    // public String value = "?"; 
    public static final String EXTRA_CONTENT_DETAIL = "contentDetail"; 
    public static final String EXTRA_CONTENT_CONTAINER = "contentContainer"; 
    public static final String EXTRA_CONTENTDETAIL_NAME = "name"; 
    public static final String EXTRA_CONTENTDETAIL_VALUE = "value"; 

// private static HashMap<String, Object> map = new HashMap<String, Object>(); 

    ContentItemContainer() { 
     super(); 
    } 

    ContentItemContainer(Parcel in) { 
     in.readMap(this, ContentItemContainer.class.getClassLoader()); 
    } 

    @Override 
    public String toString() { 
     StringBuilder sb = new StringBuilder(""); 
     Integer x = 0; 
     Iterator<HashMap.Entry<String, Object>> it = this.entrySet().iterator(); 
     while (it.hasNext()) { 
      HashMap.Entry<String, Object> pairs = (HashMap.Entry<String, Object>) it.next(); 
      x++; 
      sb.append("\n"+x.toString()+": ").append("name=").append(pairs.getKey()).append(" value=").append(pairs.getValue().toString()); 
     } 
     return sb.toString(); 
    } 

    @Override 
    public int describeContents() { 
     return 0; 
    } 

    @Override 
    public void writeToParcel(Parcel dest, int flags) { 
     dest.writeMap(this); 
    } 

    public static final Parcelable.Creator<ContentItemContainer> CREATOR = new Parcelable.Creator<ContentItemContainer>() { 
     public ContentItemContainer createFromParcel(Parcel in) { 
      return new ContentItemContainer(in); 
     } 

     public ContentItemContainer[] newArray(int size) { 
      return new ContentItemContainer[size]; 
     } 
    }; 

} 
+2

Dlaczego nie użyć 'Bundle' zamiast' HashMap'? – CommonsWare

+0

Pomyślałem o tym, więc pozwól mi zmienić i zobaczyć, czy dostaję to, czego potrzebuję. Tymczasem czy to może działać? Wiem, że dokumentacja stwierdza, że ​​Parcelable nie ma być serializerem "ogólnego przeznaczenia", ale myślałem, że jest to nadal w rozsądnym zakresie. – mobibob

+1

Przełączanie na zestaw wygląda tak, jak będzie działać w przypadku mojego projektu. Tymczasem chciałbym znać odpowiedź na to pytanie w obecnej formie. (Wierzę, że odpowiedź będzie oświecająca w odniesieniu do systemu operacyjnego Android i jego SDK.) – mobibob

Odpowiedz

22

Sposób klasa ContentItemContainer jest wdrożone - jako rozszerzenie HashMap, twoje writeToParcel i Parcelable.Creator nigdy nie będą wywoływane.

Powodem jest to, że mapa jest prawidłowym typem danych do umieszczenia w paczce, tak aby klasa została spłaszczona przy użyciu logiki klasy HashMap, a nie twojej. Dzieje się tak dlatego, że sposób implementacji Parcel sprawdza, czy dostarczone wartości są potomkami niektórych klas w określonej kolejności.

Po zrównaniu dodatków, HashMap zostanie utworzona z danych obiektu w konsekwencji.

Aby obejść ten problem, można ukryć HashMap w klasie i udostępnić pliki pobierające/puttery dla HashMap. Jest to dokładnie to samo, co implementacja ContentValues, a parcelowanie/rozpakowywanie działa bez żadnych problemów.

+0

Więc, pomóżcie mi zrozumieć odpowiedź, czy mówicie, że moja HashMap zapisuje jako Mapę, a następnie próbuje ponownie odczytać jako HashMap, ale czy dane są określone jako mapa, a zatem wyjątek?Czy wyjątek nie powinien być "odrzuconym wyjątkiem", a nie "wyjątkiem wskaźnika zerowego"? – mobibob

+1

Prawie. Pakiet próbuje ponownie odczytać jako Parcelable, ale znajduje HashMap. Tak więc wyjątek CastException klasy występuje wewnętrznie w pakiecie(), ale jest przechwytywany, a następnie pakiet zwraca null w wywołaniu getParcelable() (Intent.getParcelableExtra jest skrótem do wywoływania funkcji Intent.getExtras, a następnie Bundle.getParcelable). – Thorstenvv

+0

To wszystko ma sens, ale nie mam dzisiaj czasu na weryfikację. Zakładam, że patrzysz na źródło Androida, aby odpowiedzieć na moje pytanie i nie można go "debugować" jako blackboxa i narzędzia, jak można oczekiwać, że rozszerzenia i interfejs implikują. Przyjmuję twoją odpowiedź, ponieważ czas ucieka na nagrodę i jestem przekonany, że to rozwinie się, jak wyjaśniasz. – mobibob

Powiązane problemy