amay077 / Xamarin.Forms.GoogleMaps

Map library for Xamarin.Forms using Google maps API
https://www.nuget.org/packages/Xamarin.Forms.GoogleMaps/
MIT License
546 stars 350 forks source link

Crash when using BitmapDescriptorFactory.FromView for custom Pin. #524

Closed sonnguyen0310 closed 5 years ago

sonnguyen0310 commented 6 years ago

VERSIONS

PLATFORMS

ACTUAL BEHAVIOR

Android 7.0 When I using custom Pin view with the local resource, sometimes it make a crash.

ACTUAL SCREENSHOTS/STACKTRACE

AppCenterCrashes] Unhandled Exception from source=AndroidEnvironment
[AppCenterCrashes] System.NullReferenceException: Object reference not set to an instance of an object.
[AppCenterCrashes]   at System.Collections.Generic.LinkedList`1[T].InternalInsertNodeBefore (System.Collections.Generic.LinkedListNode`1[T] node, System.Collections.Generic.LinkedListNode`1[T] newNode) [0x00013] in <5a97d41d36694fb19855c17429527b10>:0 
[AppCenterCrashes]   at System.Collections.Generic.LinkedList`1[T].AddLast (T value) [0x00019] in <5a97d41d36694fb19855c17429527b10>:0 
[AppCenterCrashes]   at Xamarin.Forms.GoogleMaps.Android.Utils+<>c__DisplayClass6_0.<ConvertViewToBitmapDescriptor>b__0 () [0x000e6] in <2708054b9c524524949e63a8d8895424>:0 
[AppCenterCrashes]   at System.Threading.Tasks.Task`1[TResult].InnerInvoke () [0x0000f] in <f32579baafc1404fa37ba3ec1abdc0bd>:0 
[AppCenterCrashes]   at System.Threading.Tasks.Task.Execute () [0x00010] in <f32579baafc1404fa37ba3ec1abdc0bd>:0 
[AppCenterCrashes] --- End of stack trace from previous location where exception was thrown ---
[AppCenterCrashes]   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x0000c] in <f32579baafc1404fa37ba3ec1abdc0bd>:0 
[AppCenterCrashes]   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (System.Threading.Tasks.Task task) [0x0003e] in <f32579baafc1404fa37ba3ec1abdc0bd>:0 
[AppCenterCrashes]   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Threading.Tasks.Task task) [0x00028] in <f32579baafc1404fa37ba3ec1abdc0bd>:0 
[AppCenterCrashes]   at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd (System.Threading.Tasks.Task task) [0x00008] in <f32579baafc1404fa37ba3ec1abdc0bd>:0 
[AppCenterCrashes]   at System.Runtime.CompilerServices.TaskAwaiter`1[TResult].GetResult () [0x00000] in <f32579baafc1404fa37ba3ec1abdc0bd>:0 
[AppCenterCrashes]   at Xamarin.Forms.GoogleMaps.Logics.Android.PinLogic+<TransformXamarinViewToAndroidBitmap>d__29.MoveNext () [0x001e3] in <2708054b9c524524949e63a8d8895424>:0 
[AppCenterCrashes] --- End of stack trace from previous location where exception was thrown ---
[AppCenterCrashes]   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x0000c] in <f32579baafc1404fa37ba3ec1abdc0bd>:0 
[AppCenterCrashes]   at System.Runtime.CompilerServices.AsyncMethodBuilderCore+<>c.<ThrowAsync>b__6_0 (System.Object state) [0x00000] in <f32579baafc1404fa37ba3ec1abdc0bd>:0 
[AppCenterCrashes]   at Android.App.SyncContext+<>c__DisplayClass2_0.<Post>b__0 () [0x00000] in <f9b96d3a30e44b50a15b67d62391cfda>:0 
[AppCenterCrashes]   at Java.Lang.Thread+RunnableImplementor.Run () [0x00008] in <f9b96d3a30e44b50a15b67d62391cfda>:0 
[AppCenterCrashes]   at Java.Lang.IRunnableInvoker.n_Run (System.IntPtr jnienv, System.IntPtr native__this) [0x00008] in <f9b96d3a30e44b50a15b67d62391cfda>:0 
[AppCenterCrashes]   at (wrapper dynamic-method) System.Object.60b093c8-a10f-4fc4-a0c3-a7ee9f1e4fa1(intptr,intptr)

