Limites

Étant donné que les applications sur Android nécessitent la génération de types de proxy Java pendant le processus de génération, il n’est pas possible de générer tout le code au moment de l’exécution.

Voici les limitations de Xamarin.Android par rapport au mono de bureau :

Prise en charge limitée du langage dynamique

Des wrappers pouvant être appelés Android sont nécessaires chaque fois que le runtime Android a besoin d’appeler du code managé. Les wrappers pouvant être appelé Android sont générés au moment de la compilation, en fonction d’une analyse statique de l’il. Résultat net : vous ne pouvez pas utiliser de langages dynamiques (IronPython, IronRuby, etc.) dans n’importe quel scénario où la sous-classification de types Java est requise (y compris la sous-classification indirecte), car il n’existe aucun moyen d’extraire ces types dynamiques au moment de la compilation pour générer les wrappers pouvant être appelé Android nécessaires.

Prise en charge limitée de la génération Java

Les wrappers Android Callable doivent être générés pour que le code Java appelle du code managé. Par défaut, les wrappers pouvant être appelé Android ne contiennent que (certains) constructeurs et méthodes déclarés qui remplacent une méthode Java virtuelle (c’est-à-dire qu’elle a RegisterAttribute) ou implémentent une méthode d’interface Java (l’interface a Attributeégalement ).

Avant la version 4.1, aucune méthode supplémentaire ne pouvait être déclarée. Avec la version 4.1, les Export attributs personnalisés et ExportField peuvent être utilisés pour déclarer des méthodes et des champs Java dans le wrapper android callable.

Constructeurs manquants

Les constructeurs restent délicats, sauf s’ils sont ExportAttribute utilisés. L’algorithme permettant de générer des constructeurs de wrapper pouvant être appelé Android est qu’un constructeur Java sera émis si :

  1. Il existe un mappage Java pour tous les types de paramètres
  2. La classe de base déclare le même constructeur : cela est obligatoire, car le wrapper pouvant être appelé Android doit appeler le constructeur de classe de base correspondant ; aucun argument par défaut ne peut être utilisé (car il n’existe pas de moyen simple de déterminer les valeurs à utiliser dans Java).

Par exemple, considérons la classe suivante :

[Service]
class MyIntentService : IntentService {
    public MyIntentService (): base ("value")
    {
    }
}

Bien que cela semble parfaitement logique, le wrapper appelant Android résultant dans les builds Release ne contient pas de constructeur par défaut. Par conséquent, si vous tentez de démarrer ce service (par exemple Context.StartService, il échoue :

E/AndroidRuntime(31766): FATAL EXCEPTION: main
E/AndroidRuntime(31766): java.lang.RuntimeException: Unable to instantiate service example.MyIntentService: java.lang.InstantiationException: can't instantiate class example.MyIntentService; no empty constructor
E/AndroidRuntime(31766):        at android.app.ActivityThread.handleCreateService(ActivityThread.java:2347)
E/AndroidRuntime(31766):        at android.app.ActivityThread.access$1600(ActivityThread.java:130)
E/AndroidRuntime(31766):        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1277)
E/AndroidRuntime(31766):        at android.os.Handler.dispatchMessage(Handler.java:99)
E/AndroidRuntime(31766):        at android.os.Looper.loop(Looper.java:137)
E/AndroidRuntime(31766):        at android.app.ActivityThread.main(ActivityThread.java:4745)
E/AndroidRuntime(31766):        at java.lang.reflect.Method.invokeNative(Native Method)
E/AndroidRuntime(31766):        at java.lang.reflect.Method.invoke(Method.java:511)
E/AndroidRuntime(31766):        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
E/AndroidRuntime(31766):        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
E/AndroidRuntime(31766):        at dalvik.system.NativeStart.main(Native Method)
E/AndroidRuntime(31766): Caused by: java.lang.InstantiationException: can't instantiate class example.MyIntentService; no empty constructor
E/AndroidRuntime(31766):        at java.lang.Class.newInstanceImpl(Native Method)
E/AndroidRuntime(31766):        at java.lang.Class.newInstance(Class.java:1319)
E/AndroidRuntime(31766):        at android.app.ActivityThread.handleCreateService(ActivityThread.java:2344)
E/AndroidRuntime(31766):        ... 10 more

La solution de contournement consiste à déclarer un constructeur par défaut, à l’orner avec et ExportAttributeà définir :ExportAttribute.SuperStringArgument

[Service]
class MyIntentService : IntentService {
    [Export (SuperArgumentsString = "\"value\"")]
    public MyIntentService (): base("value")
    {
    }

    // ...
}

Classes C# génériques

Les classes C# génériques ne sont que partiellement prises en charge. Les limites suivantes existent :

  • Les types génériques ne peuvent pas utiliser [Export] ou [ExportField]. Si vous tentez de le faire, une XA4207 erreur est générée.

    public abstract class Parcelable<T> : Java.Lang.Object, IParcelable
    {
        // Invalid; generates XA4207
        [ExportField ("CREATOR")]
        public static IParcelableCreator CreateCreator ()
        {
            ...
    }
    
  • Les méthodes génériques ne peuvent pas utiliser [Export] ou [ExportField]:

    public class Example : Java.Lang.Object
    {
    
        // Invalid; generates XA4207
        [Export]
        public static void Method<T>(T value)
        {
            ...
        }
    }
    
  • [ExportField] ne peut pas être utilisé sur les méthodes qui retournent void:

    public class Example : Java.Lang.Object
    {
        // Invalid; generates XA4208
        [ExportField ("CREATOR")]
        public static void CreateSomething ()
        {
        }
    }
    
  • Les instances de types génériques ne doivent pas être créées à partir de code Java. Ils ne peuvent être créés en toute sécurité qu’à partir de code managé :

    [Activity (Label="Die!", MainLauncher=true)]
    public class BadGenericActivity<T> : Activity
    {
        protected override void OnCreate (Bundle bundle)
        {
            base.OnCreate (bundle);
        }
    }
    

Prise en charge partielle des génériques Java

La prise en charge de la liaison de génériques Java est limitée. En particulier, les membres d’une classe générique instance dérivée d’une autre classe générique (non instanciée) sont exposés en tant que Java.Lang.Object. Par exemple, la méthode Android.Content.Intent.GetParcelableExtra retourne Java.Lang.Object. Cela est dû à des génériques Java effacés. Certaines classes n’appliquent pas cette limitation, mais elles sont ajustées manuellement.