Skip to content

TouchTracking. throw System.ArgumentException: An item with the same key has already been added. Key: 0 #25

@EugenyTsoy

Description

@EugenyTsoy

I am using TouchTracking.Forms with Prism.Forms INavigationService for app on Andriod platform.

OnTouchAction is used for navigation between view by INavigationService. For example in touchReleasedactionHandler call:
navigationService.NavigateAsync($"/{nameof(someView)}");

If I frequantly switching between pages by touching corresponded buttons with attached OnTouchAction then generated exception:

{System.ArgumentException: An item with the same key has already been added. Key: 0
  at System.Collections.Generic.Dictionary`2[TKey,TValue].TryInsert (TKey key, TValue value, System.Collections.Generic.InsertionBehavior behavior) [0x000dd] in /Users/builder/jenkins/workspace/archive-mono/2020-02/android/release/external/corefx/src/Common/src/CoreLib/System/Collections/Generic/Dictionary.cs:531 
  at System.Collections.Generic.Dictionary`2[TKey,TValue].Add (TKey key, TValue value) [0x00000] in /Users/builder/jenkins/workspace/archive-mono/2020-02/android/release/external/corefx/src/Common/src/CoreLib/System/Collections/Generic/Dictionary.cs:240 
  at **TouchTracking.Droid.TouchHandler.OnTouch** (System.Object sender, Android.Views.View+TouchEventArgs args) [0x0008f] in <16086595d2354bd2a5fae8bf7e121c37>:0 
  at Android.Views.View+IOnTouchListenerImplementor.OnTouch (Android.Views.View v, Android.Views.MotionEvent e) [0x00014] in /Users/builder/azdo/_work/30/s/xamarin-android/src/Mono.Android/obj/Release/monoandroid10/android-29/mcw/Android.Views.View.cs:4028 
  at Android.Views.View+IOnTouchListenerInvoker.n_OnTouch_Landroid_view_View_Landroid_view_MotionEvent_ (System.IntPtr jnienv, System.IntPtr native__this, System.IntPtr native_v, System.IntPtr native_e) [0x00018] in /Users/builder/azdo/_work/30/s/xamarin-android/src/Mono.Android/obj/Release/monoandroid10/android-29/mcw/Android.Views.View.cs:3967 
  at (wrapper dynamic-method) Android.Runtime.DynamicMethodNameCounter.53(intptr,intptr,intptr,intptr)}

The exception is thrown when trying to add a duplicate key 1 or 0 to dictionary the TouchHandler._idToTouchHandlerDictionary.

I found that there might be a case where an element receives a touch action (adding touch to _idToTouchHandlerDictionary) and does not receive a touch release action (remove touch from _idToTouchHandlerDictionary) because it was previously disposed.

During disposing, the visual element is detahed from the "TouchEffect" effect. "TouchEffect" calls "_touchHandler.UnregisterEvents (_view)" where happens unscribing from "view.Touch" event:

view.Touch -= OnTouch;

Is it need to add some code that will be removed elements associated with disposed or detached view? It solves problem in my case.

 var idToTouchHandlerDictKeys = _idToTouchHandlerDictionary
                    .Where(x => x.Value.Handler._view == view)
                    .Select(x => x.Key)
                    .Distinct();

                foreach (var key in idToTouchHandlerDictKeys)
                {
                    _idToTouchHandlerDictionary.Remove(key);
                }

in TouchHandler.UnregisterEvents

public override void UnregisterEvents(View view)
        {
            try
            {
                view.GetHashCode();
            }
            catch (ObjectDisposedException) //view can be already disposed and we have no other way to remove it from dictionary
            {

                Console.WriteLine($"___disposed view:{Type.GetTypeHandle(view).Value}");
                var newDictionary = new Dictionary<View, TouchHandler>();
                foreach (KeyValuePair<View, TouchHandler> item in _viewDictionary)
                {
                    try
                    {
                        newDictionary[item.Key] = item.Value;
                    }
                    catch (ObjectDisposedException)
                    {
                        continue;
                    }
                }
                _viewDictionary = newDictionary;
                return;
            }
            if (_viewDictionary.ContainsKey(view))
            {
                _viewDictionary.Remove(view);


                // Is it need to add this processing?
                var idToTouchHandlerDictKeys = _idToTouchHandlerDictionary
                    .Where(x => x.Value._view == view)
                    .Select(x => x.Key)
                    .Distinct();

                foreach (var key in idToTouchHandlerDictKeys)
                {
                    _idToTouchHandlerDictionary.Remove(key);
                }
                //---

                view.Touch -= OnTouch;
                Console.WriteLine($"___OnTouch removed view:{Type.GetTypeHandle(view).Value}");
            }
        }

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions