Limiti

Poiché le applicazioni in Android richiedono la generazione di tipi proxy Java durante il processo di compilazione, non è possibile generare tutto il codice in fase di esecuzione.

Queste sono le limitazioni di Xamarin.Android rispetto a Mono desktop:

Supporto del linguaggio dinamico limitato

I wrapper chiamabili Android sono necessari ogni volta che il runtime Android deve richiamare il codice gestito. I wrapper chiamabili Android vengono generati in fase di compilazione, in base all'analisi statica di IL. Risultato netto di questo: non è possibile usare i linguaggi dinamici (IronPython, IronRuby e così via) in qualsiasi scenario in cui è necessaria la sottoclasse dei tipi Java (inclusa la sottoclasse indiretta), perché non è possibile estrarre questi tipi dinamici in fase di compilazione per generare i wrapper chiamabili Android necessari.

Supporto per la generazione limitata di Java

I wrapper chiamabili Android devono essere generati per consentire al codice Java di chiamare codice gestito. Per impostazione predefinita, i wrapper chiamabili Android contengono solo (determinati) costruttori e metodi dichiarati che eseguono l'override di un metodo Java virtuale (ad esempio, ha RegisterAttribute) o implementano un metodo di interfaccia Java (l'interfaccia ha Attributeanalogamente ).

Prima della versione 4.1, non è possibile dichiarare metodi aggiuntivi. Con la versione 4.1, gli attributi personalizzati e ExportField possono essere usati per dichiarare i metodi e i Export campi Java all'interno di Android Callable Wrapper.

Costruttori mancanti

I costruttori rimangono difficili, a meno che non ExportAttribute venga usato. L'algoritmo per la generazione di costruttori wrapper chiamabili Android è che verrà generato un costruttore Java se:

  1. Esiste un mapping Java per tutti i tipi di parametro
  2. La classe di base dichiara lo stesso costruttore: questo è necessario perché il wrapper chiamabile Android deve richiamare il costruttore della classe base corrispondente. Non è possibile usare argomenti predefiniti, perché non esiste un modo semplice per determinare quali valori devono essere usati in Java.

Si consideri ad esempio la classe seguente:

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

Anche se questo aspetto è perfettamente logico, il wrapper chiamabile Android risultante nelle build release non conterrà un costruttore predefinito. Di conseguenza, se si tenta di avviare questo servizio ,ad esempio Context.StartService, l'operazione avrà esito negativo:

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 soluzione alternativa consiste nel dichiarare un costruttore predefinito, adornarlo con ExportAttributee impostare ExportAttribute.SuperStringArgument:

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

    // ...
}

Classi C# generiche

Le classi C# generiche sono supportate solo parzialmente. Esistono le limitazioni seguenti:

  • I tipi generici non possono usare [Export] o [ExportField]. Se si tenta di eseguire questa operazione, verrà generato un XA4207 errore.

    public abstract class Parcelable<T> : Java.Lang.Object, IParcelable
    {
        // Invalid; generates XA4207
        [ExportField ("CREATOR")]
        public static IParcelableCreator CreateCreator ()
        {
            ...
    }
    
  • I metodi generici non possono usare [Export] o [ExportField]:

    public class Example : Java.Lang.Object
    {
    
        // Invalid; generates XA4207
        [Export]
        public static void Method<T>(T value)
        {
            ...
        }
    }
    
  • [ExportField] non può essere usato nei metodi che restituiscono void:

    public class Example : Java.Lang.Object
    {
        // Invalid; generates XA4208
        [ExportField ("CREATOR")]
        public static void CreateSomething ()
        {
        }
    }
    
  • Le istanze di tipi generici non devono essere create dal codice Java. Possono essere creati solo in modo sicuro dal codice gestito:

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

Supporto parziale di generics Java

Il supporto dell'associazione di generics Java è limitato. In particolare, i membri di una classe di istanza generica derivata da un'altra classe generica (non creata un'istanza) vengono lasciati esposti come Java.Lang.Object. Ad esempio, il metodo Android.Content.Intent.GetParcelableExtra restituisce Java.Lang.Object. Ciò è dovuto a generics Java cancellati. Esistono alcune classi che non applicano questa limitazione, ma vengono modificate manualmente.