InvalidCastException in release when casting TextFormatted to ISpannable or ISpanned

539 views Asked by At

This question is the same but it doesn't seem to answer the question as to why this cast fails in release mode but works in debug.

The Android docs specify:

Return the text the TextView is displaying. If setText() was called with an argument of BufferType.SPANNABLE or BufferType.EDITABLE, you can cast the return value from this method to Spannable or Editable, respectively. Note: The content of the return value should not be modified. If you want a modifiable one, you should make your own copy first.

If I run the following in debug it works, in release it throws an InvalidCastException

var editText = FindViewById<EditText>(Resource.Id.MyEditText);
editText.SetText("hello", TextView.BufferType.Spannable);
var myTextView = FindViewById<TextView>(Resource.Id.MyTextView);

try
{
    ISpannable t21 = (ISpannable)editText.TextFormatted;
    ISpanned t22 = (ISpanned)editText.TextFormatted;
}
catch (Exception exception)
{
    myTextView.Text = exception.Message;
}

FATAL EXCEPTION: main
06-09 16:30:34.135 E/AndroidRuntime(31672): Process: App27.App27, PID: 31672
06-09 16:30:34.135 E/AndroidRuntime(31672): java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
06-09 16:30:34.135 E/AndroidRuntime(31672): Caused by: md52ce486a14f4bc06-09 16:30:34.135 E/AndroidRuntime(31672):  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601)
06-09 16:30:34.135 E/AndroidRuntime(31672):     at dalvik.system.NativeStart.main(Native Method)
06-09 16:30:34.135 E/AndroidRuntime(31672): Caused by: java.lang.reflect.InvocationTargetException
06-09 16:30:34.135 E/AndroidRuntime(31672):     at java.lang.reflect.Method.invokeNative(Native Method)
06-09 16:30:34.135 E/AndroidRuntime(31672):     at java.lang.reflect.Method.invoke(Method.java:515)
06-09 16:30:34.135 E/AndroidRuntime(31672):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785)
06-09 16:30:34.135 E/AndroidRuntime(31672):     ... 2 more
06-09 16:30:34.135 E/AndroidRuntime(31672): Caused by: md52ce486a14f4bcd95899665e9d932190b.JavaProxyThrowable: System.InvalidCastException: Cannot cast from source type to destination type.
06-09 16:30:34.135 E/AndroidRuntime(31672): at App27.MainActivity.OnCreate (Android.OS.Bundle) [0x00074] in d:\Users\dbeattie\Documents\Visual Studio 2013\Projects\App27\App27\MainActivity.cs:29
06-09 16:30:34.135 E/AndroidRuntime(31672): at Android.App.Activity.n_OnCreate_Landroid_os_Bundle_ (intptr,intptr,intptr) <IL 0x00013, 0x000ef>
06-09 16:30:34.135 E/AndroidRuntime(31672): at (wrapper dynamic-method) object.6917b467-8852-465b-9332-eaefa6fe6832 (intptr,intptr,intptr) <IL 0x00017, 0x00043>

Version info:

Xamarin 3.11.590.0 (5160db7) Visual Studio extension to enable development for Xamarin.iOS and Xamarin.Android.

Xamarin.Android 5.1.3.1 (d419c934e6ce2113653ff4c40214e3a5d5a69440) Visual Studio plugin to enable development for Xamarin.Android.

1

There are 1 answers

1
SharpMobileCode On BEST ANSWER

I was able to re-create your issue. Though I can't give you a definitive as to exactly why this is happening, I did notice this. It may be either a bug in Xamarin.Android and/or the linker is being too aggressive and doing something that is causing the InvalidCastException here's what I've done.

In my Release Configuration, my Linker was set to "SDK Assemblies Only". The InvalidCastException happened. When I set the Linker to "Don't Link", the InvalidCastException did not happen. This make it similar to the Debug configuration in where the Linker was set to "Don't Link".

So it would appear that the Linker is stripping out something that is needed and/or a bug is Xamarin.Android.

However, I did find a solution that works for both Debug as Release. Since ISpannable and ISpanned objects are java bridges to Java objects, and ultimately will implement Java.Lang.Obj, I usually use JavaCast<> when casting those objects. In casting C# objects, I either use the () or "as" keyword. In this case, since the you're trying to cast Java Objects(wrappers), then the proper casting method would to be use JavaCast<> like so:

var editText = FindViewById<EditText>(Resource.Id.MyEditText);
editText.SetText("hello", TextView.BufferType.Spannable);
var myTextView = FindViewById<TextView>(Resource.Id.MyTextView);

try
{
    ISpannable t21 = editText.TextFormatted.JavaCast<ISpannable>();
    ISpanned t22 = editText.TextFormatted.JavaCast<ISpanned>();
}
catch (Exception exception)
{
    myTextView.Text = exception.Message;
}

Using this method works for both the Debug and Release configuration, including setting the Linker to "Don't Link" and "SDK Assemblies Only".

Either way, I would probably let the Xamarin folks know by submitting a bug report on http://bugzilla.xamarin.com. Regardless, I think that using JavaCast<> in this case (since you are casting java wrappers) is the proper way to cast in this case.