在MAUI中注册兼容PageRenderer引发堆栈溢出异常
迁移MAUI应用后,尝试复用Xamarin.Forms项目中的现有PageRenderer,页面加载时触发堆栈溢出异常,调用栈如下:
0x39 in Java.Interop.NativeMethods.java_interop_jnienv_call_nonvirtual_void_method_a C#
0x74 in Java.Interop.JniEnvironment.InstanceMethods.CallNonvirtualVoidMethod at /Users/runner/work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/obj/Release/net7.0/JniEnvironment.g.cs:12320,4 C#
0x2D in Java.Interop.JniPeerMembers.JniInstanceMethods.InvokeNonvirtualVoidMethod at /Users/runner/work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods_Invoke.cs:93,6 C#
0x69 in Android.Views.ViewGroup.Layout at /Users/runner/work/1/s/xamarin-android/src/Mono.Android/obj/Release/net7.0/android-33/mcw/Android.Views.ViewGroup.cs:3369,5 C#
0xD4 in Microsoft.Maui.ViewHandlerExtensions.PlatformArrangeHandler at D:\a_work\1\s\src\Core\src\Handlers\ViewHandlerExtensions.Android.cs:133,4 C#
0x2 in Microsoft.Maui.Handlers.ViewHandler<Microsoft.Maui.IView,Android.Views.View>.PlatformArrange at D:\a_work\1\s\src\Core\src\Handlers\View\ViewHandlerOfT.Android.cs:15,4 C#
0x37 in Microsoft.Maui.Controls.Compatibility.RendererToHandlerShim.PlatformArrange at D:\a_work\1\s\src\Compatibility\Core\src\RendererToHandlerShim.Android.cs:51,4 C#
0x1F in Microsoft.Maui.Controls.ContentPage.ArrangeOverride at D:\a_work\1\s\src\Controls\src\Core\HandlerImpl\ContentPage.Impl.cs:22,4 C#
0x2 in Microsoft.Maui.Controls.VisualElement.Microsoft.Maui.IView.Arrange at D:\a_work\1\s\src\Controls\src\Core\HandlerImpl\VisualElement\VisualElement.Impl.cs:111,4 C#
0x4F in Microsoft.Maui.Controls.Compatibility.Platform.Android.PageRenderer.OnLayout at D:\a_work\1\s\src\Compatibility\Core\src\Android\Renderers\PageRenderer.cs:169,4 C#
0x10 in Microsoft.Maui.MauiViewGroup.n_OnLayout_ZIIII at D:\a_work\1\s\src\Core\src\obj\Release\net7.0-android\generated\src\Microsoft.Maui.MauiViewGroup.cs:206,4 C#
0x11 in Android.Runtime.JNINativeWrapper.Wrap_JniMarshal_PPZIIII_V at /Users/runner/work/1/s/xamarin-android/src/Mono.Android/Android.Runtime/JNINativeWrapper.g.cs:429,5 C#
即使注释掉渲染器中的业务代码,问题依然存在。当前使用的渲染器代码如下:
using Android.Content; using System; using Microsoft.Maui.Controls.Platform; using Microsoft.Maui.Controls; using Microsoft.Maui.Controls.Compatibility.Platform.Android; using PetActivityMobile.Core; using Microsoft.Maui.Controls.PlatformConfiguration.AndroidSpecific; namespace PetActivityMobile.Droid.Renderers { public class CreateAccountViewRenderer : PageRenderer { private bool _initialized; private Page _page; public CreateAccountViewRenderer(Context context) : base(context) { } protected override void OnElementChanged(ElementChangedEventArgs<Page> e) { if (!_initialized && e?.NewElement != null) { _initialized = true; _page = e.NewElement; //The appearing event invoke before OnElementChanged called. //So I'm changing the keyboard behavior also here for the first time when page up. App.Current.On<Microsoft.Maui.Controls.PlatformConfiguration.Android>().UseWindowSoftInputModeAdjust(WindowSoftInputModeAdjust.Pan); _page.Appearing -= OnAppearing; _page.Appearing += OnAppearing; _page.Disappearing -= OnDisappearing; _page.Disappearing += OnDisappearing; } } protected override void OnDetachedFromWindow() { _page.Appearing -= OnAppearing; _page.Disappearing -= OnDisappearing; _initialized = false; } private void OnAppearing(object sender, EventArgs e) { App.Current.On<Microsoft.Maui.Controls.PlatformConfiguration.Android>().UseWindowSoftInputModeAdjust(WindowSoftInputModeAdjust.Pan); } private void OnDisappearing(object sender, EventArgs e) { App.Current.On<Microsoft.Maui.Controls.PlatformConfiguration.Android>().UseWindowSoftInputModeAdjust(WindowSoftInputModeAdjust.Resize); } } }
在Android项目中注册渲染器的方式如下:
private void RegisterRenderers(MauiAppBuilder mauiApp) { mauiApp.ConfigureMauiHandlers((handlers) => { handlers.AddCompatibilityRenderer(typeof(MyEntry), typeof(MyRenderer)); handlers.AddCompatibilityRenderer(typeof(CreateAccountView), typeof(CreateAccountViewRenderer)); }); }
其中Entry的渲染器可以正常工作,想问有没有人尝试过在MAUI中复用Xamarin.Forms的PageRenderer?
解决方案
- 放弃直接继承PageRenderer:MAUI的兼容性渲染器机制对Page类型的处理存在递归布局调用问题,直接复用Xamarin.Forms的PageRenderer会触发
OnLayout和Arrange的循环调用,导致堆栈溢出。 - 改用MAUI原生方式处理需求:针对页面的软键盘调整需求,无需使用渲染器,直接在页面代码后台实现:
protected override void OnAppearing() { base.OnAppearing(); App.Current.On<Microsoft.Maui.Controls.PlatformConfiguration.Android>() .UseWindowSoftInputModeAdjust(WindowSoftInputModeAdjust.Pan); } protected override void OnDisappearing() { base.OnDisappearing(); App.Current.On<Microsoft.Maui.Controls.PlatformConfiguration.Android>() .UseWindowSoftInputModeAdjust(WindowSoftInputModeAdjust.Resize); } - 移除PageRenderer的兼容性注册:删除
handlers.AddCompatibilityRenderer(typeof(CreateAccountView), typeof(CreateAccountViewRenderer));这一行,避免与MAUI原生布局流程冲突。 - 检查页面布局:确认
CreateAccountView页面的布局是否存在嵌套过深或约束错误,这类问题会加重堆栈溢出的触发概率。
原因分析
MAUI的RendererToHandlerShim会将旧的Xamarin.Forms渲染器适配为MAUI的Handler,但Page类型的布局流程在MAUI中已重构,PageRenderer的OnLayout方法会触发MAUI的PlatformArrange,而后者又会回调到OnLayout,形成无限递归,最终导致堆栈溢出。Entry等控件的渲染器无此问题,是因为它们的布局逻辑不存在这种递归触发链。
内容的提问来源于stack exchange,提问作者o_w

