2010-04-08 7 views
19

Ich möchte ein Bundle-Objekt serialisieren, aber ich finde es nicht einfach, einen einfachen Weg zu finden. Die Verwendung von Parcel scheint keine Option zu sein, da ich die serialisierten Daten in einer Datei speichern möchte.Wie man ein Bündel serialisiert?

Irgendwelche Ideen zu Möglichkeiten, dies zu tun?

Der Grund, warum ich dies möchte, ist zu speichern und den Status meiner Aktivität wiederherzustellen, auch wenn es vom Benutzer getötet wird. Ich erstelle bereits ein Paket mit dem Status, den ich in onSaveInstanceState speichern möchte. Aber Android behält dieses Bündel nur, wenn die Aktivität vom SYSTEM beendet wird. Wenn der Benutzer die Aktivität beendet, muss ich sie selbst speichern. Daher möchte ich es serialisieren und in einer Datei speichern. Natürlich, wenn Sie eine andere Möglichkeit haben, dasselbe zu erreichen, wäre ich auch dafür dankbar.

Edit: Ich entschied mich, meinen Zustand als JSONObject anstelle eines Bundle zu kodieren. Das JSON-Objekt kann dann als Serializable in ein Bundle gestellt oder in einer Datei gespeichert werden. Wahrscheinlich nicht der effizienteste Weg, aber es ist einfach, und es scheint in Ordnung zu sein.

Antwort

6

Ich benutze SharedPreferences, um diese Einschränkung zu umgehen, es verwendet den gleichen putXXX() und getXXX() Stil zum Speichern und Abrufen von Daten wie die Bundle Klasse und ist relativ einfach zu implementieren, wenn Sie zuvor ein Bundle verwendet haben.

So in onCreate habe ich einen Scheck wie diese

if(savedInstanceState != null) 
{ 
    loadGameDataFromSavedInstanceState(savedInstanceState); 
} 
else 
{ 
    loadGameDataFromSharedPreferences(getPreferences(MODE_PRIVATE)); 
} 

ich meine Spieldaten zu einem Bündel in onSaveInstanceState() und Lastdaten von einem Bündel in onRestoreInstanceState()

UND

speichern auch Spieldaten zu SharedPreferences in onPause(), und Daten von SharedPreferences in onResume()

onPause() 
{ 
    // get a SharedPreferences editor for storing game data to 
    SharedPreferences.Editor mySharedPreferences = getPreferences(MODE_PRIVATE).edit(); 

    // call a function to actually store the game data 
    saveGameDataToSharedPreferences(mySharedPreferences); 

    // make sure you call mySharedPreferences.commit() at the end of your function 
} 

onResume() 
{ 
    loadGameDataFromSharedPreferences(getPreferences(MODE_PRIVATE)); 
} 
0 speichern

I

Ich wäre nicht überrascht, wenn einige Leute denken, dies ist eine falsche Verwendung von SharedPreferences, aber es macht den Job erledigt. Ich verwende diese Methode seit über einem Jahr in all meinen Spielen (fast 2 Millionen Downloads) und es funktioniert.

+2

Sicher, das funktioniert, ich hatte nur gehofft zu vermeiden 2 Möglichkeiten der Bündelung des Staates, auch wenn sie sehr ähnlich sind. – hermo

+0

Dies ist genau das, was ich im Sinn hatte, um einen dauerhaften Zustand zu speichern. – Awemo

7

jede Parcel in einer Datei zu speichern ist sehr einfach:

FileOutputStream fos = context.openFileOutput(localFilename, Context.MODE_PRIVATE); 
Parcel p = Parcel.obtain(); // i make an empty one here, but you can use yours 
fos.write(p.marshall()); 
fos.flush(); 
fos.close(); 

genießen!

+9

Ja, das habe ich auch gefunden. Das Problem ist, dass es keine Garantie dafür gibt, dass Sie es wieder entpacken können, sagen Sie, wenn das Betriebssystem aktualisiert und das Paket geändert wurde. Aber wenn du damit leben kannst, ist es in Ordnung. – hermo

+12

