Communicate with ISerializable

TeHaTeHa DEMember ✭✭✭

Hey folks,

I've got a little problem with communication between different Activities with the help of Intents.
I have few Classes (all derived from Java.IO.ISerializable) and I want to reach them between different activities like that:

// Code form an Activity that try to give a Result to another Activity
MyClass mc = new MyClass();
Intent.PutExtra("SomeText", mc);
this.SetResult(Result.Ok, Intent);
this.Finish();
//
// Code from the Activity that receives the data
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
  base.OnActivityResult(requestCode, resultCode, data);
  MyClass mc = (MyClass)data.GetSerializableExtra("SomeText"); // <- NotSupportedException
}

(Full Exception-Message is: "System.notSupportedException: Unable to activate instance of type MyClass from native handle dba00025")

This also happens when I try doing cast with JavaCast<>-Extension Method. Another (at least for me) strange thing is: When I use MyClass-Type in Bundle-Objects within the Save- an RestoreInstanceState-Methods it works.
Is there any work-around for this problem or is it just a mistake of mine? How else can I reach complex data between Activities?

Posts

  • CheesebaronCheesebaron DKInsider, University mod

    I don't think Java.IO.ISerializable can be implemented in managed code, or at least it was not possible last year around this time, as can be seen in this thread: http://mono-for-android.1047100.n5.nabble.com/How-to-send-a-custom-Data-Class-to-next-Activity-td4805844.html

    An alternative would be to use something like System.Runtime.Serialization, ServiceStack or Newtonsoft JSON to serialize and deserialize your classes and pass serialized classes between Intents. Where the two latter serializes are probably a bit quicker than the one in System.Runtime.Serialization.

  • TeHaTeHa DEMember ✭✭✭

    thx, I've read the this thread before, but I hoped that this fact has changed in the last year.
    I've simple override the ToString-method so that I can pass the data as string. MyClass-Type also implements a implicit cast Method, so that the receiver-site can simply cast the string to an object.

  • TeHaTeHa DEMember ✭✭✭

    @jonp: A real great example which really works (also if the code-construct looks some kind of weird) after I add a constructor that takes a IntPtr and JniHandleOwnership in the MySerializable-class.

    I'll let you know when this construct reaches its limits :)

  • t0likt0lik RUMember

    Thanks, johnp.
    Now the problem is when I call GetSerializableExtra("test") I get an empty object. What did I miss?

  • JonathanPryorJonathanPryor USXamarin Team Xamurai

    @t0lik: Can you provide additional context, such as where you're calling GetSerializableExtra("test") from? I've improved ExportAttributeTest so that it now reads the Serializable instance, and it deserializes properly for me.

  • t0likt0lik RUMember
    edited December 2012

    My base class is

    public class BaseObject : Java.Lang.Object, ISerializable
    {
        public BaseObject()
        {
        }
    
        public BaseObject(IntPtr handle, JniHandleOwnership transfer) : base(handle, transfer)
        {
        }
    
        [Export("readObject", Throws = new[] 
                                        {
                                            typeof(Java.IO.IOException),
                                            typeof(Java.Lang.ClassNotFoundException)
                                        }
        )]
        private void ReadObjectDummy(Java.IO.ObjectInputStream source)
        {
            Console.WriteLine("I'm in ReadObject");
        }
    
        [Export("writeObject", Throws = new[] 
        {
            typeof(Java.IO.IOException),
            typeof(Java.Lang.ClassNotFoundException)
        }
        )]
        private void WriteObjectDummy(Java.IO.ObjectOutputStream destination)
        {
            Console.WriteLine("I'm in WriteObject");
        }
    }
    

    I use the following class to send it to another activity:

    public class FakeObject : BaseObject
    {
        public FakeObject(IntPtr handle, JniHandleOwnership transfer) : base(handle, transfer)
        {
        }
    
        public FakeObject()
        {
        }
    
        public string StringField1 { get; set; }
    
        public string StringField2 { get; set; }
    
        public string StringField3 { get; set; }
    }
    

    Now starting activity with params:

            var intent = new Intent();
            var obj = new FakeObject();
            obj.StringField1 = "test1";
            obj.StringField2 = "test2";
            intent.SetClass(this, typeof(EditActivity));
            intent.PutExtra("object", obj);
            StartActivityForResult(intent, IDM_EDIT);
    

    In EditActivity.OnCreate() method:

             var extras = Intent.Extras;
             var obj = extras.GetSerializable("object") as FakeObject;
             var testEdit = FindViewById<EditText>(Resource.Id.testEdit);
             // in this line StringField1 is empty 
             testEdit.Text = obj.StringField1;
    
  • TeHaTeHa DEMember ✭✭✭

    @t0lik: I always use just:

    var obj = Intent.GetSerializable("object") as SomeType;
    

    in my Activity-Class

  • JonathanPryorJonathanPryor USXamarin Team Xamurai

    @t0lik: There's one bug in your code...

    the problem is when I call GetSerializableExtra("test") I get an empty object. What did I miss?

    What you missed is that you need to actually implement the serialization and deserialization logic. You're not using (and can't use) Java's default object serializer (there's no Java-side fields to serialize!), so you need to do this yourself:

    class FakeObject : BaseObject
    {   
        public FakeObject()
        {
            Console.WriteLine ("FakeObject..ctor");
        }
    
        public FakeObject(IntPtr handle, JniHandleOwnership transfer)
            : base(handle, transfer)
        {
            Console.WriteLine ("FakeObject..ctor(IntPtr, JniHandleOwnership)");
        }
    
        public string StringField1 { get; set; }
        public string StringField2 { get; set; }
        public string StringField3 { get; set; }
    
        [Export ("readObject", Throws = new [] {
            typeof (Java.IO.IOException),
            typeof (Java.Lang.ClassNotFoundException)})]
        private void ReadObjectDummy (Java.IO.ObjectInputStream source)
        {
            Console.WriteLine ("FakeObject.ReadObjectDummy");
            StringField1 = ReadNullableString (source);
            StringField2 = ReadNullableString (source);
            StringField3 = ReadNullableString (source);
        }
    
        [Export ("writeObject", Throws = new [] {
            typeof (Java.IO.IOException),
            typeof (Java.Lang.ClassNotFoundException)})]
        private void WriteObjectDummy (Java.IO.ObjectOutputStream destination)
        {
            Console.WriteLine ("FakeObject.WriteObjectDummy");
            WriteNullableString (destination, StringField1);
            WriteNullableString (destination, StringField2);
            WriteNullableString (destination, StringField3);
        }
    
        static void WriteNullableString (Java.IO.ObjectOutputStream dest, string value)
        {
            dest.WriteBoolean (value != null);
            if (value != null)
                dest.WriteUTF (value);
        }
    
        static string ReadNullableString (Java.IO.ObjectInputStream source)
        {
            if (source.ReadBoolean ())
                return source.ReadUTF ();
            return null;
        }
    }
    

    The above isn't necessarily the "best" way to go, but it works.

  • t0likt0lik RUMember

    Thanks a lot, Jonathan!
    And from your code I have the following question: how to serialize/deserialize values of types Decimal and DateTime, for example?

  • JonathanPryorJonathanPryor USXamarin Team Xamurai

    @t0lik: Related question: Why are you implementing Java.IO.ISerializable? If it's just to send a serialized copy of some object graph from one Activity to another, you could just serialize to/from XML/JSON/etc. and add to the Intent as a string, or store the "contextual" information within an Android.App.Application subclass.

    how to serialize/deserialize values of types Decimal and DateTime, for example?

    Via strings.

  • t0likt0lik RUMember
    edited December 2012

    Jonathan, you're quite right! I just wish to send object copy to another activity.
    Now I'm using ServiceStack JsonSerializer after failing with Java.IO.ISerializable and it works for me. The question is what benefits and disadvantages of serialization via Java.IO.ISerializable in comparison with ServiceStack JsonSerializer?

  • t0likt0lik RUMember
    edited December 2012

    (deleted message)

  • JonathanPryorJonathanPryor USXamarin Team Xamurai

    what benefits and disadvantages of serialization via Java.IO.ISerializable

    The only reason I can think of for using Java.IO.ISerializable is for compatibility with a Java-originated serializer.

    If both sides of serialization are running within Mono for Android, there's no reason to prefer Java.IO.ISerializable or Android.OS.IParcelable over serialization-to-strings.

  • t0likt0lik RUMember

    Fine, thanks Jonathan.

  • SuperLuoSuperLuo USMember

    Thanks the convasation between you,I get a lot .

  • suneelvangalasuneelvangala USMember

    I got small error when I build my project , it asking namespace for Export. Can any one tel the namespace for export in the above example.
    Thanks in advance

  • suneelvangalasuneelvangala USMember

    Thanks for above example , but I got error when I build my project that there is no namespace for Export. Can anyone please tel the namespace of Export in above example.
    Thanks in advance

  • NinineaNininea USUniversity ✭✭✭

    How should I put list of objects via Serializable object, anyone has an experience?

  • aquabayaquabay AUMember ✭✭

    Sounds like a lot of noise for the simple task of passing data between 2 activities.

  • RudolfStepanRudolfStepan USMember ✭✭

    Hi guys.

    I recommend you using .net serialization and an object <> char[] conversion.

    • Mark your data class (and complex objects if needed) with the [Serializable] attribute.

    • Send serialized char[] and open destination intent:

    Intent intent = new Intent(this, typeof(MyDetailsActivity));
    intent.PutExtra("Item", Util.ObjectToByteArray(myDataClassObject));

    StartActivity(intent);

    • inside the activity :

    protected override void OnCreate(Bundle savedInstanceState)
    {
    ....

    // grab the object back from the extra data container
    var myDataClassObject = Util.ByteArrayToObject(this.Intent.GetByteArrayExtra("Item")) as MyDataClassType;
    

    }

    External Link:
    http://stackoverflow.com/questions/1446547/how-to-convert-an-object-to-a-byte-array-in-c-sharp

    This works for me fine ;)

Sign In or Register to comment.