Partajați prin


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:

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.