Partager via


Résolution des problèmes de liaisons

Important

Nous étudions actuellement l’utilisation des liaisons personnalisées sur la plateforme Xamarin. Prenez cette enquête pour informer les futurs efforts de développement.

Cet article récapitule les erreurs courantes serveur qui peuvent se produire lors de la génération de liaisons, ainsi que les causes possibles et les méthodes suggérées pour les résoudre.

Vue d’ensemble

La liaison d’une bibliothèque Android (un fichier .aar ou un .jar) est rarement une affaire simple ; il nécessite généralement des efforts supplémentaires pour atténuer les problèmes résultant des différences entre Java et .NET. Ces problèmes empêchent Xamarin.Android de lier la bibliothèque Android et de se présenter en tant que messages d’erreur dans le journal de génération. Ce guide fournit des conseils pour résoudre les problèmes, répertorier certains des problèmes/scénarios les plus courants et fournir des solutions possibles pour lier correctement la bibliothèque Android.

Lors de la liaison d’une bibliothèque Android existante, il est nécessaire de garder à l’esprit les points suivants :

  • Dépendances externes pour la bibliothèque : toutes les dépendances Java requises par la bibliothèque Android doivent être incluses dans le projet Xamarin.Android en tant que ReferenceJar ou en tant que EmbeddedReferenceJar.

  • Niveau de l’API Android ciblant la bibliothèque Android : il n’est pas possible de « rétrograder » le niveau de l’API Android ; assurez-vous que le projet de liaison Xamarin.Android cible le même niveau d’API (ou supérieur) que la bibliothèque Android.

  • La version du JDK Android utilisé pour empaqueter la bibliothèque Android : des erreurs de liaison peuvent se produire si la bibliothèque Android a été créée avec une version différente de JDK que celle utilisée par Xamarin.Android. Si possible, recompilez la bibliothèque Android à l’aide de la même version du JDK que celle utilisée par votre installation de Xamarin.Android.

La première étape de résolution des problèmes liés à la liaison d’une bibliothèque Xamarin.Android consiste à activer la sortie MSBuild de diagnostic. Après avoir activé la sortie de diagnostic, régénérez le projet de liaison Xamarin.Android et examinez le journal de build pour rechercher des indices sur la cause du problème.

Il peut également être utile de décompiler la bibliothèque Android et d’examiner les types et méthodes que Xamarin.Android essaie de lier. Ceci est abordé plus en détail plus loin dans ce guide.

Décompilation d’une bibliothèque Android

L’inspection des classes et des méthodes des classes Java peut fournir des informations précieuses qui vous aideront à lier une bibliothèque. JD-GUI est un utilitaire graphique qui peut afficher du code source Java à partir des fichiers CLASS contenus dans un fichier JAR. Il peut être exécuté en tant qu’application autonome ou en tant que plug-in pour IntelliJ ou Eclipse.

Pour décompiler une bibliothèque Android, ouvrez le fichier . Fichier JAR avec le décompileur Java. Si la bibliothèque est un . Fichier AAR , il est nécessaire d’extraire le fichier classes.jar du fichier d’archivage. Voici un exemple de capture d’écran de l’utilisation de JD-GUI pour analyser le fichier JAR Picasso :

Using the Java Decompiler to analyze picasso-2.5.2.jar

Une fois que vous avez décompilé la bibliothèque Android, examinez le code source. En général, recherchez :

  • Les classes qui ont des caractéristiques d’obfuscation : les caractéristiques des classes obfuscatées sont les suivantes :

    • Le nom de la classe comprend un $, c’est-à-dire un$.class
    • Le nom de la classe est entièrement compromis de caractères minuscules, c’est-à-dire a.class
  • import instructions pour les bibliothèques non référencées : identifiez la bibliothèque non référencée et ajoutez ces dépendances au projet de liaison Xamarin.Android avec une action de build de ReferenceJar ou EmbedddedReferenceJar.

Remarque

La décomposition d’une bibliothèque Java peut être interdite ou soumise à des restrictions légales en fonction des lois locales ou de la licence sous laquelle la bibliothèque Java a été publiée. Si nécessaire, inscrivez les services d’un professionnel juridique avant de tenter de décompiler une bibliothèque Java et d’inspecter le code source.

Inspecter API.XML

Dans le cadre de la création d’un projet de liaison, Xamarin.Android génère un nom de fichier XML obj/Debug/api.xml :

Generated api.xml under obj/Debug

Ce fichier fournit la liste de toutes les API Java que Xamarin.Android essaie de lier. Le contenu de ce fichier peut aider à identifier les types ou méthodes manquants, la liaison en double. Bien que l’inspection de ce fichier soit fastidieuse et fastidieuse, elle peut fournir des indices sur ce qui peut entraîner des problèmes de liaison. Par exemple, api.xml peut révéler qu’une propriété retourne un type inapproprié ou qu’il existe deux types qui partagent le même nom managé.

