Freigeben über


Aufbau

Xamarin.Android-Anwendungen werden in der Mono-Ausführungsumgebung ausgeführt. Diese Ausführungsumgebung wird parallel mit dem virtuellen Computer für Android-Runtime (ART) ausgeführt. Beide Laufzeitumgebungen werden über dem Linux-Kernel ausgeführt und machen verschiedene APIs für den Benutzercode verfügbar, mit dem Entwickler auf das zugrunde liegende System zugreifen können. Die Mono-Laufzeit wird in der Sprache C geschrieben.

Sie können das System, System.IO, System.Net und die restlichen .NET-Klassenbibliotheken verwenden, um auf die zugrunde liegenden Linux-Betriebssystemeinrichtungen zuzugreifen.

Unter Android sind die meisten Systemeinrichtungen wie Audio, Grafiken, OpenGL und Telefonie nicht direkt für systemeigene Anwendungen verfügbar, sie werden nur über die Java-APIs der Android-Runtime verfügbar gemacht, die sich in einem der Java.*-Namespaces oder den Android.*-Namespaces befinden. Die Architektur ist ungefähr wie folgt:

Diagramm von Mono und ART über dem Kernel und unter .NET/Java + Bindungen

Xamarin.Android-Entwickler greifen auf die verschiedenen Features des Betriebssystems zu, indem sie entweder .NET-APIs aufrufen, die sie kennen (für den Zugriff auf niedriger Ebene) oder die Klassen verwenden, die in den Android-Namespaces verfügbar gemacht werden, die eine Brücke zu den Java-APIs bieten, die von der Android-Runtime verfügbar gemacht werden.

Weitere Informationen dazu, wie die Android-Klassen mit den Android-Runtime-Klassen kommunizieren, finden Sie im API-Designdokument .

Anwendungspakete

Android-Anwendungspakete sind ZIP-Container mit einer .apk Dateierweiterung. Xamarin.Android-Anwendungspakete weisen die gleiche Struktur und das gleiche Layout wie normale Android-Pakete mit den folgenden Ergänzungen auf:

  • Die Anwendungsassemblys (mit IL) werden innerhalb des Assemblyordners nicht komprimiert gespeichert. Während des Prozessstarts in Release-Builds wird der .apk mmap() in den Prozess eingegliedert, und die Assemblys werden aus dem Arbeitsspeicher geladen. Dies ermöglicht einen schnelleren App-Start, da Assemblys vor der Ausführung nicht extrahiert werden müssen.

  • Hinweis: Assemblyspeicherortinformationen wie Assembly.Location und Assembly.CodeBasekönnen in Releasebuilds nicht vertrauen . Sie sind nicht als unterschiedliche Dateisystemeinträge vorhanden und haben keinen verwendbaren Speicherort.

  • Native Bibliotheken, die die Mono-Laufzeit enthalten, sind in der .apk vorhanden. Eine Xamarin.Android-Anwendung muss native Bibliotheken für die gewünschten/gezielten Android-Architekturen enthalten, z. B. armeabi , armeabi-v7a , x86 . Xamarin.Android-Anwendungen können nicht auf einer Plattform ausgeführt werden, es sei denn, sie enthält die entsprechenden Laufzeitbibliotheken.

Xamarin.Android-Anwendungen enthalten auch Android-Aufrufbare Wrapper , damit Android verwalteten Code aufrufen kann.

Android Callable Wrapper

  • Android-Aufrufbare Wrapper sind eine JNI-Brücke , die jedes Mal verwendet wird, wenn die Android-Runtime verwalteten Code aufrufen muss. Aufzurufende Android-Wrapper stellen fest, wie virtuelle Methoden überschrieben werden können und Java-Schnittstellen implementiert werden können. Weitere Informationen finden Sie im Java-Integrationsübersichtsdokument .

Managed Callable Wrappers

Verwaltete aufrufbare Wrapper sind eine JNI-Brücke, die jedes Mal verwendet wird, wenn verwalteter Code Android-Code aufruft und Unterstützung für das Überschreiben virtueller Methoden und die Implementierung von Java-Schnittstellen bereitstellt. Die gesamten Android.* und zugehörigen Namespaces werden über .jar Bindung generierte aufrufbare Wrapper verwaltet. Verwaltete aufrufbare Wrapper sind dafür verantwortlich, zwischen verwalteten und Android-Typen zu konvertieren und die zugrunde liegenden Android-Plattformmethoden über JNI aufzurufen.

