Android Callable Wrappers for Xamarin.Android - Xamarin (original) (raw)
Share via
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, theJNI DefineClass() functionis 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.exeprogram during thebuild process: they are generated for all types that (directly or indirectly) inheritJava.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, seeRegisterAttribute.
Implementing Interfaces
There are times when you may need to implement an Android interface, such asAndroid.Content.IComponentCallbacks. Since all Android classes and interface extend theAndroid.Runtime.IJavaObjectinterface, 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.