Udostępnij za pośrednictwem


Wywoływane otoki systemu Android dla platformy Xamarin.Android

Otoki z możliwością wywołania systemu Android (ACW) są wymagane za każdym razem, gdy środowisko uruchomieniowe systemu Android wywołuje kod zarządzany. Te otoki są wymagane, ponieważ nie ma możliwości rejestrowania klas w środowisku uruchomieniowym ART (środowisko uruchomieniowe systemu Android). (W szczególności, Funkcja JNI DefineClass() nie jest obsługiwana przez środowisko uruchomieniowe systemu Android.} Otoki z możliwością wywołania systemu Android stanowią w związku z brakiem obsługi rejestracji typu środowiska uruchomieniowego.

Za każdym razem, gdy kod systemu Android musi wykonać metodę virtual lub interfejs, która jest overridden lub zaimplementowana w kodzie zarządzanym, platforma Xamarin.Android musi podać serwer proxy języka Java, aby ta metoda została wysłana do odpowiedniego typu zarządzanego. Te typy serwerów proxy Języka Java to kod Java, który ma "tę samą" klasę bazową i listę interfejsów Java co typ zarządzany, implementując te same konstruktory i deklarując wszelkie zastąpione metody klasy bazowej i interfejsu.

Wywoływane otoki systemu Android są generowane przez program monodroid.exe podczas procesu kompilacji: są generowane dla wszystkich typów, które (bezpośrednio lub pośrednio) dziedziczą obiekt Java.Lang.Object.

Nazywanie otoki z możliwością wywołania systemu Android

Nazwy pakietów dla opakowań wywoływanych przez system Android są oparte na MD5SUM nazwy kwalifikowanej przez zestaw typu eksportowanego. Ta technika nazewnictwa umożliwia udostępnienie tej samej w pełni kwalifikowanej nazwy typu przez różne zestawy bez wprowadzania błędu pakowania.

Ze względu na ten schemat nazewnictwa MD5SUM nie można uzyskać bezpośredniego dostępu do typów według nazwy. Na przykład następujące adb polecenie nie będzie działać, ponieważ nazwa my.ActivityType typu nie jest domyślnie generowana:

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

Ponadto w przypadku próby odwołania się do typu według nazwy mogą pojawić się błędy podobne do następujących:

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

Jeśli potrzebujesz dostępu do typów według nazwy, możesz zadeklarować nazwę tego typu w deklaracji atrybutu. Na przykład poniżej przedstawiono kod, który deklaruje działanie z w pełni kwalifikowaną nazwą My.ActivityType:

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

Właściwość ActivityAttribute.Name można ustawić tak, aby jawnie zadeklarowała nazwę tego działania:

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

Po dodaniu my.ActivityType tego ustawienia właściwości można uzyskać dostęp za pomocą nazwy z kodu zewnętrznego i skryptów adb . Atrybut Name można ustawić dla wielu różnych typów, w tym Activity, , ApplicationService, BroadcastReceiveri ContentProvider:

Nazwa ACW oparta na rozwiązaniu MD5SUM została wprowadzona w środowisku Xamarin.Android 5.0. Aby uzyskać więcej informacji na temat nazewnictwa atrybutów, zobacz RegisterAttribute.

Implementowanie interfejsów

Czasami może być konieczne zaimplementowanie interfejsu systemu Android, takiego jak Android.Content.IComponentCallbacks. Ponieważ wszystkie klasy i interfejsy systemu Android rozszerzają interfejs Android.Runtime.IJavaObject , pojawia się pytanie: jak zaimplementować IJavaObject?

Na powyższe pytanie udzielono odpowiedzi: powodem, dla którego wszystkie typy systemu Android muszą być zaimplementowane IJavaObject , jest to, że platforma Xamarin.Android ma wywoływaną otokę dla systemu Android, tj. serwer proxy Java dla danego typu. Ponieważ monodroid.exe szuka Java.Lang.Object tylko podklas i Java.Lang.Object implementuje IJavaObject, odpowiedź jest oczywista: podklasa 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...
    }
}

Szczegóły implementacji

Pozostała część tej strony zawiera szczegóły implementacji, które mogą ulec zmianie bez powiadomienia (i są tu prezentowane tylko dlatego, że deweloperzy będą ciekawi, co się dzieje).

Na przykład biorąc pod uwagę następujące źródło języka 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 wygeneruje następującą otokę z możliwością wywołania systemu 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);
}

Zwróć uwagę, że klasa bazowa jest zachowywana, a native deklaracje metod są udostępniane dla każdej metody, która jest zastępowana w kodzie zarządzanym.