Jeder erstellte verwaltete aufrufbare Wrapper enthält einen globalen Java-Verweis, auf den über die Android.Runtime.IJavaObject.Handle-Eigenschaft zugegriffen werden kann. Globale Verweise werden verwendet, um die Zuordnung zwischen Java-Instanzen und verwalteten Instanzen bereitzustellen. Globale Verweise sind eine begrenzte Ressource: Emulatoren ermöglichen jeweils nur 2000 globale Verweise, während die meisten Hardware gleichzeitig über 52.000 globale Verweise zulassen.

Um nachzuverfolgen, wann globale Verweise erstellt und zerstört werden, können Sie die debug.mono.log Systemeigenschaft so festlegen, dass sie Gref enthält.

Globale Verweise können explizit freigegeben werden, indem Java.Lang.Object.Dispose() für den verwalteten aufrufbaren Wrapper aufgerufen wird. Dadurch wird die Zuordnung zwischen der Java-Instanz und der verwalteten Instanz entfernt und die Erfassung der Java-Instanz ermöglicht. Wenn auf die Java-Instanz über verwalteten Code erneut zugegriffen wird, wird dafür ein neuer verwalteter aufrufbarer Wrapper erstellt.

Die Sorgfalt muss beim Löschen von verwalteten aufrufbaren Wrappern ausgeübt werden, wenn die Instanz versehentlich zwischen Threads freigegeben werden kann, da sich das Löschen der Instanz auf Verweise aus anderen Threads auswirkt. Für maximale Sicherheit gilt nur Dispose() für Instanzen, die über new oder von Methoden zugewiesen wurden, die Sie wissen , immer neue Instanzen und keine zwischengespeicherten Instanzen zuzuweisen, die zu versehentlicher Instanzenfreigabe zwischen Threads führen können.

Verwaltete aufrufbare Wrapper-Unterklassen

Verwaltete aufrufbare Wrapper-Unterklassen sind der Ort, an dem sich alle "interessanten" anwendungsspezifischen Logik befinden. Dazu gehören benutzerdefinierte Android.App.Activity-Unterklassen (z. B. der Aktivität1-Typ in der Standardprojektvorlage). (Insbesondere sind dies alle Java.Lang.Object-Unterklassen, die kein benutzerdefiniertes RegisterAttribute-Attribut oder RegisterAttribute.DoNotGenerateAcw enthalten, ist false, was der Standardwert ist.)

Wie verwaltete aufrufbare Wrapper enthalten verwaltete aufrufbare Wrapper-Unterklassen auch einen globalen Verweis, auf den über die Java.Lang.Object.Handle-Eigenschaft zugegriffen werden kann. Genau wie bei verwalteten aufrufbaren Wrappern können globale Verweise explizit durch Aufrufen von Java.Lang.Object.Dispose()freigegeben werden. Im Gegensatz zu verwalteten aufrufbaren Wrappern sollte vor dem Löschen solcher Instanzen eine große Sorgfalt ergriffen werden, da dispose()-ing der Instanz die Zuordnung zwischen der Java-Instanz (einer Instanz eines android callable Wrappers) und der verwalteten Instanz unterbrochen.

Java-Aktivierung

Wenn ein aufrufbarer Android-Wrapper (ACW) aus Java erstellt wird, bewirkt der ACW-Konstruktor, dass der entsprechende C#-Konstruktor aufgerufen wird. Beispielsweise enthält das ACW für MainActivity einen Standardkonstruktor, der den Standardkonstruktor von MainActivity aufruft. (Dies erfolgt über die TypeManager.Activate()- Aufruf innerhalb der ACW-Konstruktoren.)

Es gibt eine andere Konstruktorsignatur der Folge: der Konstruktor (IntPtr, JniHandleOwnership). Der Konstruktor (IntPtr, JniHandleOwnership) wird aufgerufen, wenn ein Java-Objekt für verwalteten Code verfügbar gemacht wird und ein verwalteter Aufrufbarer Wrapper erstellt werden muss, um das JNI-Handle zu verwalten. Dies erfolgt in der Regel automatisch.

Es gibt zwei Szenarien, in denen der Konstruktor (IntPtr, JniHandleOwnership) in einer unterklasse verwalteten aufrufbaren Wrapper manuell bereitgestellt werden muss:

  1. Android.App.Application ist unterklassigt. Die Anwendung ist speziell. Der Standardmäßige Applicaton-Konstruktor wird nie aufgerufen, und der Konstruktor (IntPtr, JniHandleOwnership) muss stattdessen bereitgestellt werden.

  2. Virtuelle Methodenaufrufe aus einem Basisklassenkonstruktor.