EXPECTED BEHAVIOR

Is anyway to fix it ?

HOW TO REPRODUCE

It's not 100% reproduce, just happen randomly when I try to add multi-marker by using for loop as the code below

foreach(Market m in _marketList) {
var pin = new Pin()
    {
    Type = PinType.Generic,
    Address = m.id.ToString(),
    Label = m.id.ToString(),
    Flat = true,
    Position = new Position(m.location.Latitude, m.location.Longitude),
         Icon = BitmapDescriptorFactory.FromView(new PinView(m.resouce);
    };
myMap.Pins.Add(pin);
}
amay077 commented 6 years ago

I'm not sure what is reason for it.

But we released "how to cache images" with v3.0.0 .

Please try implement your CachingNativeBitmapDescriptorFactory .

sonnguyen0310 commented 6 years ago

@amay077 Thank for your quick reply. I will try to implement it and report here if it happens again.

sonnguyen0310 commented 6 years ago

@amay077 CachingNativeBitmapDescriptorFactory look cannot resolve the problem, I will continuous investigate it and report the root cause later

langtu commented 6 years ago

I got the same issue but its not easy to reproduce. Sometime the app crashs when rendering custom pin from view.

sonnguyen0310 commented 6 years ago

Look like I fixed this bug, that I do not see crash log anymore for couple weeks.

In my case, this happen because of synchronized thread running at the same time, for an example, call add pin while the map is clearing the pins.

So i make it await, try to handle it not called to add, clear pins at the same time and it good to go.

amay077 commented 6 years ago

Hi @sonnguyen0310 , Good report!

I suggest your use only UI thread for create and add, remove or clear pins. This means do not use async/await or Task<T> for it.

@langtu , please check your using threads.

langtu commented 6 years ago

Hi,

It seems that the issue still exists although I put all code of add/remove/clear pins within Device.BeginInvokeOnMainThread().

Here's my crash log:

LinkedList`1[T].InternalRemoveNode (System.Collections.Generic.LinkedListNode`1[T] node)
System.NullReferenceException: Object reference not set to an instance of an object
LinkedList`1[T].InternalRemoveNode (System.Collections.Generic.LinkedListNode`1[T] node)
LinkedList`1[T].RemoveFirst ()
Utils+<>c__DisplayClass6_0.<ConvertViewToBitmapDescriptor>b__0 ()
Task`1[TResult].InnerInvoke ()
Task.Execute ()
ExceptionDispatchInfo.Throw ()
TaskAwaiter.ThrowForNonSuccess (System.Threading.Tasks.Task task)
TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Threading.Tasks.Task task)
TaskAwaiter.ValidateEnd (System.Threading.Tasks.Task task)
TaskAwaiter`1[TResult].GetResult ()
PinLogic+<TransformXamarinViewToAndroidBitmap>d__31.MoveNext ()
ExceptionDispatchInfo.Throw ()
AsyncMethodBuilderCore+<>c.<ThrowAsync>b__6_0 (System.Object state)
SyncContext+<>c__DisplayClass2_0.<Post>b__0 ()
Thread+RunnableImplementor.Run ()
IRunnableInvoker.n_Run (System.IntPtr jnienv, System.IntPtr native__this)
(wrapper dynamic-method) System.Object.26dfa028-9e84-49d0-82ab-149862c32528(intptr,intptr)

I temporary fix the issue by rebuild code using lock in method ConvertViewToBitmapDescriptor in class Xamarin.Forms.GoogleMaps.Android.Utils (https://github.com/amay077/Xamarin.Forms.GoogleMaps/blob/v3.0.1/Xamarin.Forms.GoogleMaps/Xamarin.Forms.GoogleMaps.Android/Utils.cs):

return Task.Run(() => {

    var bmp = ConvertViewToBitmap(v);
    var img = global::Android.Gms.Maps.Model.BitmapDescriptorFactory.FromBitmap(bmp);

    var buffer = ByteBuffer.Allocate(bmp.ByteCount);
    bmp.CopyPixelsToBuffer(buffer);
    buffer.Rewind();

    // https://forums.xamarin.com/discussion/5950/how-to-convert-from-bitmap-to-byte-without-bitmap-compress
    IntPtr classHandle = JNIEnv.FindClass("java/nio/ByteBuffer");
    IntPtr methodId = JNIEnv.GetMethodID(classHandle, "array", "()[B");
    IntPtr resultHandle = JNIEnv.CallObjectMethod(buffer.Handle, methodId);
    byte[] bytes = JNIEnv.GetArray<byte>(resultHandle);
    JNIEnv.DeleteLocalRef(resultHandle);

    var sha = MD5.Create();
    var hash = Convert.ToBase64String(sha.ComputeHash(bytes));

    var exists = cache.ContainsKey(hash);
    lock (lruTracker)
    {        
        if (exists)
        {
            lruTracker.Remove(hash);
            lruTracker.AddLast(hash);
            return cache[hash];
        }
        if (lruTracker.Count > 10) // O(1)
        {
            global::Android.Gms.Maps.Model.BitmapDescriptor tmp;
            cache.TryRemove(lruTracker.First.Value, out tmp);
            lruTracker.RemoveFirst();
        }
        lruTracker.AddLast(hash);
    }
    cache.GetOrAdd(hash, img);

    return img;
});

@amay077 I also don't understand why this method need lruTracker and cache fields although the img always be created. Why do not immediately return img without check for caching?

jerryhuang-net commented 5 years ago

@langtu I think it's because LinkedList is not a thread-safe implementation. Ref: https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.linkedlist-1.system-collections-icollection-syncroot?view=netframework-4.8

I believe your fix is legit. @amay077 can you please include this to master and make a release to nuget?

amay077 commented 5 years ago

Could you send Pull request?

amay077 commented 5 years ago

I released v3.2.1 that includes fix this.

rubberydev commented 5 years ago

Hi guys,

I have the same issue in v3.2.1 in Android platform, I also added the PlatformConfig like @amay077 said in the advice above, even though, when I use CachingNativeBitmapDescriptorFactory the render view for custom pin crashed, I'm running my application in Motorola 2g and 3g Android 6 (Marshmellow) and 8 (Oreo), the detail about stack trace is below:

Source: mscorlib Message: Object reference not set to an instance of an object. at Xamarin.Forms.GoogleMaps.Android.Utils+<>cDisplayClass2_0.b0 () [0x00038] in <15e1414cdc6345fe95968791274ef90d>:0 \n
at System.Threading.Tasks.Task`1[TResult].InnerInvoke () [0x0000f] in :0 \n
at System.Threading.Tasks.Task.Execute () [0x00000] in :0 \n--- End of stack trace from previous location where exception was thrown ---\n\n
at Xamarin.Forms.GoogleMaps.Logics.Android.PinLogic.TransformXamarinViewToAndroidBitmap(Xamarin.Forms.GoogleMaps.Pin outerItem, Android.Gms.Maps.Model.Marker nativeItem) [0x0013b] in <15e1414cdc6345fe95968791274ef90d>:0 \n
at System.Runtime.CompilerServices.AsyncMethodBuilderCore+<>c.b
7_0 (System.Object state) [0x00000] in :0 \n
at Android.App.SyncContext+<>c
DisplayClass2_0.b__0 () [0x00000] in :0 \n at Java.Lang.Thread+RunnableImplementor.Run () [0x00008] in :0 \n
at Java.Lang.IRunnableInvoker.n_Run (System.IntPtr jnienv, System.IntPtr native__this) 0x00009] in :0 \n
at (wrapper dynamic-method) Android.Runtime.DynamicMethodNameCounter.46(intptr,intptr)

