Save 2d array of custom object onSaveInstanceState - android

Hi im trying to save a 2d array of a custom object in a Bundle inside the method onSaveInstanceState so it saves the array of custom objects when the users rotates the screen
but i dont know how to achieve it.. If possible id like an "easy" solution... Thanks
This is the class of the custom object, i tried to add the implements Parcelable & methods but not sure if that part is correct:
package com.example.game
import android.os.Parcel;
import android.os.Parcelable;
//hacemos el implements parcelable para que se puede guardar en el savedinstance
public class NumeroCarton implements Parcelable {
protected int numero;
protected boolean numacertado;
public NumeroCarton(int numero) {
this.numacertado =false;
this.numero = numero;
}
public int getNumero() {
return numero;
}
public void setNumero(int numero) {
this.numero = numero;
}
public boolean isNumacertado() {
return numacertado;
}
public void setNumacertado(boolean numacertado) {
this.numacertado = numacertado;
}
////parcelable :
private NumeroCarton(Parcel in) {
numero = in.readInt();
}
#Override
public int describeContents() {
// TODO Auto-generated method stub
return 0;
}
#Override
public void writeToParcel(Parcel out, int flags) {
out.writeInt(numero);
}
public static final Parcelable.Creator<NumeroCarton> CREATOR
= new Parcelable.Creator<NumeroCarton>() {
public NumeroCarton createFromParcel(Parcel in) {
return new NumeroCarton(in);
}
public NumeroCarton[] newArray(int size) {
return new NumeroCarton[size];
}
};
}
Ok, Inside the activity where i have the object i want to save, its micarton, a 2d array
of the class i wrote above :
NumeroCarton[][] micarton=new NumeroCarton[5][3];
And i want to save it inside the bundle of the onSaveInstanceState method:
public void onSaveInstanceState(Bundle outInstance) {
super.onSaveInstanceState(outInstance);
outInstance.putInt(STATE_PUNTOS, puntos);
//This is my problem:
outInstance.putParcelable(STATE_MICARTONN, micarton);
//The method putParcelable(String, Parcelable)
// in the type Bundle is not //applicable for the arguments
//(String, NumeroCarton[][])
//Neither outInstance.putParcelableArray[](STATE_MICARTONN, micarton)
}

I think if you make the custom class serializable it will work.

There is another easy solution, try to convert 2nd array into csv string like this:
data11,data12,data13
data21,data22,data23
....
in this format, you can easily convert 2nd array to a string and backwards

Related

Crash appears on a member variable that is null despite Gson converter that never returns null: impossible case

