Obálky volatelné pro Android pro Xamarin.Android

Při vyvolání spravovaného kódu modul runtime Androidu se vyžadují obálky s možností volání Androidu (ACWs). Tyto obálky jsou vyžadovány, protože neexistuje způsob, jak za běhu zaregistrovat třídy v modulu RUNTIME (Android Runtime). (Konkrétně se jedná o Modul runtime Androidu nepodporuje funkci JNI DefineClass().} Obálky volatelné pro Android se tak tvoří kvůli nedostatku podpory registrace typu modulu runtime.

Pokaždé, když kód Androidu potřebuje spustit metodu nebo metodu virtual rozhraní, která je overridden nebo implementovaná ve spravovaném kódu, musí Xamarin.Android poskytnout proxy jazyka Java, aby byla tato metoda odeslána do příslušného spravovaného typu. Tyto typy proxy serverů Java jsou kód Java, který má "stejnou" základní třídu a seznam rozhraní Java jako spravovaný typ, implementuje stejné konstruktory a deklaruje všechny přepsané základní třídy a metody rozhraní.

Obálky volatelné pro Android jsou generovány programem monodroid.exe během procesu sestavení: jsou generovány pro všechny typy, které (přímo nebo nepřímo) dědí Java.Lang.Object.

Pojmenování volatelného obálky pro Android

Názvy balíčků pro obálky volatelné pro Android jsou založeny na MD5SUM sestavení kvalifikovaný název typu, který se exportuje. Tato technika pojmenování umožňuje, aby byl stejný plně kvalifikovaný název typu zpřístupněn různými sestaveními bez zavedení chyby balení.

Kvůli tomuto schématu pojmenování MD5SUM nemůžete přímo přistupovat k typům podle názvu. Například následující adb příkaz nebude fungovat, protože název my.ActivityType typu není ve výchozím nastavení generován:

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

Pokud se pokusíte odkazovat na typ podle názvu, můžou se také zobrazit chyby podobné následujícímu:

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

Pokud potřebujete přístup k typům podle názvu, můžete deklarovat název pro tento typ v deklaraci atributu. Tady je například kód, který deklaruje aktivitu s plně kvalifikovaným názvem My.ActivityType:

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

Vlastnost ActivityAttribute.Name lze nastavit tak, aby explicitně deklaruje název této aktivity:

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

Po přidání my.ActivityType tohoto nastavení vlastnosti je možné k němu přistupovat pomocí názvu z externího kódu a ze adb skriptů. Atribut Name lze nastavit pro mnoho různých typů, včetně Activity, ApplicationService, , BroadcastReceivera ContentProvider:

Pojmenování ACW založené na MD5SUM bylo zavedeno v Xamarin.Android 5.0. Další informace o pojmenování atributů naleznete v tématu RegisterAttribute.

Implementace rozhraní

Někdy může být potřeba implementovat rozhraní Androidu, například Android.Content.IComponentCallbacks. Vzhledem k tomu, že všechny třídy a rozhraní Androidu rozšiřují rozhraní Android.Runtime.IJavaObject , vyvstává otázka: jak implementujeme IJavaObject?

Na otázku jsme odpověděli výše: důvodem, proč je potřeba implementovat IJavaObject všechny typy Androidu, je, aby Xamarin.Android měl obálku s možností volání pro Android, tj. proxy Java pro daný typ. Vzhledem k tomu , monodroid.exe hledá Java.Lang.Object pouze podtřídy a Java.Lang.Object implementuje IJavaObject, odpověď je zřejmé: podtřída 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...
    }
}

Podrobnosti implementace

Zbývající část této stránky obsahuje podrobnosti implementace, které se mohou bez předchozího upozornění změnit (a jsou zde uvedeny pouze proto, že vývojáři budou zvědaví na to, co se děje).

Například vzhledem k následujícímu zdroji jazyka 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);
        }
    }
}

Program mandroid.exe vygeneruje následující obálku pro 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);
}

Všimněte si, že základní třída je zachována a native deklarace metod jsou k dispozici pro každou metodu, která je přepsána v rámci spravovaného kódu.