Xamarin应用因FormsEditText缺失构造函数偶发崩溃 仅生产环境触发
根因分析
这个偶发崩溃属于Xamarin.Android生命周期与Xamarin.Forms控件映射的已知兼容问题,触发逻辑如下:
- 生产环境中当应用退到后台、系统剩余内存不足时,Android原生框架会主动回收非前台页面持有的原生
FormsEditText控件实例,但C#层对应的托管包装对象不会同步被GC回收,形成托管对象和原生实例状态不一致的问题 - 当用户切回应用、系统尝试重建被回收的原生控件时,Xamarin运行时会通过反射查找控件类型的
(System.IntPtr, Android.Runtime.JniHandleOwnership)签名构造函数,用来重新绑定C#托管包装和新生的原生实例 - 5.0.0.2401之前版本的Xamarin.Forms中,
FormsEditText类没有将这个必需的构造函数声明为公开访问级别,跨程序集反射查找时会直接抛出MissingMethodException,外层包装后就会抛出Unable to activate instance的NotSupportedException - 由于触发前提是系统达到内存阈值触发原生控件回收,测试环境通常不会模拟高压内存场景,因此无法稳定复现,仅会在生产环境不同机型、不同使用场景下随机触发。
修复方案
按落地成本和修复彻底性排序,可选择以下方案:
- 彻底修复方案:将Xamarin.Forms版本升级至5.0.0.2401及更高正式版本,官方已经在该版本为
FormsEditText及其他存在同类问题的平台控件补全了公开的必需构造函数,从根源上解决反射查找失败的问题 - 兼容修复方案(无法升级大版本时使用):在Android项目中自定义
Entry控件的渲染器,显式补全缺失的构造函数,替换系统默认的FormsEditText实现,参考代码如下:
using Android.Content; using Android.Runtime; using Xamarin.Forms; using Xamarin.Forms.Platform.Android; // 替换Entry默认渲染器 [assembly: ExportRenderer(typeof(Entry), typeof(FixedEntryRenderer))] namespace YourApp.Droid.Renderers { public class FixedEntryRenderer : EntryRenderer { public FixedEntryRenderer(Context context) : base(context) { } protected override FormsEditText CreateNativeControl() { return new FixedFormsEditText(Context); } } public class FixedFormsEditText : FormsEditText { public FixedFormsEditText(Context context) : base(context) { } // 补全Xamarin运行时重建实例必需的公开构造函数 public FixedFormsEditText(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer) { } } }
- 兜底优化方案:在Android项目主Activity中重写
OnTrimMemory方法,当系统内存告警等级达到TrimMemoryModerate及以上时,主动清理非前台页面的控件缓存,降低原生控件被系统单独回收的概率,该方案仅能降低崩溃触发概率,无法彻底解决问题。
内容的提问来源于stack exchange,提问作者MrSpt
相关产品推荐
相关产品推荐

