Limitaciones

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

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

Compatibilidad limitada con el lenguaje dinámico

Los contenedores invocables de Android son necesarios en cualquier momento en que el runtime de Android necesite invocar código administrado. Los contenedores invocables de Android se generan en tiempo de compilación, en función del análisis estático de IL. El resultado neto de esto es que no puede 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 forma de extraer estos tipos dinámicos en tiempo de compilación para generar los contenedores invocables de Android necesarios.

Compatibilidad limitada con la generación de Java

Los contenedores invocables de Android deben generarse para que el código Java llame al código administrado. De manera predeterminada, los contenedores invocables de Android solo contendrán (ciertos) constructores y métodos declarados que invalidan un método de Java virtual (es decir, tiene RegisterAttribute) o implementan 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 Export y ExportField se pueden usar para declarar los métodos y campos de Java en el contenedor invocable de Android.

Constructores faltantes

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

  1. Hay una asignación de Java para todos los tipos de parámetros
  2. La clase base declara el mismo constructor: esto es necesario porque el contenedor invocable de Android debe invocar al constructor de clase base correspondiente. No se pueden usar argumentos predeterminados (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 invocable de Android resultante en las compilaciones de versión no contendrá ningún 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 es declarar un constructor predeterminado, adornarlo con ExportAttribute y establecer el 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:

  • Es posible que los tipos genéricos no usen [Export] o [ExportField. Si intenta hacerlo, se generará un error XA4207.

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

    public class Example : Java.Lang.Object
    {
    
        // Invalid; generates XA4207
        [Export]
        public static void Method<T>(T value)
        {
            ...
        }
    }
    
  • No se puede usar [ExportField] 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 se deben crear 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 particular, los miembros de una clase de instancia genérica derivada de otra clase genérica (que no forma parte de una instancia) se dejan expuestas 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.