限制
由于 Android 上的应用程序在生成过程中需要生成 Java 代理类型,因此无法在运行时生成所有代码。
与桌面 Mono 相比,Xamarin.iOS 存在以下限制:
有限的动态语言支持
Android 运行时每次需要调用托管代码时都需要 Android 可调用包装器。 Android 可调用包装器是在编译时基于 IL 的静态分析生成的。 最终结果是:在需要进行 Java 类型子类化(包括间接子类化)的任何场景中,你都无法使用动态语言(IronPython、IronRuby,等等),因为在编译时无法提取这些动态类型来生成所需的 Android 可调用包装器。
有限的 Java 生成支持
要想让 Java 代码调用托管代码,需要生成 Android 可调用包装器。 默认情况下,Android 可调用包装器将仅包含(某些)已声明的构造函数和方法,这些构造函数和方法替代虚拟 Java 方法(即它具有 RegisterAttribute
)或实现 Java 接口方法(接口同样具有 Attribute
)。
在 4.1 版本之前,无法声明其他方法。 在 4.1 版本中,可以使用 Export
和 ExportField
自定义属性来声明 Android 可调用包装器中的 Java 方法和字段。
缺少构造函数
构造函数仍然很棘手,除非使用 ExportAttribute
。 用于生成 Android 可调用包装器构造函数的算法是在以下情况下发出 Java 构造函数:
- 所有参数类型都有一个 Java 映射
- 基类声明相同的构造函数 - 这是必需的,因为 Android 可调用包装器必须调用相应的基类构造函数;不能使用默认参数(因为没有简单的方法来确定应在 Java 中使用哪些值)。
例如,考虑以下 类:
[Service]
class MyIntentService : IntentService {
public MyIntentService (): base ("value")
{
}
}
虽然这看起来完全合乎逻辑,但发布版本中生成的 Android 可调用包装器将不包含默认构造函数。 因此,如果尝试启动此服务(例如 Context.StartService
,它将失败:
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
解决方法是声明一个默认构造函数,使用 ExportAttribute
对其进行装饰,并设置 ExportAttribute.SuperStringArgument
:
[Service]
class MyIntentService : IntentService {
[Export (SuperArgumentsString = "\"value\"")]
public MyIntentService (): base("value")
{
}
// ...
}
泛型 C# 类
泛型 C# 类仅部分受支持。 存在以下限制:
泛型类型不能使用
[Export]
或[ExportField
]。 尝试这样做将生成XA4207
错误。public abstract class Parcelable<T> : Java.Lang.Object, IParcelable { // Invalid; generates XA4207 [ExportField ("CREATOR")] public static IParcelableCreator CreateCreator () { ... }
泛型方法不能使用
[Export]
或[ExportField]
:public class Example : Java.Lang.Object { // Invalid; generates XA4207 [Export] public static void Method<T>(T value) { ... } }
[ExportField]
不能用于将返回void
的方法:public class Example : Java.Lang.Object { // Invalid; generates XA4208 [ExportField ("CREATOR")] public static void CreateSomething () { } }
不能从 Java 代码创建泛型类型的实例。 只能从托管代码安全地创建它们:
[Activity (Label="Die!", MainLauncher=true)] public class BadGenericActivity<T> : Activity { protected override void OnCreate (Bundle bundle) { base.OnCreate (bundle); } }
不完整的 Java 泛型支持
Java 泛型绑定支持有限。 特别是,派生自另一个泛型(非实例化)类的泛型实例类中的成员将公开为 Java.Lang.Object。 例如,Android.Content.Intent.GetParcelableExtra 方法返回 Java.Lang.Object。 这是因为擦除了 Java 泛型。 我们有一些不存在此限制的类,但它们是手动调整的。