Problèmes connus

Cette section répertorie quelques-uns des messages d’erreur courants ou symptômes que j’ai rencontrés lors de la tentative de liaison d’une bibliothèque Android.

Problème : Incompatibilité de version Java

Parfois, les types ne seront pas générés ou des incidents inattendus peuvent se produire, car vous utilisez une version plus récente ou antérieure de Java par rapport à ce que la bibliothèque a été compilée avec. Recompilez la bibliothèque Android avec la même version du JDK que votre projet Xamarin.Android utilise.

Problème : Au moins une bibliothèque Java est requise

Vous recevez l’erreur « au moins une bibliothèque Java est requise », même si un . JAR a été ajouté.

Causes possibles :

Vérifiez que l’action de génération est définie sur EmbeddedJar. Étant donné qu’il existe plusieurs actions de génération pour . Les fichiers JAR (tels que InputJar, ReferenceJarEmbeddedJaret EmbeddedReferenceJar), le générateur de liaisons ne peut pas deviner automatiquement celui à utiliser par défaut. Pour plus d’informations sur les actions de génération, consultez Actions de génération.

Problème : les outils de liaison ne peuvent pas charger le . Bibliothèque JAR

Le générateur de bibliothèque de liaisons ne parvient pas à charger le fichier . Bibliothèque JAR.

Causes possibles

Certains. Les bibliothèques JAR qui utilisent l’obfuscation du code (via des outils tels que Proguard) ne peuvent pas être chargées par les outils Java. Étant donné que notre outil utilise la réflexion Java et la bibliothèque d’ingénierie de code d’octet ASM, ces outils dépendants peuvent rejeter les bibliothèques obfuscatées alors que les outils d’exécution Android peuvent passer. La solution de contournement consiste à lier manuellement ces bibliothèques au lieu d’utiliser le générateur de liaisons.

Problème : types C# manquants dans la sortie générée.

La liaison .dll génère mais manque certains types Java, ou la source C# générée ne génère pas en raison d’une erreur indiquant qu’il manque des types.

Causes possibles :

Cette erreur peut se produire en raison de plusieurs raisons, comme indiqué ci-dessous :

  • La bibliothèque liée peut référencer une deuxième bibliothèque Java. Si l’API publique de la bibliothèque liée utilise des types de la deuxième bibliothèque, vous devez également référencer une liaison managée pour la deuxième bibliothèque.

  • Il est possible qu’une bibliothèque ait été injectée en raison de la réflexion Java, similaire à la raison de l’erreur de chargement de la bibliothèque ci-dessus, ce qui entraîne le chargement inattendu des métadonnées. Les outils de Xamarin.Android ne peuvent pas résoudre cette situation actuellement. Dans ce cas, la bibliothèque doit être liée manuellement.

  • Un bogue s’est produit dans le runtime .NET 4.0 qui n’a pas pu charger des assemblys lorsqu’il doit avoir. Ce problème a été résolu dans le runtime .NET 4.5.

  • Java autorise la dérivation d’une classe publique à partir d’une classe non publique, mais elle n’est pas prise en charge dans .NET. Étant donné que le générateur de liaisons ne génère pas de liaisons pour les classes non publiques, les classes dérivées telles que celles-ci ne peuvent pas être générées correctement. Pour résoudre ce problème, supprimez l’entrée de métadonnées pour ces classes dérivées à l’aide du nœud de suppression dans Metadata.xml, ou corrigez les métadonnées qui rendent la classe non publique publique. Bien que la dernière solution crée la liaison afin que la source C# génère, la classe non publique ne doit pas être utilisée.

    Par exemple :

    <attr path="/api/package[@name='com.some.package']/class[@name='SomeClass']"
        name="visibility">public</attr>
    
  • Les outils qui obfusent les bibliothèques Java peuvent interférer avec le générateur de liaisons Xamarin.Android et sa capacité à générer des classes wrapper C#. L’extrait de code suivant montre comment mettre à jour Metadata.xml pour annuler l’objet d’un nom de classe :

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

Problème : la source C# générée ne génère pas en raison d’une incompatibilité de type de paramètre

La source C# générée ne génère pas. Les types de paramètres de la méthode substituée ne correspondent pas.

Causes possibles :

Xamarin.Android inclut divers champs Java mappés à des énumérations dans les liaisons C#. Celles-ci peuvent entraîner des incompatibilités de type dans les liaisons générées. Pour résoudre ce problème, les signatures de méthode créées à partir du générateur de liaison doivent être modifiées pour utiliser les énumérations. Pour plus d’informations, consultez Correction des énumérations.

