Android Callable Wrappers for Xamarin.Android
Android Callable Wrappers (ACWs) are required whenever the Android runtime invokes managed code. These wrappers are required because there is no way to register classes with ART (the Android runtime) at runtime. (Specifically, the JNI DefineClass() function is not supported by the Android runtime.} Android Callable Wrappers thus make up for the lack of runtime type registration support.
Every time Android code needs to execute a virtual
or interface
method that is overridden
or implemented in managed code,
Xamarin.Android must provide a Java proxy so that this method is
dispatched to the appropriate managed type. These Java proxy types are
Java code that has the "same" base class and Java interface list as
the managed type, implementing the same constructors and declaring any
overridden base class and interface methods.
Android callable wrappers are generated by the monodroid.exe program during the build process: they are generated for all types that (directly or indirectly) inherit Java.Lang.Object.
Android Callable Wrapper Naming
Package names for Android Callable Wrappers are based on the MD5SUM of the assembly-qualified name of the type being exported. This naming technique makes it possible for the same fully-qualified type name to be made available by different assemblies without introducing a packaging error.
Because of this MD5SUM naming scheme, you cannot directly access your
types by name. For example, the following adb
command will not work
because the type name my.ActivityType
is not generated by default:
adb shell am start -n My.Package.Name/my.ActivityType
Also, you may see errors like the following if you attempt to reference a type by name:
java.lang.ClassNotFoundException: Didn't find class "com.company.app.MainActivity"
on path: DexPathList[[zip file "/data/app/com.company.App-1.apk"] ...
If you do require access to types by name, you can declare a name for
that type in an attribute declaration. For example, here is code that
declares an activity with the fully-qualified name My.ActivityType
:
namespace My {
[Activity]
public partial class ActivityType : Activity {
/* ... */
}
}
The ActivityAttribute.Name
property can be set to explicitly declare
the name of this activity:
namespace My {
[Activity(Name="my.ActivityType")]
public partial class ActivityType : Activity {
/* ... */
}
}
After this property setting is added, my.ActivityType
can be accessed
by name from external code and from adb
scripts. The Name
attribute
can be set for many different types including Activity
,
Application
, Service
, BroadcastReceiver
, and ContentProvider
:
- ActivityAttribute.Name
- ApplicationAttribute.Name
- ServiceAttribute.Name
- BroadcastReceiverAttribute.Name
- ContentProviderAttribute.Name
MD5SUM-based ACW naming was introduced in Xamarin.Android 5.0. For more information about attribute naming, see RegisterAttribute.
Implementing Interfaces
There are times when you may need to implement an Android interface, such as
Android.Content.IComponentCallbacks.
Since all Android classes and interface extend the
Android.Runtime.IJavaObject
interface, the question arises: how do we implement IJavaObject
?
The question was answered above: the reason all Android types need to
implement IJavaObject
is so that Xamarin.Android has an Android
callable wrapper to provide to Android, i.e. a Java proxy for the given
type. Since monodroid.exe only looks for Java.Lang.Object
subclasses, and Java.Lang.Object
implements IJavaObject
, the answer
is obvious: subclass Java.Lang.Object
:
class MyComponentCallbacks : Java.Lang.Object, Android.Content.IComponentCallbacks {
public void OnConfigurationChanged (Android.Content.Res.Configuration newConfig)
{
// implementation goes here...
}
public void OnLowMemory ()
{
// implementation goes here...
}
}
Implementation Details
The remainder of this page provides implementation details subject to change without notice (and is presented here only because developers will be curious about what's going on).
For example, given the following C# source:
using System;
using Android.App;
using Android.OS;
namespace Mono.Samples.HelloWorld
{
public class HelloAndroid : Activity
{
protected override void OnCreate (Bundle savedInstanceState)
{
base.OnCreate (savedInstanceState);
SetContentView (R.layout.main);
}
}
}
The mandroid.exe program will generate the following Android Callable Wrapper:
package mono.samples.helloWorld;
public class HelloAndroid
extends android.app.Activity
{
static final String __md_methods;
static {
__md_methods = "n_onCreate:(Landroid/os/Bundle;)V:GetOnCreate_Landroid_os_Bundle_Handler\n" + "";
mono.android.Runtime.register (
"Mono.Samples.HelloWorld.HelloAndroid, HelloWorld, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=null", HelloAndroid.class, __md_methods);
}
public HelloAndroid ()
{
super ();
if (getClass () == HelloAndroid.class)
mono.android.TypeManager.Activate (
"Mono.Samples.HelloWorld.HelloAndroid, HelloWorld, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=null", "", this, new java.lang.Object[] { });
}
@Override
public void onCreate (android.os.Bundle p0)
{
n_onCreate (p0);
}
private native void n_onCreate (android.os.Bundle p0);
}
Notice that the base class is preserved, and native
method
declarations are provided for each method that is overridden within
managed code.