In my Activity, I have a Training object member initialized during onCreate(). All the members of this object are set.
private Training mTraining; is a class member
public class Training extends BaseModel {
...
#SerializedName("state")
public TrainingState state;
....
public TrainingPreview() {
}
This object is got from server (JSON), and I had a converter on this state to ensure this enum can't be null (I use GSON engine):
public class TrainingStateConverter extends EnumConverter<TrainingState> {
public static final Type TYPE = new TypeToken<TrainingState>() {}.getType();
#Override
protected TrainingState deserialize(String value) {
return TrainingState.fromString(value);
}
#Override
protected TrainingState getUnknownValue() {
return TrainingState.UNKNOWN;
}
}
During the setup, I've created the exercise list with the listener to show a specific exercise:
private void refreshExercisesList() {
final Runnable showTrainingParts = new Runnable() {
public void run() {
int nbItems = mCardExercises.setExercises(mTraining.training, mTraining.state,
new FlatCardTrainingProfilePartExercisesView.OnClickExerciseListener() {
#Override
public void showPart(String trainingPartId, int index) {
onClickOnExercisesList(trainingPartId, index);
}
});
}
};
}
...
}
My onClickOnExercisesList() method:
private void onClickOnExercisesList(String trainingPartId, int index) {
...
switch (mTraining.state) {
...
This Activity code works perfectly since couple of months, but yesterday there was a NullPointerException on switch (mTraining.state) :
int com.xxx.model.training.TrainingState.ordinal()' on a null object reference
com.xxx.ui.training.TrainingActivity.onClickOnExercisesList
How is possible guys?
Thank you very much for your help!
This would occur if state did not appear in the JSON.
The TypeConverter is only used if there is a value in the JSON to convert. If the value isn't present, then there's nothing to convert, so the value is whatever the default is, which is null, because you didn't set it:
#SerializedName("state")
public TrainingState state;
To fix the issue, initialize the variable to a default value:
#SerializedName("state")
public TrainingState state = TrainingState.UNKNOWN;

Android Parcelable write / read String List null object reference [duplicate]

This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 6 years ago.
I'm trying to read a List of Strings and I keep having the following error:
with the following Parcelable class:
import java.util.List;
import android.os.Parcel;
import android.os.Parcelable;
public class ActivePolicies implements Parcelable {
private List<String> activePolicies;
public ActivePolicies(List<String> activePolicies) {
this.activePolicies = activePolicies;
}
public List<String> getActivePolicies(){
return activePolicies;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeStringList(activePolicies);
}
public static final Parcelable.Creator<ActivePolicies> CREATOR = new Parcelable.Creator<ActivePolicies>() {
#Override
public ActivePolicies createFromParcel(Parcel in) {
try {
return new ActivePolicies(in.createStringArrayList());
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
#Override
public ActivePolicies[] newArray(int size) {
return new ActivePolicies[size];
}
};
}
It keeps giving me the exception on createFromParcel(), but I can't figure out why.
It seems a simple parcelable, but when I'm debugging at readStringList() from the Parcel class, the list is null.
But when I evaluate the value in createStringArrayList() before it's returning, the list is there with the expected values.
you need to create constructor that accept Parcel
public ActivePolicies(Parcel in) {
in.readStringList(activePolicies);
}
in.createStringArrayList()? Why are you creating a new String list when you want to construct your object?
Use this method.
Parcel.readStringList(List<String> list)
Read into the given List items String objects that were written with
writeStringList(List) at the current dataPosition().
Recommended: implement a constructor that accepts a Parcel, then, optionally a readFromParcel method.
public class ActivePolicies implements Parcelable {
private List<String> activePolicies;
public ActivePolicies() {
activePolicies = new ArrayList<String>();
}
public ActivePolicies(Parcel in) {
this();
readFromParcel(in);
}
private void readFromParcel(Parcel in) {
in.readStringList(activePolicies);
}
In the creator.
return new ActivePolicies(in);

Why is Parcel.ReadStringList() writing an empty list in MonoDroid?

I'm using a custom parcelable object called GameSettings to pass a number of settings between Activites within an Android app (developed using MonoDroid). The settings are stored as properties on this GameSettings class, and up until now they've all been simple integers which I've been able to parcel just fine using Parcel.WriteInt() and Parcel.ReadInt().
I've just added a new property to GameSettings called CelebrityNames which is of type List<string>, and I'm trying to pass this in the same way but when ReadStringList() is called the property gets populated with an empty list (despite a non-empty list being written to the parcel prior to this using WriteStringList()). The parcel is being passed from NameEntryActivity to GameRoundActivity.
GameSettings.cs
using System;
using System.Collections.Generic;
using Android.OS;
using Java.Interop;
using Object = Java.Lang.Object;
namespace Celebrities
{
public class GameSettings : Object, IParcelable
{
private static readonly GenericParcelableCreator<GameSettings> _creator
= new GenericParcelableCreator<GameSettings>((parcel) => new GameSettings(parcel));
[ExportField("CREATOR")]
public static GenericParcelableCreator<GameSettings> InitializeCreator()
{
return _creator;
}
public int NumberOfPlayers { get; set; }
public int NumberOfTeams { get; set; }
public int CelebritiesPerPlayer { get; set; }
public int SecondsPerRound { get; set; }
private List<string> _celebrityNames;
public List<string> CelebrityNames {
get
{
_celebrityNames.Shuffle ();
return _celebrityNames;
}
set
{
_celebrityNames = value;
}
}
public GameSettings (int players, int teams, int celebrities, int secondsPerRound)
{
NumberOfPlayers = players;
NumberOfTeams = teams;
CelebritiesPerPlayer = celebrities;
SecondsPerRound = secondsPerRound;
}
private GameSettings(Parcel parcel) : this(parcel.ReadInt (), parcel.ReadInt (), parcel.ReadInt (), parcel.ReadInt ())
{
if (_celebrityNames == null)
{
_celebrityNames = new List<string>();
}
parcel.ReadStringList (_celebrityNames);
}
public void WriteToParcel(Parcel dest, ParcelableWriteFlags flags)
{
dest.WriteInt (NumberOfPlayers);
dest.WriteInt (NumberOfTeams);
dest.WriteInt (CelebritiesPerPlayer);
dest.WriteInt (SecondsPerRound);
dest.WriteStringList (_celebrityNames);
}
public int DescribeContents()
{
return 0;
}
}
}
Note: I'm using the backing variable _celebrityNames for parcelling as I have a custom getter that shuffles the list, which isn't necessary at this point. The problem is the same whether using the property or the variable.
GenericParcelableCreator.cs
using System;
using Android.OS;
using Object = Java.Lang.Object;
namespace Celebrities
{
public sealed class GenericParcelableCreator<T> : Object, IParcelableCreator
where T : Object, new()
{
private readonly Func<Parcel, T> _createFunc;
public GenericParcelableCreator(Func<Parcel, T> createFromParcelFunc)
{
_createFunc = createFromParcelFunc;
}
public Object CreateFromParcel(Parcel source)
{
return _createFunc(source);
}
public Object[] NewArray(int size)
{
return new T[size];
}
}
}
I'm including the relevant code from the Activity classes below (these are not the complete files for brevity, please ask if you think it would be helpful to see the rest too).
NameEntryActivity.cs (where I'm passing the parcel from)
public class NameEntryActivity : Activity
{
...
private GameSettings _gameSettings;
private List<string> _celebrityNames;
protected override void OnCreate (Bundle savedInstanceState)
{
...
_gameSettings = (Intent.Extras.GetParcelable ("GameSettings") as GameSettings);
_celebrityNames = new List<string> ();
...
}
...
private void MoveToNextCelebrity()
{
...
_gameSettings.CelebrityNames = _celebrityNames;
var intent = new Intent (this, typeof(GameRoundActivity));
intent.PutExtra("GameSettings", _gameSettings);
StartActivity (intent);
...
}
}
GameRoundActivity.cs (where I'm passing the parcel to)
public class GameRoundActivity : Activity
{
private GameSettings _gameSettings;
protected override void OnCreate (Bundle savedInstanceState)
{
base.OnCreate (savedInstanceState);
SetContentView (Resource.Layout.GameRound);
_gameSettings = (Intent.Extras.GetParcelable ("GameSettings") as GameSettings);
}
}
This is my first time developing an Android app, so it may well be that I've made a mistake somewhere in implementing the parcelling framework or have misunderstood it. Equally I've been looking at this code for so long that maybe I'm just missing a more general silly mistake :)
Thanks in advance!
I switched to using a string array instead of a list and it's now working using Parcel.WriteStringArray() and Parcel.CreateStringArray().
Obviously this wouldn't be applicable in every situation though so I'm still interested in why this was happening!

Save custom objects when activty stops on Android

I've written a small andorid app. This app uses a vector of custom objects and displays them in a listview. I want to save those objects when the activity is send to background. What is the best way for this. In the vector are about 25 objects. Every object has a boolean, two long, and two strings.
Thanks for your help
do you need them saved when the app shuts down or just when the activity goes into the background?
if your objects are parcelables you could use the save and restore instance state methods:
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelableArrayList("objectsArray", myObjects); // if its an array of parceleble objects
}
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
myObjects= savedInstanceState.getParcelableArrayList("objectsArray");
}
here is an example of a parcelable object
public class KNUserSketch implements Parcelable{
public int id;
public int user;
public String url;
public int views;
public int locations;
public KNUserSketch(JSONObject obj)
{
id = KNSafeJSonUtilties.safeGetInteger(obj, "id");
user = KNSafeJSonUtilties.safeGetInteger(obj, "user");
views = KNSafeJSonUtilties.safeGetInteger(obj, "views");
locations = KNSafeJSonUtilties.safeGetInteger(obj, "locations");
url = KNSafeJSonUtilties.safeGetString(obj, "url");
Log.v("JSON","jsonObject: "+obj.toString());
url.replace("https:", "http:");
}
public KNUserSketch(){
id=-1;
user=-1;
views = 0;
url ="";
}
public KNUserSketch(Parcel p){
id= p.readInt();
user = p.readInt();
url = p.readString();
views = p.readInt();
locations = p.readInt();
}
#Override
public int describeContents() {
// TODO Auto-generated method stub
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
// TODO Auto-generated method stub
dest.writeInt(id);
dest.writeInt(user);
dest.writeString(url);
dest.writeInt(views);
dest.writeInt(locations);
}
public static final Parcelable.Creator<KNUserSketch> CREATOR = new Creator<KNUserSketch>() {
public KNUserSketch createFromParcel(Parcel source) {
return new KNUserSketch(source);
}
public KNUserSketch[] newArray(int size) {
return new KNUserSketch[size];
}
};
}
You could use SharedPreferences to put the objets in a safe place, but objets will have to implement Parcelable protocol.

How do I send an array of objects from one Activity to another?

I have a class like:
Class persona implements Serializable {
int age;
String name;
}
And my first Activity fill an array:
persona[] p;
Then, I need this info in another Activity. How I can send it?
I try to make:
Bundle b = new Bundle();
b.putSerializable("persona", p);
But I Can't.
AFAIK the is no method that put a serializable array into bundle any way here is a solution to use that uses parcel
change you class to this
import android.os.Parcel;
import android.os.Parcelable;
public class persona implements Parcelable {
int age;
String name;
public static final Parcelable.Creator<persona> CREATOR = new Creator<persona>() {
#Override
public persona[] newArray(int size) {
// TODO Auto-generated method stub
return new persona[size];
}
#Override
public persona createFromParcel(Parcel source) {
// TODO Auto-generated method stub
return new persona(source);
}
};
public persona(Parcel in) {
super();
age = in.readInt();
name = in.readString();
}
public persona() {
super();
// TODO Auto-generated constructor stub
}
#Override
public int describeContents() {
// TODO Auto-generated method stub
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(age);
dest.writeString(name);
}
}
then you can send the array like this
Bundle b = new Bundle();
b.putParcelableArray("persona", p);
btw using Parcelable instead of Serializable is more efficient in Android
You class will need to implement Parcelable. Then, you can send it in a bundle by using the Bundle.putParcelableArray()-method.
Also, a general advice: Class names should always start uppercase

Categories

Resources