从 Xamarin.Forms 从 4.1 升级到 4.2+ 后出现神秘的 nullReferenceException

Mystery nullReferenceException occurs after upgrade from Xamarin.Forms from 4.1 to 4.2+

将我的应用程序从 Xamarin.Forms 4.1.0.709244 升级到 4.2.0.709249 (或更新的任何软件包版本)后,以前正常运行的代码现在不再运行并崩溃,但仅在一种特定情况下。

我的应用程序通过 OneSignal 包接收推送通知,预期行为是:

  1. 通知到达并打开
  2. 通知打开处理程序将有效负载保存到Appclass参数并设置Application.Current.MainPage = new MainPage();
  3. MainPage 初始化程序中的逻辑解析有效负载以确定用户需要导航到哪个页面,并为该新页面创建一个 Page 对象。
  4. Detail页面设置为新页面

4.1 下,这按预期工作。点击通知,用户将在所有场景下被带到应用程序中的相应页面。升级到 4.2+ 后,如果应用当前不是 运行,则此方案失败,但如果应用是 运行 并且在前台或在背景。

当它失败时,不是应用加载然后用户被带到正确的页面,而是应用加载但随后只停留在默认主页(好像 Detail 没有被设置使用来自通知负载的信息)。同样,此时如果您点击汉堡包图标,导航菜单会短暂显示,应用程序会立即崩溃,并出现如下所示的空引用异常:

System.NullReferenceException: Object reference not set to an instance of an object at Xamarin.Forms.Platform.Android.AppCompat.Platform.Xamarin.Forms.Platform.Android.IPlatformLayout.OnLayout (System.Boolean changed, System.Int32 l, System.Int32 t, System.Int32 r, System.Int32 b) [0x0002b] in <596751900f1f46919eb25349c2e7053a>:0 at Xamarin.Forms.Platform.Android.PlatformRenderer.OnLayout (System.Boolean changed, System.Int32 l, System.Int32 t, System.Int32 r, System.Int32 b) [0x00025] in <596751900f1f46919eb25349c2e7053a>:0 at Android.Views.ViewGroup.n_OnLayout_ZIIII (System.IntPtr jnienv, System.IntPtr native__this, System.Boolean changed, System.Int32 l, System.Int32 t, System.Int32 r, System.Int32 b) [0x00009] in <21b22bf2aca24508938d2117f4c11761>:0 at (wrapper dynamic-method) Android.Runtime.DynamicMethodNameCounter.28(intptr,intptr,bool,int,int,int,int)

由于这种情况只发生在通过通知操作关闭和启动应用程序时,我无法使用 Visual Studio 调试器来准确识别异常发生的位置.我使用 Microsoft AppCenter 添加了许多事件,这些事件将通过各种方法跟踪进度,然后报告崩溃发生在逻辑流中的哪个位置,但所有预期的步骤都在进行中以完成。正在调用将 Detail 页面设置为新页面的步骤,点击汉堡图标时调用的 isPresentedChanged 处理程序也是 运行 完成。

我已经查看了 4.2 here and while there are a few fixes related to layout of page that might be related to the crash, I get the feeling the crash is more a symptom of the change to the Detail page not happening correctly than a problem I need to solve outright. I have found a few similar-sounding errors reported to the Xamarin team, but they are all closed out as resolved and are not exactly the same problem. See here, here, here, here, and here 的发行说明。

NotificationServices.csOneSignal 的服务扩展;无论通过哪个 userNotificationType,代码现在都会失败) :

public class NotificationServices
{
    public static void HandleNotificationOpened(OSNotificationOpenedResult result)
    {
        string notificationActionId = "HandleMessage";

        OSNotificationPayload payload = result.notification.payload;
        string message = payload.body;
        App.NotificationActionId = notificationActionId;
        App.NotificationData = payload.additionalData;
        Application.Current.MainPage = new MainPage();
    }

