Skip to content

Conversation

@radekdoulik
Copy link
Member

@radekdoulik radekdoulik commented Jan 7, 2021

Context: #5454

The use of DataContract attribute on Java.Lang.Object class pulls
in System.Runtime.Serialization.Primitives assembly.

We still need to make the Java.Lang.Object class serializable, so that
inherited types can be serialized. Otherwise that would lead to
System.Runtime.Serialization.InvalidDataContractException during
runtime.

Instead of decorating Java.Lang.Object with DataContract attribute,
use Serializable attribute on class and NonSerialized attribute on fields.

This might seem contraproductive, to use multiple attribute instances
instead of one. In real it is not and leads to size saving and getting rid
of System.Runtime.Serialization.Primitives assembly reference
in linked apps. Because the Serializable and NonSerialized attributes
are not present in the compiled output, instead flags are used.

The compiled Java.Lang.Object class IL looks like this:

.class public auto ansi serializable beforefieldinit Java.Lang.Object
       extends [System.Private.CoreLib]System.Object
       implements [System.Private.CoreLib]System.IDisposable,
                  Android.Runtime.IJavaObject,
                  Java.Interop.IJavaObjectEx,
                  [Java.Interop]Java.Interop.IJavaPeerable
{
  .custom instance void Android.Runtime.RegisterAttribute::.ctor(string) = ( 01 00 10 6A 61 76 61 2F 6C 61 6E 67 2F 4F 62 6A   // ...java/lang/Obj
                                                                             65 63 74 01 00 54 02 10 44 6F 4E 6F 74 47 65 6E   // ect..T..DoNotGen
                                                                             65 72 61 74 65 41 63 77 01 )                      // erateAcw.
  .field private static initonly class [Java.Interop]Java.Interop.JniPeerMembers _members
  .field private static class [System.Private.CoreLib]System.Delegate cb_equals_Ljava_lang_Object_
  .field private static class [System.Private.CoreLib]System.Delegate cb_hashCode
  .field private static class [System.Private.CoreLib]System.Delegate cb_toString
  .field private notserialized native int key_handle
  .field private notserialized native int weak_handle
  .field private notserialized int32 refs_added
  .field private notserialized valuetype Android.Runtime.JObjectRefType handle_type
  .field private notserialized native int handle
  .field private notserialized bool needsActivation
  .field private notserialized bool isProxy
...

The apk size savings on net6 in BuildReleaseArm64False test:

> apkdiff -md -e dll$ before.apk after.apk
Size difference in bytes ([*1] apk1 only, [*2] apk2 only):
  -          13 assemblies/Mono.Android.dll
    -         120 Metadata
      -          60 Stream #~ (tables)
        -           6 Table TypeRef
        -           6 Table MemberRef
        -           6 Table CustomAttribute
        -          40 Table AssemblyRef
      -          60 Stream #Strings
    Type Java.Lang.Object
      -             CustomAttribute System.Runtime.Serialization.DataContractAttribute
  -       2,583 assemblies/System.Runtime.Serialization.Primitives.dll *1
Summary:
  -       2,596 Assemblies -0.28% (of 915,384)

I have also added a new test for Java.Lang.Object class serialization with code from
old Hello sample.

Example of System.Runtime.Serialization.InvalidDataContractException during runtime,
if Java.Lang.Object class was not serializable:

I MonoDroid: Android.Runtime.JavaProxyThrowable: Exception of type 'Android.Runtime.JavaProxyThrowable' was thrown.
I MonoDroid:   --- End of managed Android.Runtime.JavaProxyThrowable stack trace ---
I MonoDroid: android.runtime.JavaProxyThrowable: System.Runtime.Serialization.InvalidDataContractException: Type 'UnnamedProject.Person' cannot inherit from a type that is not marked with DataContractAttribute or SerializableAttribute.  Consider marking the base type 'Java.Lang.Object' with DataContractAttribute or SerializableAttribute, or removing them from the derived type.
I MonoDroid:   at System.Runtime.Serialization.ClassDataContract+ClassDataContractCriticalHelper..ctor (System.Type type) [0x001c3] in <686be187480b41979dcbf5635f805a7b>:0
I MonoDroid:   at System.Runtime.Serialization.ClassDataContract..ctor (System.Type type) [0x00000] in <686be187480b41979dcbf5635f805a7b>:0
I MonoDroid:   at System.Runtime.Serialization.DataContract+DataContractCriticalHelper.CreateDataContract (System.Int32 id, System.RuntimeTypeHandle typeHandle, System.Type type) [0x000e0] in <686be187480b41979dcbf5635f805a7b>:0
I MonoDroid:   at System.Runtime.Serialization.DataContract+DataContractCriticalHelper.GetDataContractSkipValidation (System.Int32 id, System.RuntimeTypeHandle typeHandle, System.Type type) [0x0000b] in <686be187480b41979dcbf5635f805a7b>:0
I MonoDroid:   at System.Runtime.Serialization.DataContract.GetDataContractSkipValidation (System.Int32 id, System.RuntimeTypeHandle typeHandle, System.Type type) [0x00000] in <686be187480b41979dcbf5635f805a7b>:0
I MonoDroid:   at System.Runtime.Serialization.DataContract.GetDataContract (System.Int32 id, System.RuntimeTypeHandle typeHandle, System.Runtime.Serialization.SerializationMode mode) [0x00000] in <686be187480b41979dcbf5635f805a7b>:0
I MonoDroid:   at System.Runtime.Serialization.DataContract.GetDataContract (System.RuntimeTypeHandle typeHandle, System.Type type, System.Runtime.Serialization.SerializationMode mode) [0x00006] in <686be187480b41979dcbf5635f805a7b>:0
I MonoDroid:   at System.Runtime.Serialization.DataContract.GetDataContract (System.Type type) [0x00006] in <686be187480b41979dcbf5635f805a7b>:0
I MonoDroid:   at System.Runtime.Serialization.Json.DataContractJsonSerializer.get_RootContract () [0x00022] in <686be187480b41979dcbf5635f805a7b>:0
I MonoDroid:   at System.Runtime.Serialization.Json.DataContractJsonSerializer.InternalWriteObjectContent (System.Runtime.Serialization.XmlWriterDelegator writer, System.Object graph) [0x00031] in <686be187480b41979dcbf5635f805a7b>:0
I MonoDroid:   at System.Runtime.Serialization.Json.DataContractJsonSerializer.InternalWriteObject (System.Runtime.Serialization.XmlWriterDelegator writer, System.Object graph) [0x00008] in <686be187480b41979dcbf5635f805a7b>:0
I MonoDroid:   at System.Runtime.Serialization.XmlObjectSerializer.InternalWriteObject (System.Runtime.Serialization.XmlWriterDelegator writer, System.Object graph, System.Runtime.Serialization.DataContractResolver dataContractResolver) [0x00000] in <686be187480b41979dcbf5635f805a7b>:0
I MonoDroid:   at System.Runtime.Serialization.XmlObjectSerializer.WriteObjectHandleExceptions (System.Runtime.Serialization.XmlWriterDelegator writer, System.Object graph, System.Runtime.Serialization.DataContractResolver dataContractResolver) [0x00073] in <686be187480b41979dcbf5635f805a7b>:0 01-07 16:13:09.246 10624 10624 I MonoDroid:   at System.Runtime.Serialization.XmlObjectSerializer.WriteObjectHandleExceptions (System.Runtime.Serialization.XmlWriterDelegator writer, System.Object graph) [0x00000] in <686be187480b41979dcbf5635f805a7b>:0
I MonoDroid:   at System.Runtime.Serialization.Json.DataContractJsonSerializer.WriteObject (System.Xml.XmlDictionaryWriter writer, System.Object graph) [0x0000d] in <686be187480b41979dcbf5635f805a7b>:0
I MonoDroid:   at System.Runtime.Serialization.Json.DataContractJsonSerializer.WriteObject (System.IO.Stream stream, System.Object graph) [0x00018] in <686be187480b41979dcbf5635f805a7b>:0
I MonoDroid:   at UnnamedProject.MainActivity.TestJsonDeserializationCreatesJavaHandle () [0x00033] in <11a2c65ab60d41dea720c7638d552ce0>:0
I MonoDroid:   at UnnamedProject.MainActivity.OnCreate (Android.OS.Bundle bundle) [0x0004b] in <11a2c65ab60d41dea720c7638d552ce0>:0 01-07 16:13:09.247 10624 10624 I MonoDroid:   at Android.App.Activity.n_OnCreate_Landroid_os_Bundle_ (System.IntPtr jnienv, System.IntPtr native__this, System.IntPtr native_savedInstanceState) [0x00012] in <3045e86f0ced43c19ee1b522f77c72d7>:0
I MonoDroid:   at Android.App.Activity.n_OnCreate_Landroid_os_Bundle_ (System.IntPtr jnienv, System.IntPtr native__this, System.IntPtr native_savedInstanceState) [0x00012] in <3045e86f0ced43c19ee1b522f77c72d7>:0
I MonoDroid:   at (wrapper dynamic-method) Android.Runtime.DynamicMethodNameCounter.1(intptr,intptr,intptr)
I MonoDroid:     at unnamedproject.unnamedproject.MainActivity.n_onCreate(Native Method)
I MonoDroid:     at unnamedproject.unnamedproject.MainActivity.onCreate(MainActivity.java:29)
I MonoDroid:     at android.app.Activity.performCreate(Activity.java:8000)
I MonoDroid:     at android.app.Activity.performCreate(Activity.java:7984)
I MonoDroid:     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1309)
I MonoDroid:     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3404)
I MonoDroid:     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3595)
I MonoDroid:     at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
I MonoDroid:     at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
I MonoDroid:     at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
I MonoDroid:     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066)
I MonoDroid:     at android.os.Handler.dispatchMessage(Handler.java:106)
I MonoDroid:     at android.os.Looper.loop(Looper.java:223)
I MonoDroid:     at android.app.ActivityThread.main(ActivityThread.java:7660)
I MonoDroid:     at java.lang.reflect.Method.invoke(Native Method)
I MonoDroid:     at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
I MonoDroid:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
I MonoDroid:
W ActivityTaskManager:   Force finishing activity UnnamedProject.UnnamedProject/unnamedproject.unnamedproject.MainActivity

@radekdoulik radekdoulik marked this pull request as draft January 7, 2021 16:54
@radekdoulik radekdoulik marked this pull request as ready for review January 7, 2021 17:14
@jonpryor
Copy link
Contributor

jonpryor commented Jan 7, 2021

@radekdoulik : looks like some tests need updating, e.g. Xamarin.Android.Build.Tests.PackagingTest.CheckIncludedAssemblies

The following Expected files are missing. System.Runtime.Serialization.dll
Expected: False
But was:  True

Copy link
Contributor

@dellis1972 dellis1972 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks ok as long as its green :)

@radekdoulik radekdoulik merged commit 101fea2 into dotnet:master Jan 12, 2021
@github-actions github-actions bot locked and limited conversation to collaborators Jan 26, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants