Problembehandlung von Bindungen

Wichtig

Wir untersuchen derzeit die Nutzung benutzerdefinierter Bindungen auf der Xamarin-Plattform. Nehmen Sie an dieser Umfrage teil, um zukünftige Entwicklungsarbeiten zu unterstützen.

In diesem Artikel werden mehrere häufige Fehler, die beim Erstellen von Bindungen auftreten können, sowie mögliche Ursachen und empfohlene Möglichkeiten, diese zu beheben, behandelt.

Übersicht

Das Binden einer Android-Bibliotheksdatei (AAR- oder JAR-Datei) ist meistens kompliziert. In der Regel ist ein zusätzlicher Aufwand erforderlich, um Probleme zu beheben, die sich aufgrund der Unterschiede zwischen Java und .NET ergeben. Diese Probleme verhindern, dass Xamarin.Android die Android-Bibliothek bindet, und werden im Buildprotokoll als Fehlermeldungen angezeigt. Diese Anleitung enthält einige Tipps zur Problembehandlung, eine Liste der häufigeren Probleme/Szenarios sowie mögliche Lösungen für eine erfolgreiche Bindung der Android-Bibliothek.

Beim Binden einer vorhandenen Android-Bibliothek müssen folgende Punkte beachtet werden:

  • Die externen Abhängigkeiten für die Bibliothek – Alle Java-Abhängigkeiten, die von der Android-Bibliothek benötigt werden, müssen im Xamarin.Android-Projekt als ReferenceJar oder als EmbeddedReferenceJar enthalten sein.

  • Die Android-API-Ebene, auf die die Android-Bibliothek ausgerichtet ist – Es ist nicht möglich, die Android-API-Ebene zu "downgrade". Stellen Sie sicher, dass das Xamarin.Android-Bindungsprojekt auf die gleiche API-Ebene (oder höher) wie die Android-Bibliothek ausgerichtet ist.

  • Die Version des Android JDK, das zum Verpacken der Android-Bibliothek verwendet wurde – Bindungsfehler können auftreten, wenn die Android-Bibliothek mit einer anderen Version von JDK erstellt wurde als die, die von Xamarin.Android verwendet wird. Kompilieren Sie die Android-Bibliothek möglichst mit derselben JDK-Version neu, die von der jeweiligen Xamarin.Android-Installation verwendet wird.

Der erste Schritt bei der Behebung von Problemen beim Binden einer Xamarin.Android-Bibliothek besteht darin, die MSBuild-Diagnoseausgabe zu aktivieren. Kompilieren Sie das Xamarin.Android-Bindungsprojekt nach dem Aktivieren der Diagnoseausgabe erneut, und überprüfen Sie das Buildprotokoll, um Hinweise zur Ursache des Problems zu finden.

Außerdem kann es hilfreich sein, die Android-Bibliothek zu dekompilieren und die Typen und Methoden zu untersuchen, die Xamarin.Android binden möchte. Dies weiter unten in dieser Anleitung ausführlicher beschrieben.

Dekompilieren einer Android-Bibliothek

Die Überprüfung der Klassen und Methoden der Java-Klassen kann wertvolle Informationen liefern, die beim Binden einer Bibliothek von Nutzen sind. JD-GUI ist ein grafisches Hilfsprogramm, das Java-Quellcode aus den CLASS-Dateien anzeigen kann, die in einer JAR-Datei enthalten sind. Es kann als eigenständige Anwendung oder als Plug-In für IntelliJ oder Eclipse ausgeführt werden.

Um eine Android-Bibliothek zu dekompilieren, öffnen Sie die JAR Datei mit dem Java Decompiler. Wenn es sich bei der Bibliothek um eine AAR-Datei handelt, muss die Datei classes.jar aus der Archivdatei extrahiert werden. Der folgende Beispielscreenshot veranschaulicht die Verwendung von JD-GUI zum Analysieren der Picasso-JAR:

Using the Java Decompiler to analyze picasso-2.5.2.jar

Untersuchen Sie den Quellcode, nachdem Sie die Android-Bibliothek dekompiliert haben. Suchen Sie generell nach:

  • Klassen mit Merkmalen der Verschleierung – Zu den Merkmalen verschleierter Klassen gehören:

    • Der Klassenname enthält ein $, z. B. a$.class.
    • Der Klassenname besteht ausschließlich aus Kleinbuchstaben, z. B. a.class.
  • import Anweisungen für nicht referenzierte Bibliotheken – Identifizieren Sie die nicht referenzierte Bibliothek, und fügen Sie diese Abhängigkeiten dem Xamarin.Android-Bindungsprojekt mit einer Buildaktion von ReferenceJar oder EmbedddedReferenceJar hinzu.

Hinweis

Das Dekompilieren einer Java-Bibliothek ist möglicherweise unzulässig oder unterliegt aufgrund von lokalen Gesetzen oder der Lizenz, unter der die Java-Bibliothek veröffentlicht wurde, rechtlichen Beschränkungen. Nehmen Sie ggf. die Dienste eines spezialisierten Juristen in Anspruch, bevor Sie versuchen, eine Java-Bibliothek zu dekompilieren und den Quellcode zu untersuchen.

Untersuchen von API.XML

Bei der Erstellung eines Bindungsprojekts generiert Xamarin.Android eine XML-Datei namens obj/Debug/api.xml:

Generated api.xml under obj/Debug

Diese Datei enthält eine Liste aller Java-APIs, die Xamarin.Android binden möchte. Der Inhalt dieser Datei kann hilfreich sein, um fehlende Typen oder Methoden bzw. doppelte Bindungen zu ermitteln. Obwohl die Überprüfung dieser Datei mühsam und zeitaufwändig ist, kann sie Hinweise auf mögliche Ursachen für Bindungsprobleme liefern. Beispielsweise kann api.xml zeigen, dass eine Eigenschaft einen ungeeigneten Typ zurückgibt oder zwei Typen mit demselben verwalteten Namen vorhanden sind.

Bekannte Probleme

In diesem Abschnitt werden einige der häufigsten Fehlermeldungen oder Symptome aufgelistet, die beim Binden einer Android-Bibliothek auftreten können.

Problem: Java-Versionskonflikt

Manchmal werden Typen nicht generiert, oder es können unerwartete Abstürze auftreten, da Sie eine andere (neuere oder eine ältere) Version von Java verwenden als die Version, mit der die Bibliothek kompiliert wurde. Kompilieren Sie die Android-Bibliothek mit der gleichen JDK-Version neu, die vom Xamarin.Android-Projekt verwendet wird.

Problem: Mindestens eine Java-Bibliothek ist erforderlich.

Sie erhalten die Fehlermeldung, dass mindestens eine Java-Bibliothek erforderlich ist, obwohl eine JAR-Datei hinzugefügt wurde.

Mögliche Ursachen:

Vergewissern Sie sich, dass der Buildvorgang auf EmbeddedJar festgelegt ist. Da es mehrere Buildvorgänge für JAR-Dateien gibt (z. B. InputJar, EmbeddedJar, ReferenceJar und EmbeddedReferenceJar), kann der Bindungsgenerator nicht automatisch erahnen, welcher standardmäßig verwendet werden soll. Weitere Informationen zu Buildvorgängen finden Sie unter Buildvorgänge.

Problem: Bindungstools können die . JAR-Bibliothek

Der Bindungsbibliotheksgenerator kann die JAR-Bibliothek nicht laden.

Mögliche Ursachen

Einige JAR-Bibliotheken, die Codeverschleierung verwenden (über Tools wie Proguard), können nicht von den Java-Tools geladen werden. Da unser Tool Java-Reflexion und die ASM Byte Code Engineering-Bibliothek verwendet, können die betreffenden abhängigen Tools die verschleierten Bibliotheken im Gegensatz zu Android-Runtimetools ablehnen. Um dieses Problem zu umgehen, können Sie diese Bibliotheken manuell binden, anstatt den Bindungsgenerator zu verwenden.

Problem: Fehlende C#-Typen in generierter Ausgabe.

Die Bindungs-DLL wird zwar erstellt, es fehlen aber einige Java-Typen, oder der generierte C#-Quellcode wird mit der Fehlermeldung, dass Typen fehlen, nicht kompiliert.

Mögliche Ursachen:

Dieser Fehler kann aus verschiedenen Gründen auftreten, die nachstehend aufgelistet sind:

  • Die gebundene Bibliothek kann auf eine zweite Java-Bibliothek verweisen. Wenn die öffentliche API für die gebundene Bibliothek Typen aus der zweiten Bibliothek verwendet, müssen Sie eben falls auf eine verwaltete Bindung für die zweite Bibliothek verweisen.

  • Es ist möglich, dass eine Bibliothek aufgrund von Java-Reflexion eingefügt wurde, was ähnlich wie beim oben beschriebenen Bibliotheksladefehler ein unerwartetes Laden von Metadaten zur Folge hat. Die Xamarin.Android-Tools können diese Situation derzeit nicht lösen. In einem solchen Fall muss die Bibliothek manuell gebunden werden.

  • Es gab einen Fehler in der .NET 4.0-Runtime, sodass erforderliche Assemblys nicht geladen wurden. Dieses Problem wurde in der .NET-Runtime 4.5 behoben.

  • Java ermöglicht das Ableiten einer öffentlichen Klasse von einer nicht öffentlichen Klasse, dies wird aber in .NET nicht unterstützt. Da der Bindungsgenerator keine Bindungen für nicht öffentliche Klassen generiert, können abgeleitete Klassen wie diese nicht ordnungsgemäß generiert werden. Um dieses Problem zu beheben, entfernen Sie entweder den Metadateneintrag für die abgeleiteten Klassen mit „remove-node“ in Metadata.xml, oder korrigieren Sie die Metadaten, die die nicht öffentliche Klasse als öffentlich festlegen. Obwohl die zweite Lösung die Bindung so erstellt, dass der C#-Quellcode erstellt wird, sollte die nicht öffentliche Klasse nicht verwendet werden.

    Zum Beispiel:

    <attr path="/api/package[@name='com.some.package']/class[@name='SomeClass']"
        name="visibility">public</attr>
    
  • Tools, die Java-Bibliotheken verschleiern, können den Xamarin.Android-Bindungsgenerator und seine Fähigkeit zum Generieren von C#-Wrapperklassen beeinträchtigen. Der folgende Codeausschnitt zeigt, wie Metadata.xml aktualisiert werden muss, um die Verschleierung eines Klassennamens aufzuheben:

    <attr path="/api/package[@name='{package_name}']/class[@name='{name}']"
        name="obfuscated">false</attr>
    

Problem: Die generierte C#-Quelle wird aufgrund von Parametertypkonflikten nicht erstellt.

Der generierte C#-Quellcode wird nicht kompiliert. Die Parametertypen der überschriebenen Methode stimmen nicht überein.

Mögliche Ursachen:

Xamarin.Android enthält eine Vielzahl von Java-Feldern, die Enumerationen in den C#-Bindungen zugeordnet sind. Diese können Typinkompatibilitäten in den generierten Bindungen verursachen. Um dieses Problem zu beheben, müssen die vom Bindungsgenerator erstellten Methodensignaturen so geändert werden, dass die Enumerationen verwendet werden. Weitere Informationen finden Sie im Abschnitt zum Korrigieren von Enumerationen.

Problem: NoClassDefFoundError in packaging

Im Paketerstellungsschritt wird java.lang.NoClassDefFoundError ausgelöst.

Mögliche Ursachen:

Der wahrscheinlichste Grund für diesen Fehler besteht darin, dass eine erforderliche Java-Bibliothek zum Anwendungsprojekt (.csproj) hinzugefügt werden muss. JAR-Dateien werden nicht automatisch aufgelöst. Für eine Benutzerassembly, die auf dem Zielgerät oder im Emulator nicht vorhanden ist (z. B. maps.jar für Google Maps), wird nicht immer eine Java-Bibliotheksbindung generiert. Dies gilt nicht für die Unterstützung von Android-Bibliotheksprojekten, da die JAR-Bibliotheksdatei in die Bibliotheks-DLL eingebettet ist.

Problem: Doppelte benutzerdefinierte EventArgs-Typen

Der Buildvorgang schlägt aufgrund doppelter benutzerdefinierter EventArgs-Typen fehl. Die Fehlermeldung sieht in etwa folgendermaßen aus:

error CS0102: The type `Com.Google.Ads.Mediation.DismissScreenEventArgs' already contains a definition for `p0'

Mögliche Ursachen:

Dies liegt daran, dass ein Konflikt zwischen Ereignistypen besteht, die von mehreren Schnittstellenlistenertypen stammen, die Methoden mit identischen Namen aufweisen. Wenn beispielsweise wie im folgenden Beispiel zwei Java-Schnittstellen vorhanden sind, erstellt der Generator DismissScreenEventArgs für MediationBannerListener und MediationInterstitialListener, was zu einem Fehler führt.

// Java:
public interface MediationBannerListener {
    void onDismissScreen(MediationBannerAdapter p0);
}
public interface MediationInterstitialListener {
    void onDismissScreen(MediationInterstitialAdapter p0);
}

Dies ist systembedingt, um sehr lange Namen für Ereignisargumenttypen zu vermeiden. Um diese Konflikte zu vermeiden, ist eine Metadatentransformation erforderlich. Bearbeiten Sie Transforms\Metadata.xml, und fügen Sie ein argsType-Attribut für beide Schnittstellen (oder für die Schnittstellenmethode) hinzu:

<attr path="/api/package[@name='com.google.ads.mediation']/
        interface[@name='MediationBannerListener']/method[@name='onDismissScreen']"
        name="argsType">BannerDismissScreenEventArgs</attr>

<attr path="/api/package[@name='com.google.ads.mediation']/
        interface[@name='MediationInterstitialListener']/method[@name='onDismissScreen']"
        name="argsType">IntersitionalDismissScreenEventArgs</attr>

<attr path="/api/package[@name='android.content']/
        interface[@name='DialogInterface.OnClickListener']"
        name="argsType">DialogClickEventArgs</attr>

Problem: Klasse implementiert keine Schnittstellenmethode

Es wird eine Fehlermeldung mit dem Hinweis erzeugt, dass eine Methode, die für eine Schnittstelle erforderlich ist, die die generierte Klasse implementiert, von der generierten Klasse nicht implementiert wird. Wenn Sie den generierten Code betrachten, sehen Sie jedoch, dass die-Methode implementiert ist.

Hier sehen Sie ein Beispiel für den Fehler:

obj\Debug\generated\src\Oauth.Signpost.Basic.HttpURLConnectionRequestAdapter.cs(8,23):
error CS0738: 'Oauth.Signpost.Basic.HttpURLConnectionRequestAdapter' does not
implement interface member 'Oauth.Signpost.Http.IHttpRequest.Unwrap()'.
'Oauth.Signpost.Basic.HttpURLConnectionRequestAdapter.Unwrap()' cannot implement
'Oauth.Signpost.Http.IHttpRequest.Unwrap()' because it does not have the matching
return type of 'Java.Lang.Object'

Mögliche Ursachen:

Dieses Problem tritt bei der Bindung von Java-Methoden mit kovarianten Rückgabetypen auf. In diesem Beispiel muss die Methode Oauth.Signpost.Http.IHttpRequest.UnWrap() den Typ Java.Lang.Object zurückgeben. Die Methode Oauth.Signpost.Basic.HttpURLConnectionRequestAdapter.UnWrap() gibt jedoch den Typ HttpURLConnection zurück. Es gibt zwei Möglichkeiten, dieses Problem zu beheben:

  • Fügen Sie eine partielle Klassendeklaration für HttpURLConnectionRequestAdapter hinzu, und implementieren Sie IHttpRequest.Unwrap()explizit:

    namespace Oauth.Signpost.Basic {
        partial class HttpURLConnectionRequestAdapter {
            Java.Lang.Object OauthSignpost.Http.IHttpRequest.Unwrap() {
                return Unwrap();
            }
        }
    }
    
  • Entfernen Sie die Kovarianz aus dem generierten C#-Code. Dazu muss die folgende Transformation in Transforms\Metadata.xml hinzugefügt werden, die bewirkt, dass der generierte C#-Code den Rückgabetyp Java.Lang.Objectaufweist:

    <attr
        path="/api/package[@name='oauth.signpost.basic']/class[@name='HttpURLConnectionRequestAdapter']/method[@name='unwrap']"
        name="managedReturn">Java.Lang.Object
    </attr>
    

Problem: Namenskonflikte in inneren Klassen / Eigenschaften

In Konflikt stehende Sichtbarkeit für geerbte Objekte.

In Java muss eine abgeleitete Klasse nicht die gleiche Sichtbarkeit wie das zugehörige übergeordnete Element aufweisen. Java löst dies automatisch. In C# muss dies explizit erfolgen. Sie müssen also sicherstellen, dass alle Klassen in der Hierarchie die entsprechende Sichtbarkeit aufweisen. Im folgenden Beispiel wird gezeigt, wie ein Java-Paketname von com.evernote.android.job in Evernote.AndroidJob geändert wird:

<!-- Change the visibility of a class -->
<attr path="/api/package[@name='namespace']/class[@name='ClassName']" name="visibility">public</attr>

<!-- Change the visibility of a method -->
<attr path="/api/package[@name='namespace']/class[@name='ClassName']/method[@name='MethodName']" name="visibility">public</attr>

Problem: Eine für die Bindung erforderliche Bibliothek wird nicht geladen.

Einige Bindungsprojekte können auch von Funktionen in einer SO-Bibliothek. Es ist möglich, dass Xamarin.Android die SO-Bibliothek nicht automatisch lädt. Wenn der umschlossene Java-Code ausgeführt wird, schlägt Xamarin.Android den JNI-Aufruf und die Fehlermeldung java.lang.UnsatisfiedLinkError: Native Methode nicht gefunden: wird in der Abmeldung für die Anwendung angezeigt.

Diese Problem kann behoben werden, indem die SO-Bibliothek mit einem Aufruf von Java.Lang.JavaSystem.LoadLibrary manuell geladen wird. Wenn ein Xamarin.Android-Projekt beispielsweise über die freigegebene Bibliothek libpocketsphinx_jni.so verfügt, die im Bindungsprojekt mit dem Buildvorgang EmbeddedNativeLibrary enthalten ist, lädt der folgende Codeausschnitt (der vor der Verwendung der freigegebenen Bibliothek ausgeführt wird) die SO-Bibliothek:

Java.Lang.JavaSystem.LoadLibrary("pocketsphinx_jni");

Zusammenfassung

In diesem Artikel wurden einige häufig auftretende Probleme im Zusammenhang mit Java-Bindungen aufgelistet und deren Behebung erläutert.