Description
generator
emits System.String
overloads for Java members which accept or return java.lang.ICharSequence
. For example, consider TextView.setText()
, which is combined with getText()
into a TextFormatted
property:
namespace Android.Widget {
public partial class TextView {
public unsafe Java.Lang.ICharSequence? TextFormatted {
get => …,
set {
const string __id = "setText.(Ljava/lang/CharSequence;)V";
IntPtr native_value = CharSequence.ToLocalJniHandle (value);
try {
JniArgumentValue* __args = stackalloc JniArgumentValue [1];
__args [0] = new JniArgumentValue (native_value);
_members.InstanceMethods.InvokeNonvirtualVoidMethod (__id, this, __args);
} finally {
JNIEnv.DeleteLocalRef (native_value);
global::System.GC.KeepAlive (value);
}
}
}
}
}
To simplify use, generator
"overloads" this property to accept System.String
:
namespace Android.Widget {
public partial class TextView {
public string? Text {
get => TextFormatted == null ? null : TextFormatted.ToString (),
set {
var jls = value == null ? null : new global::Java.Lang.String (value);
TextFormatted = jls;
if (jls != null) jls.Dispose ();
}
}
}
}
This works, but has some implicit overheads: creating Java.Lang.String
instances requires registering them with the JniRuntime.JniValueManager
, which is "silly" when we will immediately Dispose()
of the value. This can show up when profiling apps which call TextView.set_Text()
frequently.
We could optimize these "overloads" by skipping Java.Lang.String
instances, and instead using JniObjectReference
and JniEnvironment.Strings.NewString(string?)
.
Unfortunately, just because we can doesn't mean that it will be straightforward. There are two "reasonable" ways to implement this:
Copy most of the code from e.g. set_TextFormatted()
into set_Text()
:
namespace Android.Widget {
public partial class TextView {
public unsafe string? Text {
set {
const string __id = "setText.(Ljava/lang/CharSequence;)V";
JniObjectReference native_value = JniEnvironment.Strings.NewString (value);
try {
JniArgumentValue* __args = stackalloc JniArgumentValue [1];
__args [0] = new JniArgumentValue (native_value);
_members.InstanceMethods.InvokeNonvirtualVoidMethod (__id, this, __args);
} finally {
JniObjectReference.Dispose (ref native_value);
}
}
}
}
}
Alternatively, we can look into Issue #795, and move the _members.InstanceMethods.InvokeNonvirtualVoidMethod()
invocation into an "ABI method." Instead of fully adopting #795, we could put the "ABI method" into the same scope, and use that from both TextFormatted
and Text
:
namespace Android.Widget {
public partial class TextView {
public ICharSequence? TextFormatted {
set {
IntPtr native_value = CharSequence.ToLocalJniHandle (value);
try {
abi_setText (this, native_value);
} finally {
JNIEnv.DeleteLocalRef (native_value);
global::System.GC.KeepAlive (value);
}
}
}
public string? Text {
set {
JniObjectReference native_value = JniEnvironment.Strings.NewString (value);
try {
abi_setText (this, native_value);
} finally {
JniObjectReference.Dispose (ref native_value);
}
}
}
private static void abi_setText (IJavaPeerable self, JniObjectReference text)
{
const string __id = "setText.(Ljava/lang/CharSequence;)V";
try {
JniArgumentValue* __args = stackalloc JniArgumentValue [1];
__args [0] = new JniArgumentValue (text);
_members.InstanceMethods.InvokeNonvirtualVoidMethod (__id, this, __args);
} finally {
global::System.GC.KeepAlive (self);
}
}
}
}
This has the benefit of reducing code duplication.