    public static Page GetPageFromNotificationData()
    {
        Page ReturnPage;
        ApiServices _apiServices = new ApiServices();  //service to make REST calls to backend system
        user_notification_type userNotificationType = user_notification_type.None;
        int fromUserId = 0;
        int assocId = 0;
        int msgId = 0;
        if (App.NotificationData != null)
        {
            if (App.NotificationData.ContainsKey("assocId"))
            {
                Int32.TryParse(Convert.ToString(App.NotificationData["assocId"]), out assocId);
            }
            if (App.NotificationData.ContainsKey("fromUserId"))
            {
                Int32.TryParse(Convert.ToString(App.NotificationData["fromUserId"]), out fromUserId);
            }
            if (App.NotificationData.ContainsKey("msgId"))
            {
                Int32.TryParse(Convert.ToString(App.NotificationData["msgId"]), out msgId);
            }
            if (App.NotificationData.ContainsKey("userNotificationType"))
            {
                int unInt = 0;
                Int32.TryParse(Convert.ToString(App.NotificationData["userNotificationType"]), out unInt);
                userNotificationType = (user_notification_type)unInt;
            }
        }
        switch (userNotificationType)
        {
            case user_notification_type.ChatMessage:
                TeamBasic tm = new TeamBasic();
                tm.OwnerID = fromUserId;
                tm.OwnerName = _apiServices.GetUserName(fromUserId).Result;
                ReturnPage = new ChatPage(tm);
                break;
            case user_notification_type.None:
                ReturnPage = default(Page);
                break;
            default:
                UserNotification unItem = new UserNotification();
                var unList = _apiServices.GetUserNotifications(assocId, msgId+1, 1).Result;
                unItem = unList[0];
                UserNotificationDetailViewModel undVm = new UserNotificationDetailViewModel(unItem);
                ReturnPage = new UserNotificationDetailPage(undVm);
                break;
        }

        return ReturnPage;
    }
}

Mainpage.xaml.cs:

public partial class MainPage : MasterDetailPage
{
    private IHubServices _hubServices;
    Dictionary<int, NavigationPage> MenuPages = new Dictionary<int, NavigationPage>();
    public MainPage()
    {
        InitializeComponent();

        _hubServices = DependencyService.Get<IHubServices>(); //signalR
        _hubServices.Connect();
        _hubServices.ClearPageCache += ClearPageCache;

        MasterBehavior = MasterBehavior.Popover;
        NavigationPage navPage = (NavigationPage)Detail;

        ConnectionState cs = _hubServices.GetConnectionState().Result;
        if (cs == ConnectionState.Connected)
        {
            var pg = NotificationServices.GetPageFromNotificationData();  //call to get page as specified per notification

            Detail = new NavigationPage(pg); //this is where the Detail page should be getting updated but is acting like it isn't when the app is started by the act of opening the notification
            MenuPages.Add((int)MenuItemType.LogOut, (NavigationPage)Detail);
        }
        else
        {
            App.CheckForNotificationRedirect = true;
            MenuPages.Add((int)MenuItemType.About, (NavigationPage)Detail);
        }

        IsPresentedChanged += (sender, args) =>
        {
            try {
                //anything
            }
            catch (Exception ex) 
            {
                //anything
            }
            //it is after this has completed executing that the app is crashing.  No exception ever occurs in the try/catch
        };  
    }
}

App Center提供的完整转储:

Xamarin Exception Stack: System.NullReferenceException: Object reference not set to an instance of an object at Xamarin.Forms.Platform.Android.AppCompat.Platform.Xamarin.Forms.Platform.Android.IPlatformLayout.OnLayout (System.Boolean changed, System.Int32 l, System.Int32 t, System.Int32 r, System.Int32 b) [0x0002b] in <596751900f1f46919eb25349c2e7053a>:0 at Xamarin.Forms.Platform.Android.PlatformRenderer.OnLayout (System.Boolean changed, System.Int32 l, System.Int32 t, System.Int32 r, System.Int32 b) [0x00025] in <596751900f1f46919eb25349c2e7053a>:0 at Android.Views.ViewGroup.n_OnLayout_ZIIII (System.IntPtr jnienv, System.IntPtr native__this, System.Boolean changed, System.Int32 l, System.Int32 t, System.Int32 r, System.Int32 b) [0x00009] in <21b22bf2aca24508938d2117f4c11761>:0 at (wrapper dynamic-method) Android.Runtime.DynamicMethodNameCounter.28(intptr,intptr,bool,int,int,int,int)