I hope this helps.

Thanks in advance.

gidgol commented 4 years ago

Same at Xamarin.Forms.GoogleMaps.Android.Utils+<>c__DisplayClass2_0.b__0 () [0x00038] in :0 at System.Threading.Tasks.Task`1[TResult].InnerInvoke () [0x0000f] in <19853c43ab794d18ab1a33ecb65b3c4d>:0 at System.Threading.Tasks.Task.Execute () [0x00000] in <19853c43ab794d18ab1a33ecb65b3c4d>:0 --- End of stack trace from previous location where exception was thrown ---

at Xamarin.Forms.GoogleMaps.Logics.Android.PinLogic.TransformXamarinViewToAndroidBitmap (Xamarin.Forms.GoogleMaps.Pin outerItem, Android.Gms.Maps.Model.Marker nativeItem) [0x0013b] in :0 at System.Runtime.CompilerServices.AsyncMethodBuilderCore+<>c.b7_0 (System.Object state) [0x00000] in <19853c43ab794d18ab1a33ecb65b3c4d>:0 at Android.App.SyncContext+<>c__DisplayClass2_0.b0 () [0x00000] in <8c07a09624c14764b43f6b946a5a1f23>:0 at Java.Lang.Thread+RunnableImplementor.Run () [0x00008] in <8c07a09624c14764b43f6b946a5a1f23>:0 at Java.Lang.IRunnableInvoker.n_Run (System.IntPtr jnienv, System.IntPtr native__this) [0x00009] in <8c07a09624c14764b43f6b946a5a1f23>:0 at (wrapper dynamic-method) Android.Runtime.DynamicMethodNameCounter.49(intptr,intptr)

zohaibshahzad1990 commented 4 years ago

Hi all i have solved the issue, error occurred on below method which is found in Xamarin.Forms.GoogleMaps.Android.Util.cs classs.

public static Task ConvertFormsToNative(View view, Rectangle size, IVisualElementRenderer vRenderer) { return Task.Run(() => { var viewGroup = vRenderer.ViewGroup; vRenderer.Tracker.UpdateLayout(); var layoutParams = new ViewGroup.LayoutParams((int)size.Width, (int)size.Height); viewGroup.LayoutParameters = layoutParams; view.Layout(size); viewGroup.Layout(0, 0, (int)view.WidthRequest, (int)view.HeightRequest); //await FixImageSourceOfImageViews(viewGroup as ViewGroup); // Not sure why this was being done in original return viewGroup; }); }

in the line "var viewGroup = vRenderer.ViewGroup;" it is giving a warning "ViewGroup is obsolete as of version 2.3.5. Please use View instead.".on the runtime , its getting null. so i have fixed it by implementing the suggestion that is given in the warning, here is the fix

public static Task ConvertFormsToNative(View view, Rectangle size, IVisualElementRenderer vRenderer) { return Task.Run(() => { var viewGroup = vRenderer.View; vRenderer.Tracker.UpdateLayout(); var layoutParams = new ViewGroup.LayoutParams((int)size.Width, (int)size.Height); viewGroup.LayoutParameters = layoutParams; view.Layout(size); viewGroup.Layout(0, 0, (int)view.WidthRequest, (int)view.HeightRequest); //await FixImageSourceOfImageViews(viewGroup as ViewGroup); // Not sure why this was being done in original return viewGroup; }); }

i am new in github don't know how to commit this code.

dejanbasic commented 4 years ago

Hi, this is still happening in version 3.3.0, Xamarin.Forms 4.5.0:

at System.Collections.Concurrent.ConcurrentDictionary2[TKey,TValue].ThrowKeyNotFoundException (System.Object key) [0x00000] in /Users/builder/jenkins/workspace/archive-mono/2020-02/android/release/external/corefx/src/System.Collections.Concurrent/src/System/Collections/Concurrent/ConcurrentDictionary.cs:912 at System.Collections.Concurrent.ConcurrentDictionary2[TKey,TValue].get_Item (TKey key) [0x0000b] in /Users/builder/jenkins/workspace/archive-mono/2020-02/android/release/external/corefx/src/System.Collections.Concurrent/src/System/Collections/Concurrent/ConcurrentDictionary.cs:893 at Xamarin.Forms.GoogleMaps.Android.Utils+<>cDisplayClass6_0.b0 () [0x000ae] in :0 at System.Threading.Tasks.Task`1[TResult].InnerInvoke () [0x0000f] in /Users/builder/jenkins/workspace/archive-mono/2020-02/android/release/external/corert/src/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs:534 at System.Threading.Tasks.Task.Execute () [0x00000] in /Users/builder/jenkins/workspace/archive-mono/2020-02/android/release/external/corert/src/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs:2319 --- End of stack trace from previous location where exception was thrown --- at Xamarin.Forms.GoogleMaps.Logics.Android.PinLogic.TransformXamarinViewToAndroidBitmap (Xamarin.Forms.GoogleMaps.Pin outerItem, Android.Gms.Maps.Model.Marker nativeItem) [0x001dc] in :0 at System.Runtime.CompilerServices.AsyncMethodBuilderCore+<>c.b7_0 (System.Object state) [0x00000] in /Users/builder/jenkins/workspace/archive-mono/2020-02/android/release/mcs/class/referencesource/mscorlib/system/runtime/compilerservices/AsyncMethodBuilder.cs:1021 at Android.App.SyncContext+<>cDisplayClass2_0.b__0 () [0x00000] in /Users/builder/azdo/_work/22/s/xamarin-android/src/Mono.Android/Android.App/SyncContext.cs:36 at Java.Lang.Thread+RunnableImplementor.Run () [0x00008] in /Users/builder/azdo/_work/22/s/xamarin-android/src/Mono.Android/Java.Lang/Thread.cs:36 at Java.Lang.IRunnableInvoker.n_Run (System.IntPtr jnienv, System.IntPtr native__this) [0x00008] in /Users/builder/azdo/_work/22/s/xamarin-android/src/Mono.Android/obj/Release/monoandroid10/android-28/mcw/Java.Lang.IRunnable.cs:81 at (wrapper dynamic-method) Android.Runtime.DynamicMethodNameCounter.1(intptr,intptr)

