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 Attribute
analogamente ).
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:
- Esiste un mapping Java per tutti i tipi di parametro
- 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 ExportAttribute
e 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 unXA4207
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 restituisconovoid
: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.