Вызываемые программы-оболочки Android для Xamarin.Android

Вызываемые оболочки Android (ACW) требуются при каждом вызове управляемого кода средой выполнения Android. Эти оболочки являются обязательными, так как в среде выполнения не существует способа регистрации классов с помощью ART (среда выполнения Android). (В частности, функция JNI DefineClass() не поддерживается средой выполнения Android.} Таким образом вызываемые оболочки Android компенсируют отсутствие поддержки регистрации типа среды выполнения.

Код Android должен каждый раз выполнять virtual или метод интерфейса, который является overridden или реализован в управляемом коде, а Xamarin.Android — предоставить прокси-сервер Java, чтобы этот метод был отправлен в соответствующий управляемый тип. Эти типы прокси Java — это код Java, имеющий тот же базовый класс и список интерфейсов Java, что и управляемый тип, реализующий те же конструкторы и объявляющий любые переопределенные базовые классы и методы интерфейса.

Вызываемые оболочки Android создаются программой monodroid.exe во время процесса сборки: они создаются для всех типов, которые (прямо или косвенно) наследуют Java.Lang.Object.

Имя вызываемой программы-оболочки

Имена пакетов для вызываемых оболочек Android основаны на MD5SUM имени экспортируемого типа с указанием сборки. Такая методика именования позволяет сделать то же полное имя типа доступным для разных сборок без введения ошибки упаковки.

Из-за этой схемы именования MD5SUM вы не можете напрямую обращаться к типам по имени. Например, следующая команда adb не будет работать, так как имя типа my.ActivityType не создается по умолчанию:

adb shell am start -n My.Package.Name/my.ActivityType

Кроме того, если вы попытаетесь сослаться на тип по имени, вы можете заметить следующие ошибки:

java.lang.ClassNotFoundException: Didn't find class "com.company.app.MainActivity"
on path: DexPathList[[zip file "/data/app/com.company.App-1.apk"] ...

Если вы требуете доступ к типам по имени, имя для этого типа можно объявить в объявлении атрибута. Например, ниже приведен код, объявляющий действие с полным именем My.ActivityType:

namespace My {
    [Activity]
    public partial class ActivityType : Activity {
        /* ... */
    }
}

Для свойства ActivityAttribute.Name можно задать явное объявление имени этого действия:

namespace My {
    [Activity(Name="my.ActivityType")]
    public partial class ActivityType : Activity {
        /* ... */
    }
}

После добавления этого параметра свойства my.ActivityType к нему можно получить доступ по имени из внешнего кода и из сценариев adb. Атрибут Name можно задать для множества различных типов, включая Activity, Application, Service, BroadcastReceiver и ContentProvider.

В Xamarin.Android 5.0 введено именование ACW на базе MD5SUM. Дополнительные сведения об именовании атрибутов см. в RegisterAttribute.

Реализация интерфейсов

Иногда может потребоваться реализовать интерфейс Android, например Android.Content.IComponentCallbacks. Поскольку все классы и интерфейсы Android расширяют интерфейс Android.Runtime.IJavaObject, возникает вопрос: как реализовать IJavaObject?

Ответ на этот вопрос дан выше: причина, по которой все типы Android должны реализовать IJavaObject, заключается в том, что Xamarin.Android имеет вызываемую программу-оболочку Android для предоставления Android, т. е. прокси-сервер Java для данного типа. Поскольку monodroid.exe ищет только подклассы Java.Lang.Object, а Java.Lang.Object реализует IJavaObject, ответ очевиден: подкласс 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...
    }
}

Сведения о реализации

Оставшаяся часть этой страницы предоставляет сведения о реализации, которые могут быть изменены без предварительного уведомления (и предоставляются здесь только потому, что разработчикам интересно, как это происходит).

Например, рассмотрим приведенный ниже исходный код C#.

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);
        }
    }
}

Программа mandroid.exe создаст следующую вызываемую оболочку Android:

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);
}

Обратите внимание, что базовый класс сохраняется, и для каждого метода, переопределяемого в управляемом коде, предоставляются объявления методов native.