This one is a pretty specific case, so I could just be doing something wrong or have misunderstood how MainPage works on Xamarin Forms.
I have created a very small demo project (using Xamarin's FormsGallery) that demonstrates the behavior. Please find it at my GitHub (https://github.com/dylansturg/XamForms-Broken-Demo) and in an attached zip.
The demo should display a simple page with 2 boxes - a blue and a red. The blue exhibits my expected behavior. The red crashes in the scenario described below.
What I want to do:
Change the MainPage at run time, as the result of user interaction (or any thing else, really). I expect changing the MainPage to change the application's Root, where root is the page on which pressing a hardware back button closes the application.
What I have done:
I used a TapGestureRecognizer + Command to change the MainPage instance of the current Application, as a result of user interaction. I specifically NEED to use a TapGestureRecognizer for my real project. I know that buttons often do this job - please don't ask me to use a button.
The Result 99% of the Time:
Working as expected
The Result 1% of the Time:
On Android 4.0.4 in my emulator (and on Xamarin Test Cloud devices), this crashes the application with a Java NullPointerException inside of the Java view hierarchy (somewhere, somehow, I don't really know). Check out the track trace:
[AndroidRuntime] Shutting down VM
[AndroidRuntime] FATAL EXCEPTION: main
[AndroidRuntime] java.lang.NullPointerException
[AndroidRuntime] at android.view.GestureDetector.onTouchEvent(GestureDetector.java:587)
[AndroidRuntime] at md5b60ffeb829f638581ab2bb9b1a7f4f3f.VisualElementRenderer_1.n_onTouch(Native Method)
[AndroidRuntime] at md5b60ffeb829f638581ab2bb9b1a7f4f3f.VisualElementRenderer_1.onTouch(VisualElementRenderer_1.java:56)
[AndroidRuntime] at android.view.View.dispatchTouchEvent(View.java:5536)
[AndroidRuntime] at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1951)
[AndroidRuntime] at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1712)
[AndroidRuntime] at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1957)
[AndroidRuntime] at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1726)
[AndroidRuntime] at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1957)
[AndroidRuntime] at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1726)
[AndroidRuntime] at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1957)
[AndroidRuntime] at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1726)
[AndroidRuntime] at md5b60ffeb829f638581ab2bb9b1a7f4f3f.PlatformRenderer.n_dispatchTouchEvent(Native Method)
[AndroidRuntime] at md5b60ffeb829f638581ab2bb9b1a7f4f3f.PlatformRenderer.dispatchTouchEvent(PlatformRenderer.java:70)
[AndroidRuntime] at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1957)
[AndroidRuntime] at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1726)
[AndroidRuntime] at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1957)
[AndroidRuntime] at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1726)
[AndroidRuntime] at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1957)
[AndroidRuntime] at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1726)
[AndroidRuntime] at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1957)
[AndroidRuntime] at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1726)
[AndroidRuntime] at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:1912)
[AndroidRuntime] at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1371)
[AndroidRuntime] at android.app.Activity.dispatchTouchEvent(Activity.java:2364)
[AndroidRuntime] at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:1860)
[AndroidRuntime] at android.view.View.dispatchPointerEvent(View.java:5721)
[AndroidRuntime] at android.view.ViewRootImpl.deliverPointerEvent(ViewRootImpl.java:2890)
[AndroidRuntime] at android.view.ViewRootImpl.handleMessage(ViewRootImpl.java:2466)
[AndroidRuntime] at android.view.ViewRootImpl.processInputEvents(ViewRootImpl.java:845)
[AndroidRuntime] at android.view.ViewRootImpl.handleMessage(ViewRootImpl.java:2475)
[AndroidRuntime] at android.os.Handler.dispatchMessage(Handler.java:99)
[AndroidRuntime] at android.os.Looper.loop(Looper.java:137)
[AndroidRuntime] at android.app.ActivityThread.main(ActivityThread.java:4424)
[AndroidRuntime] at java.lang.reflect.Method.invokeNative(Native Method)
[AndroidRuntime] at java.lang.reflect.Method.invoke(Method.java:511)
[AndroidRuntime] at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
[AndroidRuntime] at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
[AndroidRuntime] at dalvik.system.NativeStart.main(Native Method)
What I have Done Round 2:
After a lot of interesting experimentation, I learned that this error does not occur if:
- I change MainPage from a background thread (from Task.Factory.StartNew)
- I use a Button instead of a TapGestureRecognizer (cool but I need a custom totally-not-a-button view to be tapped)
- I use a version of Android > 4.0.4 (or any other OS)
I have included the first point in the demo as "Gesture_WorkCorrectly".