Xamarin.forms.googlemaps: Crash when using BitmapDescriptorFactory.FromView for custom Pin.

Created on 26 Jul 2018  路  14Comments  路  Source: amay077/Xamarin.Forms.GoogleMaps

VERSIONS

  • Xamarin.Forms.GoogleMaps - 2.3.0
  • Xamarin.Forms - 3.0.0.550147

PLATFORMS

  • [x] Android
  • [] iOS
  • [ ] UWP

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);
}
bug

All 14 comments

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 .

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

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

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

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.

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.

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?

@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?

Could you send Pull request?

I released v3.2.1 that includes fix this.

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+<>c__DisplayClass2_0.b__0 () [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.

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.b__7_0 (System.Object state) [0x00000] in <19853c43ab794d18ab1a33ecb65b3c4d>:0
at Android.App.SyncContext+<>c__DisplayClass2_0.b__0 () [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)

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.

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+<>c__DisplayClass6_0.b__0 () [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.b__7_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+<>c__DisplayClass2_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?

Was this page helpful?
0 / 5 - 0 ratings