@amay077 can you implement fix suggested above?

dejanbasic commented 3 years ago

Anyone still experiences this like me?

ConcurrentDictionary2[TKey,TValue].ThrowKeyNotFoundException (System.Object key) ConcurrentDictionary2[TKey,TValue].get_Item (TKey key) Utils+<>c__DisplayClass6_0.<ConvertViewToBitmapDescriptor>b__0 () Task1[TResult].InnerInvoke () Task.Execute () PinLogic.TransformXamarinViewToAndroidBitmap (Xamarin.Forms.GoogleMaps.Pin outerItem, Android.Gms.Maps.Model.Marker nativeItem) AsyncMethodBuilderCore+<>c.b7_0 (System.Object state) SyncContext+<>c__DisplayClass2_0.b0 () Thread+RunnableImplementor.Run () IRunnableInvoker.n_Run (System.IntPtr jnienv, System.IntPtr native__this) (wrapper dynamic-method) Android.Runtime.DynamicMethodNameCounter.5(intptr,intptr)`

ondrejnovotny commented 2 years ago

Anyone still experiences this like me?

ConcurrentDictionary2[TKey,TValue].ThrowKeyNotFoundException (System.Object key) ConcurrentDictionary2[TKey,TValue].get_Item (TKey key) Utils+<>c__DisplayClass6_0.<ConvertViewToBitmapDescriptor>b__0 () Task1[TResult].InnerInvoke () Task.Execute () PinLogic.TransformXamarinViewToAndroidBitmap (Xamarin.Forms.GoogleMaps.Pin outerItem, Android.Gms.Maps.Model.Marker nativeItem) AsyncMethodBuilderCore+<>c.b7_0 (System.Object state) SyncContext+<>c__DisplayClass2_0.b0 () Thread+RunnableImplementor.Run () IRunnableInvoker.n_Run (System.IntPtr jnienv, System.IntPtr native__this) (wrapper dynamic-method) Android.Runtime.DynamicMethodNameCounter.5(intptr,intptr)`

Same here