Die Daten, die Sie von der Methode mashall() abrufen, dürfen nicht in einer Art persistentem Speicher abgelegt werden (auf lokaler Festplatte, über ein Netzwerk usw.). Dazu sollten Sie die Standard-Serialisierung oder eine andere Art von allgemeinem Serialisierungsmechanismus verwenden. Die Parcel-Marshalled-Repräsentation ist stark für den lokalen IPC optimiert und versucht daher nicht, die Kompatibilität mit Daten zu gewährleisten, die in verschiedenen Versionen der Plattform erstellt wurden. (http://developer.android.com/reference/android/os/Parcel.html#marshall()) – Oneiros

+0

Ich nehme an, dass Sie gespeicherte Datei nicht auf andere Devices übertragen sollten, aber wahrscheinlich ist es in Ordnung, wenn Sie verwenden auf einem Gerät (zum Beispiel zum Speichern von temporären Daten). –

4

es SharedPreferences Konvertieren:

private void saveToPreferences(Bundle in) { 
    Parcel parcel = Parcel.obtain(); 
    String serialized = null; 
    try { 
     in.writeToParcel(parcel, 0); 

     ByteArrayOutputStream bos = new ByteArrayOutputStream(); 
     IOUtils.write(parcel.marshall(), bos); 

     serialized = Base64.encodeToString(bos.toByteArray(), 0); 
    } catch (IOException e) { 
     Log.e(getClass().getSimpleName(), e.toString(), e); 
    } finally { 
     parcel.recycle(); 
    } 
    if (serialized != null) { 
     SharedPreferences settings = getSharedPreferences(PREFS, 0); 
     Editor editor = settings.edit(); 
     editor.putString("parcel", serialized); 
     editor.commit(); 
    } 
} 

private Bundle restoreFromPreferences() { 
    Bundle bundle = null; 
    SharedPreferences settings = getSharedPreferences(PREFS, 0); 
    String serialized = settings.getString("parcel", null); 

    if (serialized != null) { 
     Parcel parcel = Parcel.obtain(); 
     try { 
      byte[] data = Base64.decode(serialized, 0); 
      parcel.unmarshall(data, 0, data.length); 
      parcel.setDataPosition(0); 
      bundle = parcel.readBundle(); 
     } finally { 
      parcel.recycle(); 
     } 
    } 
    return bundle; 
} 
+4

Dies widerspricht wieder der Empfehlung, den Inhalt eines Pakets in irgendeine Form von persistentem Speicher zu speichern (gegen die die Javadocs warnen). Nehmen wir an, Sie gehen und aktualisieren Ihr Betriebssystem aus irgendeinem Grund, dann stürzt der obige Code Ihre App in der Methode 'restoreFromPreferences()' ab oder gibt einen unbekannten Wert im Bundle zurück. – Yinzara

0

Falls Sie es in persistenten Speicher speichern Sie nicht auf parcel noch serializable Mechanismus verlassen können. Sie müssen es selbst tun und unten ist die Art und Weise, wie ich es in der Regel:

private static final Gson sGson = new GsonBuilder().create(); 
private static final String CHARSET = "UTF-8"; 
// taken from http://www.javacamp.org/javaI/primitiveTypes.html 
private static final int BOOLEAN_LEN = 1; 
private static final int INTEGER_LEN = 4; 
private static final int DOUBLE_LEN = 8; 

public static byte[] serializeBundle(Bundle bundle) { 
    try { 
     List<SerializedItem> list = new ArrayList<>(); 
     if (bundle != null) { 
      Set<String> keys = bundle.keySet(); 
      for (String key : keys) { 
       Object value = bundle.get(key); 
       if (value == null) continue; 
       SerializedItem bis = new SerializedItem(); 
       bis.setClassName(value.getClass().getCanonicalName()); 
       bis.setKey(key); 
       if (value instanceof String) 
        bis.setValue(((String) value).getBytes(CHARSET)); 
       else if (value instanceof SpannableString) { 
        String str = Html.toHtml((Spanned) value); 
        bis.setValue(str.getBytes(CHARSET)); 
       } else if (value.getClass().isAssignableFrom(Integer.class)) { 
        ByteBuffer b = ByteBuffer.allocate(INTEGER_LEN); 
        b.putInt((Integer) value); 
        bis.setValue(b.array()); 
       } else if (value.getClass().isAssignableFrom(Double.class)) { 
        ByteBuffer b = ByteBuffer.allocate(DOUBLE_LEN); 
        b.putDouble((Double) value); 
        bis.setValue(b.array()); 
       } else if (value.getClass().isAssignableFrom(Boolean.class)) { 
        ByteBuffer b = ByteBuffer.allocate(INTEGER_LEN); 
        boolean v = (boolean) value; 
        b.putInt(v ? 1 : 0); 
        bis.setValue(b.array()); 
       } else 
        continue; // we do nothing in this case since there is amazing amount of stuff you can put into bundle but if you want something specific you can still add it 
//      throw new IllegalStateException("Unable to serialize class + " + value.getClass().getCanonicalName()); 

       list.add(bis); 
      } 
      return sGson.toJson(list).getBytes(CHARSET); 
     } 
    } catch (Exception e) { 
     e.printStackTrace(); 
    } 
    throw new IllegalStateException("Unable to serialize " + bundle); 
} 

public static Bundle deserializeBundle(byte[] toDeserialize) { 
    try { 
     Bundle bundle = new Bundle(); 
     if (toDeserialize != null) { 
      SerializedItem[] bundleItems = new Gson().fromJson(new String(toDeserialize, CHARSET), SerializedItem[].class); 
      for (SerializedItem bis : bundleItems) { 
       if (String.class.getCanonicalName().equals(bis.getClassName())) 
        bundle.putString(bis.getKey(), new String(bis.getValue())); 
       else if (Integer.class.getCanonicalName().equals(bis.getClassName())) 
        bundle.putInt(bis.getKey(), ByteBuffer.wrap(bis.getValue()).getInt()); 
       else if (Double.class.getCanonicalName().equals(bis.getClassName())) 
        bundle.putDouble(bis.getKey(), ByteBuffer.wrap(bis.getValue()).getDouble()); 
       else if (Boolean.class.getCanonicalName().equals(bis.getClassName())) { 
        int v = ByteBuffer.wrap(bis.getValue()).getInt(); 
        bundle.putBoolean(bis.getKey(), v == 1); 
       } else 
        throw new IllegalStateException("Unable to deserialize class " + bis.getClassName()); 
      } 
     } 
     return bundle; 
    } catch (Exception e) { 
     e.printStackTrace(); 
    } 
    throw new IllegalStateException("Unable to deserialize " + Arrays.toString(toDeserialize)); 
} 

Sie stellen Daten als Byte-Array, das Sie ganz einfach speichern können über das Netzwerk oder speichern, um SQL-Datenbank-Datei, sendet mit ormLite als folgt:

@DatabaseField(dataType = DataType.BYTE_ARRAY) 
    private byte[] mRawBundle; 

und SerializedItem:

public class SerializedItem { 


private String mClassName; 
private String mKey; 
private byte[] mValue; 

// + getters and setters 
} 

PS: über den Code auf Gson Bibliothek abhängig ist (was ziemlich üblich ist, lassen Sie einfach Sie wissen).