Thread 2:
0   dalvik.system.VMStack.getThreadStackTrace(VMStack.java:-2)
1   java.lang.Thread.getStackTrace(Thread.java:1538)
2   java.lang.Thread.getAllStackTraces(Thread.java:1588)
3   com.microsoft.appcenter.crashes.Crashes.saveUncaughtException(Crashes.java:1093)
4   com.microsoft.appcenter.crashes.WrapperSdkExceptionManager.saveWrapperException(WrapperSdkExceptionManager.java:58)
5   crc643f46942d9dd1fff9.PlatformRenderer.n_onLayout(PlatformRenderer.java:-2)
6   crc643f46942d9dd1fff9.PlatformRenderer.onLayout(PlatformRenderer.java:55)
7   android.view.View.layout(View.java:20740)
8   android.view.ViewGroup.layout(ViewGroup.java:6268)
9   android.widget.RelativeLayout.onLayout(RelativeLayout.java:1084)
10  android.view.View.layout(View.java:20740)
11  android.view.ViewGroup.layout(ViewGroup.java:6268)
12  android.widget.FrameLayout.layoutChildren(FrameLayout.java:323)
13  android.widget.FrameLayout.onLayout(FrameLayout.java:261)
14  android.view.View.layout(View.java:20740)
15  android.view.ViewGroup.layout(ViewGroup.java:6268)
16  android.widget.FrameLayout.layoutChildren(FrameLayout.java:323)
17  android.widget.FrameLayout.onLayout(FrameLayout.java:261)
18  android.view.View.layout(View.java:20740)
19  android.view.ViewGroup.layout(ViewGroup.java:6268)
20  android.widget.FrameLayout.layoutChildren(FrameLayout.java:323)
21  android.widget.FrameLayout.onLayout(FrameLayout.java:261)
22  android.view.View.layout(View.java:20740)
23  android.view.ViewGroup.layout(ViewGroup.java:6268)
24  android.widget.LinearLayout.setChildFrame(LinearLayout.java:1812)
25  android.widget.LinearLayout.layoutVertical(LinearLayout.java:1656)
26  android.widget.LinearLayout.onLayout(LinearLayout.java:1565)
27  android.view.View.layout(View.java:20740)
28  android.view.ViewGroup.layout(ViewGroup.java:6268)
29  android.widget.FrameLayout.layoutChildren(FrameLayout.java:323)
30  android.widget.FrameLayout.onLayout(FrameLayout.java:261)
31  com.android.internal.policy.DecorView.onLayout(DecorView.java:794)
32  android.view.View.layout(View.java:20740)
33  android.view.ViewGroup.layout(ViewGroup.java:6268)
34  android.view.ViewRootImpl.performLayout(ViewRootImpl.java:2970)
35  android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2465)
36  android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1571)
37  android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:7616)
38  android.view.Choreographer$CallbackRecord.run(Choreographer.java:1034)
39  android.view.Choreographer.doCallbacks(Choreographer.java:845)
40  android.view.Choreographer.doFrame(Choreographer.java:780)
41  android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1020)
42  android.os.Handler.handleCallback(Handler.java:873)
43  android.os.Handler.dispatchMessage(Handler.java:99)
44  android.os.Looper.loop(Looper.java:205)
45  android.app.ActivityThread.main(ActivityThread.java:6991)
46  java.lang.reflect.Method.invoke(Method.java:-2)
47  com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
48  com.android.internal.os.ZygoteInit.main(ZygoteInit.java:884)

Thread 41398:
0   java.lang.Object.wait(Object.java:-2)
1   java.lang.Daemons$ReferenceQueueDaemon.runInternal(Daemons.java:178)
2   java.lang.Daemons$Daemon.run(Daemons.java:103)
3   java.lang.Thread.run(Thread.java:764)

Thread 41399:
0   java.lang.Object.wait(Object.java:-2)
1   java.lang.Object.wait(Object.java:422)
2   java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:188)
3   java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:209)
4   java.lang.Daemons$FinalizerDaemon.runInternal(Daemons.java:232)
5   java.lang.Daemons$Daemon.run(Daemons.java:103)
6   java.lang.Thread.run(Thread.java:764)

Thread 41400:
0   java.lang.Object.wait(Object.java:-2)
1   java.lang.Daemons$FinalizerWatchdogDaemon.sleepUntilNeeded(Daemons.java:297)
2   java.lang.Daemons$FinalizerWatchdogDaemon.runInternal(Daemons.java:277)
3   java.lang.Daemons$Daemon.run(Daemons.java:103)
4   java.lang.Thread.run(Thread.java:764)

Thread 41415:
0   java.lang.Object.wait(Object.java:-2)
1   com.android.okhttp.ConnectionPool.run(ConnectionPool.java:103)
2   java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
3   java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
4   java.lang.Thread.run(Thread.java:764)

Thread 41416:
0   android.os.MessageQueue.nativePollOnce(MessageQueue.java:-2)
1   android.os.MessageQueue.next(MessageQueue.java:326)
2   android.os.Looper.loop(Looper.java:170)
3   android.os.HandlerThread.run(HandlerThread.java:65)

Thread 41417:
0   android.os.MessageQueue.nativePollOnce(MessageQueue.java:-2)
1   android.os.MessageQueue.next(MessageQueue.java:326)
2   android.os.Looper.loop(Looper.java:170)
3   android.os.HandlerThread.run(HandlerThread.java:65)

Thread 41419:
0   android.os.MessageQueue.nativePollOnce(MessageQueue.java:-2)
1   android.os.MessageQueue.next(MessageQueue.java:326)
2   android.os.Looper.loop(Looper.java:170)
3   android.os.HandlerThread.run(HandlerThread.java:65)

Thread 41420:
0   java.lang.Object.wait(Object.java:-2)
1   com.android.okhttp.okio.AsyncTimeout.awaitTimeout(AsyncTimeout.java:323)
2   com.android.okhttp.okio.AsyncTimeout.access[=13=]0(AsyncTimeout.java:40)
3   com.android.okhttp.okio.AsyncTimeout$Watchdog.run(AsyncTimeout.java:286)

Thread 41421:
0   android.os.MessageQueue.nativePollOnce(MessageQueue.java:-2)
1   android.os.MessageQueue.next(MessageQueue.java:326)
2   android.os.Looper.loop(Looper.java:170)
3   android.os.HandlerThread.run(HandlerThread.java:65)

Thread 41424:
0   android.os.MessageQueue.nativePollOnce(MessageQueue.java:-2)
1   android.os.MessageQueue.next(MessageQueue.java:326)
2   android.os.Looper.loop(Looper.java:170)
3   android.os.HandlerThread.run(HandlerThread.java:65)

Thread 41425:
0   android.os.MessageQueue.nativePollOnce(MessageQueue.java:-2)
1   android.os.MessageQueue.next(MessageQueue.java:326)
2   android.os.Looper.loop(Looper.java:170)
3   android.os.HandlerThread.run(HandlerThread.java:65)

Thread 41431:
0   android.os.MessageQueue.nativePollOnce(MessageQueue.java:-2)
1   android.os.MessageQueue.next(MessageQueue.java:326)
2   android.os.Looper.loop(Looper.java:170)
3   android.os.HandlerThread.run(HandlerThread.java:65)

Thread 41469:
0   java.lang.Object.wait(Object.java:-2)
1   java.lang.Thread.parkFor$(Thread.java:2137)
2   sun.misc.Unsafe.park(Unsafe.java:358)
3   java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:230)
4   java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2101)
5   java.util.concurrent.LinkedBlockingQueue.poll(LinkedBlockingQueue.java:467)
6   java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1091)
7   java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1152)
8   java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
9   com.google.android.gms.common.util.concurrent.zza.run
10  java.lang.Thread.run(Thread.java:764)

Thread 41470:
0   java.lang.Object.wait(Object.java:-2)
1   java.lang.Thread.parkFor$(Thread.java:2137)
2   sun.misc.Unsafe.park(Unsafe.java:358)
3   java.util.concurrent.locks.LockSupport.park(LockSupport.java:190)
4   java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2059)
5   java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1120)
6   java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:849)
7   java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1092)
8   java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1152)
9   java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
10  com.google.android.gms.common.util.concurrent.zza.run
11  java.lang.Thread.run(Thread.java:764)

Thread 41478:
0   java.lang.Object.wait(Object.java:-2)
1   java.lang.Thread.parkFor$(Thread.java:2137)
2   sun.misc.Unsafe.park(Unsafe.java:358)
3   java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:230)
4   java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2101)
5   java.util.concurrent.LinkedBlockingQueue.poll(LinkedBlockingQueue.java:467)
6   java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1091)
7   java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1152)
8   java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
9   java.lang.Thread.run(Thread.java:764)

Thread 41483:
0   java.lang.Object.wait(Object.java:-2)
1   java.lang.Thread.parkFor$(Thread.java:2137)
2   sun.misc.Unsafe.park(Unsafe.java:358)
3   java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:230)
4   java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2101)
5   java.util.concurrent.LinkedBlockingQueue.poll(LinkedBlockingQueue.java:467)
6   java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1091)
7   java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1152)
8   java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
9   java.lang.Thread.run(Thread.java:764)

Thread 41495:
0   java.lang.Object.wait(Object.java:-2)
1   java.lang.Thread.parkFor$(Thread.java:2137)
2   sun.misc.Unsafe.park(Unsafe.java:358)
3   java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:230)
4   java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2101)
5   java.util.concurrent.LinkedBlockingQueue.poll(LinkedBlockingQueue.java:467)
6   java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1091)
7   java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1152)
8   java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
9   java.lang.Thread.run(Thread.java:764)

在努力追踪触发代码后,确定服务中的以下行 class 需要包装在调用中以确保它在主线程上被调用。在 Xamarin.Forms 4.2.

之前不需要这样做
await Device.InvokeOnMainThreadAsync(() => {
    Application.Current.MainPage = new MainPage();
});