Beachten Sie, dass (2) eine leckige Abstraktion ist. In Java rufen wie in C# Aufrufe virtueller Methoden aus einem Konstruktor immer die abgeleitete Methodenimplementierung auf. Beispielsweise ruft der TextView(Context, AttributeSet, int)-Konstruktor die virtuelle Methode TextView.getDefaultMovementMethod(), die als TextView.DefaultMovementMethod-Eigenschaft gebunden ist, auf. Wenn also ein Typ LogTextBox (1) Unterklasse TextView, (2) TextView.DefaultMovementMethod überschreibt und (3) eine Instanz dieser Klasse über XML aktivieren würde, würde die Außerkraftsetzung DefaultMovementMethod-Eigenschaft aufgerufen, bevor der ACW-Konstruktor ausgeführt werden kann, und es würde auftreten, bevor der C#-Konstruktor ausgeführt werden kann.

Dies wird unterstützt, indem eine Instanz LogTextBox über den LogTextView(IntPtr, JniHandleOwnership) -Konstruktor instanziiert wird, wenn die ACW LogTextBox-Instanz zuerst verwalteten Code eingibt, und dann den LogTextBox(Context, IAttributeSet, int) -Konstruktor für dieselbe Instanz aufrufen, wenn der ACW-Konstruktor ausgeführt wird.

Reihenfolge der Ereignisse:

  1. Layout-XML wird in eine ContentView geladen.

  2. Android instanziiert das Layout-Objektdiagramm und instanziiert eine Instanz von monodroid.apidemo.LogTextBox , dem ACW für LogTextBox .

  3. Der monodroid.apidemo.LogTextBox-Konstruktor führt den Konstruktor "android.widget.TextView " aus.

  4. Der TextView-Konstruktor ruft monodroid.apidemo.LogTextBox.getDefaultMovementMethod() auf.

  5. monodroid.apidemo.LogTextBox.getDefaultMovementMethod() ruft LogTextBox.n_getDefaultMovementMethod() auf, die TextView.n_GetDefaultMovementMethod() aufruft, wodurch Java.Lang.Object.GetObject<TextView> (handle, JniHandleOwnership.DoNotTransfer) aufgerufen wird.

  6. Java.Lang.Object.GetObject<TextView>() überprüft, ob bereits eine entsprechende C#-Instanz für handle vorhanden ist. Wenn vorhanden, wird sie zurückgegeben. In diesem Szenario ist dies nicht der Fall, daher muss Object.GetObject<T>() eine erstellen.

  7. Object.GetObject<T>() sucht nach dem LogTextBox(IntPtr, JniHandleOwneship) -Konstruktor, ruft ihn auf, erstellt eine Zuordnung zwischen Handle und der erstellten Instanz und gibt die erstellte Instanz zurück.

  8. TextView.n_GetDefaultMovementMethod() ruft den Getter der LogTextBox.DefaultMovementMethod-Eigenschaft auf.

  9. Das Steuerelement kehrt zum Konstruktor "android.widget.TextView " zurück, der die Ausführung beendet.

  10. Der monodroid.apidemo.LogTextBox-Konstruktor wird ausgeführt, wobei TypeManager.Activate() angezeigt wird.

  11. Der LogTextBox(Context,IAttributeSet, int) -Konstruktor wird für dieselbe Instanz ausgeführt , die in (7) erstellt wurde.

  12. Wenn der Konstruktor (IntPtr, JniHandleOwnership) nicht gefunden werden kann, wird ein System.MissingMethodException](xref:System.MissingMethodException) ausgelöst.

Vorzeitige Dispose()-Aufrufe

Es gibt eine Zuordnung zwischen einem JNI-Handle und der entsprechenden C#-Instanz. Java.Lang.Object.Dispose() bricht diese Zuordnung auf. Wenn ein JNI-Handle verwalteten Code eingibt, nachdem die Zuordnung unterbrochen wurde, sieht es wie die Java-Aktivierung aus, und der Konstruktor (IntPtr, JniHandleOwnership) wird überprüft und aufgerufen. Wenn der Konstruktor nicht vorhanden ist, wird eine Ausnahme ausgelöst.

Beispiel: In Anbetracht der folgenden Unterklasse für anrufbare Anrufumbruche:

class ManagedValue : Java.Lang.Object {

    public string Value {get; private set;}

    public ManagedValue (string value)
    {
        Value = value;
    }

    public override string ToString ()
    {
        return string.Format ("[Managed: Value={0}]", Value);
    }
}

Wenn wir eine Instanz erstellen, wird Dispose() davon erstellt und bewirkt, dass der verwaltete aufrufbare Wrapper neu erstellt wird:

var list = new JavaList<IJavaObject>();
list.Add (new ManagedValue ("value"));
list [0].Dispose ();
Console.WriteLine (list [0].ToString ());

Das Programm stirbt:

E/mono    ( 2906): Unhandled Exception: System.NotSupportedException: Unable to activate instance of type Scratch.PrematureDispose.ManagedValue from native handle 4051c8c8 --->
System.MissingMethodException: No constructor found for Scratch.PrematureDispose.ManagedValue::.ctor(System.IntPtr, Android.Runtime.JniHandleOwnership)
E/mono    ( 2906):   at Java.Interop.TypeManager.CreateProxy (System.Type type, IntPtr handle, JniHandleOwnership transfer) [0x00000] in <filename unknown>:0
E/mono    ( 2906):   at Java.Interop.TypeManager.CreateInstance (IntPtr handle, JniHandleOwnership transfer, System.Type targetType) [0x00000] in <filename unknown>:0
E/mono    ( 2906):   --- End of inner exception stack trace ---
E/mono    ( 2906):   at Java.Interop.TypeManager.CreateInstance (IntPtr handle, JniHandleOwnership transfer, System.Type targetType) [0x00000] in <filename unknown>:0
E/mono    ( 2906):   at Java.Lang.Object.GetObject (IntPtr handle, JniHandleOwnership transfer, System.Type type) [0x00000] in <filename unknown>:0
E/mono    ( 2906):   at Java.Lang.Object._GetObject[IJavaObject] (IntPtr handle, JniHandleOwnership transfer) [0x00000

Wenn die Unterklasse einen (IntPtr, JniHandleOwnership) -Konstruktor enthält, wird eine neue Instanz des Typs erstellt. Daher scheint die Instanz alle Instanzdaten zu "verlieren", da es sich um eine neue Instanz ist. (Beachten Sie, dass der Wert null ist.)

I/mono-stdout( 2993): [Managed: Value=]

Nur Dispose() von verwalteten aufrufbaren Wrapper-Unterklassen, wenn Sie wissen, dass das Java-Objekt nicht mehr verwendet wird, oder die Unterklasse enthält keine Instanzdaten und ein (IntPtr, JniHandleOwnership) -Konstruktor wurde bereitgestellt.

Application Startup (Starten von Anwendungen)

Wenn eine Aktivität, ein Dienst usw. gestartet wird, überprüft Android zunächst, ob bereits ein Prozess ausgeführt wird, um die Aktivität/den Dienst/etc. zu hosten. Wenn kein solcher Prozess vorhanden ist, wird ein neuer Prozess erstellt, der AndroidManifest.xml gelesen und der im Attribut "/manifest/application/@android:name " angegebene Typ geladen und instanziiert. Als Nächstes werden alle typen, die durch die Attributwerte "/manifest/application/provider/@android:name " angegeben sind, instanziiert und die ContentProvider.attachInfo%28) -Methode aufgerufen. Xamarin.Android hooks into this by adding a mono. MonoRuntimeProvider ContentProvider zum AndroidManifest.xml während des Buildvorgangs. Das Mono. Die MonoRuntimeProvider.attachInfo() -Methode ist für das Laden der Mono-Laufzeit in den Prozess verantwortlich. Alle Versuche, Mono vor diesem Punkt zu verwenden, schlagen fehl. ( Hinweis: Aus diesem Grund müssen Typen, die unterklasse Android.App.Application einen (IntPtr, JniHandleOwnership)-Konstruktor bereitstellen müssen, da die Anwendungsinstanz erstellt wird, bevor Mono initialisiert werden kann.)

Sobald die Prozessinitialisierung abgeschlossen ist, wird konsultiert, AndroidManifest.xml um den Klassennamen der Aktivität/des Diensts/usw. zu finden, die gestartet werden sollen. Beispielsweise wird das Attribut "/manifest/application/activity/@android:name " verwendet, um den Namen einer zu ladenden Aktivität zu bestimmen. Für Aktivitäten muss dieser Typ android.app.Activity erben. Der angegebene Typ wird über Class.forName() geladen (was erfordert, dass der Typ ein Java-Typ ist, also die aufrufbaren Wrapper für Android), dann instanziiert. Die Erstellung einer Android Callable Wrapper-Instanz löst die Erstellung einer Instanz des entsprechenden C#-Typs aus. Android ruft dann Activity.onCreate(Bundle) auf, was dazu führt, dass die entsprechende Aktivität.OnCreate(Bundle) aufgerufen wird, und Sie sind bei den Rennen unterwegs.