Problème : NoClassDefFoundError dans l’empaquetage

java.lang.NoClassDefFoundError est levée à l’étape d’empaquetage.

Causes possibles :

La raison la plus probable de cette erreur est qu’une bibliothèque Java obligatoire doit être ajoutée au projet d’application (.csproj). . Les fichiers JAR ne sont pas résolus automatiquement. Une liaison de bibliothèque Java n’est pas toujours générée sur un assembly utilisateur qui n’existe pas dans l’appareil ou l’émulateur cible (par exemple, Google Cartes maps.jar). Ce n’est pas le cas pour la prise en charge du projet Bibliothèque Android, car la bibliothèque . JAR est incorporé dans la dll de bibliothèque.

Problème : Types EventArgs personnalisés en double

La génération échoue en raison de types EventArgs personnalisés en double. Une erreur semblable à celle-ci se produit :

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

Causes possibles :

Cela est dû au fait qu’il existe un conflit entre les types d’événements qui proviennent de plusieurs types d’interface « écouteur » qui partagent des méthodes ayant des noms identiques. Par exemple, s’il existe deux interfaces Java comme indiqué dans l’exemple ci-dessous, le générateur crée DismissScreenEventArgs à la fois MediationBannerListener et MediationInterstitialListenergénère l’erreur.

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

Cela est par conception afin que les noms longs sur les types d’arguments d’événement soient évités. Pour éviter ces conflits, certaines transformations de métadonnées sont requises. Modifiez transformations\Metadata.xml et ajoutez un attribut sur l’une argsType des interfaces (ou sur la méthode d’interface) :

<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>

Problème : la classe n’implémente pas la méthode d’interface

Un message d’erreur est généré indiquant qu’une classe générée n’implémente pas une méthode requise pour une interface que la classe générée implémente. Toutefois, en examinant le code généré, vous pouvez voir que la méthode est implémentée.

Voici un exemple d’erreur :

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'

Causes possibles :

Il s’agit d’un problème qui se produit avec des méthodes Java de liaison avec des types de retour covariants. Dans cet exemple, la méthode Oauth.Signpost.Http.IHttpRequest.UnWrap() doit retourner Java.Lang.Object. Toutefois, la méthode Oauth.Signpost.Basic.HttpURLConnectionRequestAdapter.UnWrap() a un type de retour de HttpURLConnection. Il existe deux façons de résoudre ce problème :

  • Ajoutez une déclaration de classe partielle pour HttpURLConnectionRequestAdapter et implémentez IHttpRequest.Unwrap()explicitement :

    namespace Oauth.Signpost.Basic {
        partial class HttpURLConnectionRequestAdapter {
            Java.Lang.Object OauthSignpost.Http.IHttpRequest.Unwrap() {
                return Unwrap();
            }
        }
    }
    
  • Supprimez la covariance du code C# généré. Cela implique l’ajout de la transformation suivante à Transforms\Metadata.xml qui entraîne l’obtention du code C# généré par un type de retour :Java.Lang.Object

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

Problème : Collisions de noms sur les classes internes / Propriétés

Visibilité en conflit sur les objets hérités.

En Java, il n’est pas nécessaire qu’une classe dérivée ait la même visibilité que son parent. Java va juste corriger cela pour vous. En C#, qui doit être explicite, vous devez donc vous assurer que toutes les classes de la hiérarchie ont la visibilité appropriée. L’exemple suivant montre comment remplacer un nom de package Java par com.evernote.android.jobEvernote.AndroidJob:

<!-- 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>

Problème : une bibliothèque .so requise par la liaison n’est pas chargée

Certains projets de liaison peuvent également dépendre des fonctionnalités d’une bibliothèque .so . Il est possible que Xamarin.Android ne charge pas automatiquement la bibliothèque .so . Lorsque le code Java encapsulé s’exécute, Xamarin.Android ne parvient pas à effectuer l’appel JNI et le message d’erreur java.lang.UnsatisfiedLinkError : la méthode native est introuvable : apparaît dans la déconnexion de l’application.

Le correctif de ce problème consiste à charger manuellement la bibliothèque .so avec un appel à Java.Lang.JavaSystem.LoadLibrary. Par exemple, en supposant qu’un projet Xamarin.Android a une bibliothèque partagée libpocketsphinx_jni.so inclus dans le projet de liaison avec une action de génération de EmbeddedNativeLibrary, l’extrait de code suivant (exécuté avant d’utiliser la bibliothèque partagée) charge la bibliothèque .so :

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

Résumé

Dans cet article, nous avons répertorié les problèmes de résolution courants liés aux liaisons Java et expliqué comment les résoudre.