Limitaciones

Dado que las aplicaciones en Android requieren la generación de tipos de proxy de Java durante el proceso de compilación, no es posible generar todo el código en tiempo de ejecución.

Estas son las limitaciones de Xamarin.Android en comparación con el escritorio Mono:

Compatibilidad limitada con lenguaje dinámico

Los contenedores que se pueden llamar de Android son necesarios cada vez que el entorno de ejecución de Android necesita invocar código administrado. Los contenedores a los que se puede llamar de Android se generan en tiempo de compilación, en función del análisis estático de IL. El resultado neto de esto: no se pueden usar lenguajes dinámicos (IronPython, IronRuby, etc.). en cualquier escenario en el que se requiera la subclase de tipos de Java (incluida la subclase indirecta), ya que no hay ninguna manera de extraer estos tipos dinámicos en tiempo de compilación para generar los contenedores que se pueden llamar de Android necesarios.

Compatibilidad limitada con la generación de Java

Los contenedores que se pueden llamar de Android deben generarse para que el código Java llame al código administrado. Deforma predeterminada, los contenedores a los que se puede llamar de Android solo contendrán (determinados) constructores declarados y métodos que invaliden un método de Java virtual (es decir, tiene ) o implementarán un método de interfaz de Java (la interfaz también tiene Attribute ).

Antes de la versión 4.1, no se podía declarar ningún método adicional. Con la versión 4.1, los atributos personalizados y se pueden usar para declarar campos y métodos de Java dentro del contenedor al que se puede llamar de ExportField Android.

Constructores que faltan

Los constructores siguen siendo complicados, a menos ExportAttribute que se utilice . El algoritmo para generar constructores contenedores invocables de Android es que se emitirá un constructor de Java si:

  1. Hay una asignación de Java para todos los tipos de parámetro
  2. La clase base declara el mismo constructor: esto es necesario porque el contenedor al que se puede llamar de Android debe invocar el constructor de clase base correspondiente; no se puede usar ningún argumento predeterminado (ya que no hay ninguna manera fácil de determinar qué valores se deben usar en Java).

Por ejemplo, considere la siguiente clase:

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

Aunque esto parece perfectamente lógico, el contenedor al que se puede llamar de Android resultante en las compilaciones de versión no contendrá un constructor predeterminado. Por lo tanto, si intenta iniciar este servicio (por ejemplo, Context.StartService , se producirá un error:

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 solución alternativa consiste en declarar un constructor predeterminado, adornar con ExportAttribute y establecer ExportAttribute.SuperStringArgument :

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

    // ...
}

Clases genéricas de C#

Las clases genéricas de C# solo se admiten parcialmente. Existen las siguientes limitaciones:

  • Los tipos genéricos no pueden [Export] usar ni [ExportField ]. Al intentar hacerlo, se generará un XA4207 error.

    public abstract class Parcelable<T> : Java.Lang.Object, IParcelable
    {
        // Invalid; generates XA4207
        [ExportField ("CREATOR")]
        public static IParcelableCreator CreateCreator ()
        {
            ...
    }
    
  • Los métodos genéricos no pueden [Export] usar o [ExportField] :

    public class Example : Java.Lang.Object
    {
    
        // Invalid; generates XA4207
        [Export]
        public static void Method<T>(T value)
        {
            ...
        }
    }
    
  • [ExportField] no se puede usar en métodos que devuelven void :

    public class Example : Java.Lang.Object
    {
        // Invalid; generates XA4208
        [ExportField ("CREATOR")]
        public static void CreateSomething ()
        {
        }
    }
    
  • Las instancias de tipos genéricos no deben crearse a partir de código Java. Solo se pueden crear de forma segura a partir de código administrado:

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

Compatibilidad parcial con genéricos de Java

La compatibilidad con enlaces genéricos de Java es limitada. En concreto, los miembros de una clase de instancia genérica que se deriva de otra clase genérica (sin instancias) se quedan expuestos como Java.Lang.Object. Por ejemplo, el método Android.Content.Intent.GetParcelableExtra devuelve Java.Lang.Object. Esto se debe a genéricos de Java borrados. Tenemos algunas clases que no aplican esta limitación, pero se ajustan manualmente.