Xamarin.Forms.Platform.Android.DatePickerRenderer exception

Created on 26 Mar 2018  路  8Comments  路  Source: xamarin/Xamarin.Forms

Description

Internal problem with Xamarin.Forms.Platform.Android.DatePickerRenderer

Steps to Reproduce

  1. Use a Xamarin.Forms.Platform.Android.DatePickerRenderer

Expected Behavior

no exception in logs

Actual Behavior

exception in logs

Basic Information

Seeing this internal exception in App Center logs:

Android: 6.0.1 Android Build: MMB29M Manufacturer: ZTE Model: Z981 CrashReporter Key: Start Date: 2018-03-01T02:42:19.427Z Date: 2018-03-01T02:55:13.063Z

Xamarin Exception Stack: System.NullReferenceException: Object reference not set to an instance of an object at Xamarin.Forms.Platform.Android.DatePickerRenderer+<>c_DisplayClass10_0.b0 (System.Object o, Android.App.DatePickerDialog+DateSetEventArgs e) <0xdd16dc34 + 0x00120> in <173f39d71f0d4d928f5bbea42e96ffa8>:0 at Android.App.DatePickerDialog+IOnDateSetListenerImplementor.OnDateSet (Android.Widget.DatePicker view, System.Int32 year, System.Int32 month, System.Int32 dayOfMonth) <0xdcf1a6b8 + 0x0005f> in <1eccbdadbbb9494cb91c49c1020f3f06>:0 at Android.App.DatePickerDialog+IOnDateSetListenerInvoker.n_OnDateSet_Landroid_widget_DatePicker_III (System.IntPtr jnienv, System.IntPtr native_this, System.IntPtr native_view, System.Int32 year, System.Int32 month, System.Int32 dayOfMonth) <0xdcf1a2f8 + 0x0009b> in <1eccbdadbbb9494cb91c49c1020f3f06>:0 at (wrapper dynamic-method) System.Object:856cb8c3-3263-4a86-9f71-1508a1115bd7 (intptr,intptr,intptr,int,int,int)

Thread 1: 0 dalvik.system.VMStack.getThreadStackTrace(VMStack.java:-2) 1 java.lang.Thread.getStackTrace(Thread.java:580) 2 java.lang.Thread.getAllStackTraces(Thread.java:522) 3 com.microsoft.appcenter.crashes.Crashes.saveUncaughtException(Crashes.java:925) 4 com.microsoft.appcenter.crashes.WrapperSdkExceptionManager.saveWrapperException(WrapperSdkExceptionManager.java:50) 5 mono.android.app.DatePickerDialog_OnDateSetListenerImplementor.n_onDateSet(DatePickerDialog_OnDateSetListenerImplementor.java:-2) 6 mono.android.app.DatePickerDialog_OnDateSetListenerImplementor.onDateSet(DatePickerDialog_OnDateSetListenerImplementor.java:30) 7 android.app.DatePickerDialog.onClick(DatePickerDialog.java:137) 8 com.android.internal.app.AlertController$ButtonHandler.handleMessage(AlertController.java:164) 9 android.os.Handler.dispatchMessage(Handler.java:102) 10 android.os.Looper.loop(Looper.java:171) 11 android.app.ActivityThread.main(ActivityThread.java:5417) 12 java.lang.reflect.Method.invoke(Method.java:-2) 13 com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) 14 com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)

  • Version with issue:
  • Last known good version:
  • IDE: VS Mac
  • Platform Target Frameworks:

    • Android: 6.1
  • Android Support Library Version:

  • Nuget Packages:
  • Affected Devices:
4 Android bug

Most helpful comment

Temporary hack:

public class ExtendedDatePickerRenderer : DatePickerRenderer, AView.IOnClickListener
{
    /// <summary>
    /// The semaphore used to prevent multiple datepickers to be opened.
    /// https://github.com/xamarin/Xamarin.Forms/issues/2191
    /// </summary>
    private readonly SemaphoreSlim _clickSemaphore;

    public ExtendedDatePickerRenderer(Android.Content.Context context) : base(context)
    {
        _clickSemaphore = new SemaphoreSlim(1);
    }

    protected override void OnElementChanged(ElementChangedEventArgs<DatePicker> e)
    {
        base.OnElementChanged(e);

        if (Control != null)
        {
            Control.SetOnClickListener(this);
        }
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            Control.SetOnClickListener(null);
        }
        base.Dispose(disposing);
    }

    public void OnClick(AView v)
    {
        if (!_clickSemaphore.Wait(0))
        {
            // Double-tap, ignoring...
            return;
        }

        if (v.Tag is DatePickerRenderer renderer)
        {
            renderer.InvokeMethod("OnTextFieldClicked");
            Task.Delay(500) // 500 ms should be enough
                .ContinueWith((obj) =>
                {
                    _clickSemaphore.Release();
                });
        }
        else
        {
            _clickSemaphore.Release();
        }
    }
}

public static class ObjectExtensions
{
    public static object InvokeMethod(this object obj, string methodName, params object[] methodParams)
    {
        var methodParamTypes = methodParams?.Select(p => p.GetType()).ToArray() ?? new Type[] { };
        var bindingFlags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static;
        MethodInfo method = null;
        var type = obj.GetType();
        while (method == null && type != null)
        {
            method = type.GetMethod(methodName, bindingFlags, Type.DefaultBinder, methodParamTypes, null);
            type = type.BaseType;
        }

        return method?.Invoke(obj, methodParams);
    }
}

All 8 comments

Temporary hack:

public class ExtendedDatePickerRenderer : DatePickerRenderer, AView.IOnClickListener
{
    /// <summary>
    /// The semaphore used to prevent multiple datepickers to be opened.
    /// https://github.com/xamarin/Xamarin.Forms/issues/2191
    /// </summary>
    private readonly SemaphoreSlim _clickSemaphore;

    public ExtendedDatePickerRenderer(Android.Content.Context context) : base(context)
    {
        _clickSemaphore = new SemaphoreSlim(1);
    }

    protected override void OnElementChanged(ElementChangedEventArgs<DatePicker> e)
    {
        base.OnElementChanged(e);

        if (Control != null)
        {
            Control.SetOnClickListener(this);
        }
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            Control.SetOnClickListener(null);
        }
        base.Dispose(disposing);
    }

    public void OnClick(AView v)
    {
        if (!_clickSemaphore.Wait(0))
        {
            // Double-tap, ignoring...
            return;
        }

        if (v.Tag is DatePickerRenderer renderer)
        {
            renderer.InvokeMethod("OnTextFieldClicked");
            Task.Delay(500) // 500 ms should be enough
                .ContinueWith((obj) =>
                {
                    _clickSemaphore.Release();
                });
        }
        else
        {
            _clickSemaphore.Release();
        }
    }
}

public static class ObjectExtensions
{
    public static object InvokeMethod(this object obj, string methodName, params object[] methodParams)
    {
        var methodParamTypes = methodParams?.Select(p => p.GetType()).ToArray() ?? new Type[] { };
        var bindingFlags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static;
        MethodInfo method = null;
        var type = obj.GetType();
        while (method == null && type != null)
        {
            method = type.GetMethod(methodName, bindingFlags, Type.DefaultBinder, methodParamTypes, null);
            type = type.BaseType;
        }

        return method?.Invoke(obj, methodParams);
    }
}

@lassana Thanks very much for the workaround!

I also had to apply the same workaround for the TimePickerRenderer which experiences the same issue.

All the code is the same except
renderer.InvokeMethod("OnTextFieldClicked");
which became
renderer.InvokeMethod("OnClick");
for the TimePickerRenderer

@lassana What is AView in your workaround?

@bastianbecker21 android.view.view

Repros on 3.1 using the project attached to https://bugzilla.xamarin.com/show_bug.cgi?id=53656

@hartez
image

You have already fixed it and https://github.com/xamarin/Xamarin.Forms/issues/2927
Probably they can be closed ?

Maybe it's still needed to unsubscribe events on dispose..

Oh, I guess we did fix these. Thanks for the heads up!

3899 fixed this issue.

Was this page helpful?
0 / 5 - 0 ratings