question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Images loaded into CollectionView items via ImageSource.FromResource at runtime are not visible and break the layout

See original GitHub issue

Description

Hello,

If you show images in CollectionView items and update these items at runtime, certain images may not be loaded with the corresponding exception in the output. After that, the entire application’s layout becomes broken. The issue occurs only on Android devices.

Steps to Reproduce


  1. Run the application on an Android simulator;
  2. Use the Refresh button several times. Each time two items with different icons should be displayed in the CollectionView.
  3. Once you see that only one image is shown - review the Output window. You will see an error (see the call stack below);
  4. Try to switch to a different tab. You will see a completely empty pages as the layout is broken.

The screencast below illustrates these steps and the problematic behavior:

Screencast.mp4

Link to public reproduction project repository

https://github.com/AndreyMarten/maui-android-image-from-resource-issue

Version with bug

7.0 (current)

Last version that worked well

Unknown/Other

Affected platforms

Android

Affected platform versions

Android 11

Did you find any workaround?

No

Relevant log output

[0:] Microsoft.Maui.StreamImageSourceService: Warning: Unable to load image stream.

Android.Util.AndroidRuntimeException: Only the original thread that created a view hierarchy can touch its views.
   at Java.Interop.JniEnvironment.StaticMethods.CallStaticVoidMethod(JniObjectReference type, JniMethodInfo method, JniArgumentValue* args) in /Users/runner/work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/obj/Release/net7.0/JniEnvironment.g.cs:line 13250
   at Java.Interop.JniPeerMembers.JniStaticMethods.InvokeVoidMethod(String encodedMember, JniArgumentValue* parameters) in /Users/runner/work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniStaticMethods.cs:line 97
   at Microsoft.Maui.PlatformInterop.LoadImageFromStream(ImageView imageView, Stream inputStream, IImageLoaderCallback callback) in D:\a\_work\1\s\src\Core\src\obj\Release
et7.0-android\generated\src\Microsoft.Maui.PlatformInterop.cs:line 443
   at Microsoft.Maui.StreamImageSourceService.LoadDrawableAsync(IImageSource imageSource, ImageView imageView, CancellationToken cancellationToken) in D:\a\_work\1\s\src\Core\src\ImageSources\StreamImageSourceService\StreamImageSourceService.Android.cs:line 28
  --- End of managed Android.Util.AndroidRuntimeException stack trace ---
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
	at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:8798)
	at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:1606)
	at android.view.View.requestLayout(View.java:25390)
	at android.view.View.requestLayout(View.java:25390)
	at android.view.View.requestLayout(View.java:25390)
	at android.view.View.requestLayout(View.java:25390)
	at android.view.View.requestLayout(View.java:25390)
	at android.view.View.requestLayout(View.java:25390)
	at android.view.View.requestLayout(View.java:25390)
	at androidx.drawerlayout.widget.DrawerLayout.requestLayout(DrawerLayout.java:1353)
	at android.view.View.requestLayout(View.java:25390)
	at android.view.View.requestLayout(View.java:25390)
	at android.view.View.requestLayout(View.java:25390)
	at android.view.View.requestLayout(View.java:25390)
	at android.view.View.requestLayout(View.java:25390)
	at android.view.View.requestLayout(View.java:25390)
	at androidx.recyclerview.widget.RecyclerView.requestLayout(RecyclerView.java:4586)
	at android.view.View.requestLayout(View.java:25390)
	at android.view.View.requestLayout(View.java:25390)
	at android.view.View.requestLayout(View.java:25390)
	at android.view.View.requestLayout(View.java:25390)
	at android.view.View.requestLayout(View.java:25390)
	at androidx.recyclerview.widget.RecyclerView.requestLayout(RecyclerView.java:4586)
	at android.view.View.requestLayout(View.java:25390)
	at android.view.View.requestLayout(View.java:25390)
	at android.widget.ImageView.setImageDrawable(ImageView.java:597)
	at androidx.appcompat.widget.AppCompatImageView.setImageDrawable(AppCompatImageView.java:112)
	at com.microsoft.maui.glide.MauiCustomViewTarget.onResourceCleared(MauiCustomViewTarget.java:30)
	at com.bumptech.glide.request.target.CustomViewTarget.onLoadCleared(CustomViewTarget.java:210)
	at com.bumptech.glide.request.SingleRequest.clear(SingleRequest.java:337)
	at com.bumptech.glide.request.ErrorRequestCoordinator.clear(ErrorRequestCoordinator.java:48)
	at com.bumptech.glide.manager.RequestTracker.clearAndRemove(RequestTracker.java:70)
	at com.bumptech.glide.RequestManager.untrack(RequestManager.java:660)
	at com.bumptech.glide.Glide.removeFromManagers(Glide.java:913)
	at com.bumptech.glide.RequestManager.untrackOrDelegate(RequestManager.java:647)
	at com.bumptech.glide.RequestManager.clear(RequestManager.java:624)
	at com.bumptech.glide.RequestBuilder.into(RequestBuilder.java:811)
	at com.bumptech.glide.RequestBuilder.into(RequestBuilder.java:780)
	at com.bumptech.glide.RequestBuilder.into(RequestBuilder.java:771)
	at com.microsoft.maui.PlatformInterop.prepare(PlatformInterop.java:229)
	at com.microsoft.maui.PlatformInterop.loadInto(PlatformInterop.java:234)
	at com.microsoft.maui.PlatformInterop.loadImageFromStream(PlatformInterop.java:265)

  --- End of managed Android.Util.AndroidRuntimeException stack trace ---
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
	at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:8798)
	at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:1606)
	at android.view.View.requestLayout(View.java:25390)
	at android.view.View.requestLayout(View.java:25390)
	at android.view.View.requestLayout(View.java:25390)
	at android.view.View.requestLayout(View.java:25390)
	at android.view.View.requestLayout(View.java:25390)
	at android.view.View.requestLayout(View.java:25390)
	at android.view.View.requestLayout(View.java:25390)
	at androidx.drawerlayout.widget.DrawerLayout.requestLayout(DrawerLayout.java:1353)
	at android.view.View.requestLayout(View.java:25390)
	at android.view.View.requestLayout(View.java:25390)
	at android.view.View.requestLayout(View.java:25390)
	at android.view.View.requestLayout(View.java:25390)
	at android.view.View.requestLayout(View.java:25390)
	at android.view.View.requestLayout(View.java:25390)
	at androidx.recyclerview.widget.RecyclerView.requestLayout(RecyclerView.java:4586)
	at android.view.View.requestLayout(View.java:25390)
	at android.view.View.requestLayout(View.java:25390)
	at android.view.View.requestLayout(View.java:25390)
	at android.view.View.requestLayout(View.java:25390)
	at android.view.View.requestLayout(View.java:25390)
	at androidx.recyclerview.widget.RecyclerView.requestLayout(RecyclerView.java:4586)
	at android.view.View.requestLayout(View.java:25390)
	at android.view.View.requestLayout(View.java:25390)
	at android.widget.ImageView.setImageDrawable(ImageView.java:597)
	at androidx.appcompat.widget.AppCompatImageView.setImageDrawable(AppCompatImageView.java:112)
	at com.microsoft.maui.glide.MauiCustomViewTarget.onResourceCleared(MauiCustomViewTarget.java:30)
	at com.bumptech.glide.request.target.CustomViewTarget.onLoadCleared(CustomViewTarget.java:210)
	at com.bumptech.glide.request.SingleRequest.clear(SingleRequest.java:337)
	at com.bumptech.glide.request.ErrorRequestCoordinator.clear(ErrorRequestCoordinator.java:48)
	at com.bumptech.glide.manager.RequestTracker.clearAndRemove(RequestTracker.java:70)
	at com.bumptech.glide.RequestManager.untrack(RequestManager.java:660)
	at com.bumptech.glide.Glide.removeFromManagers(Glide.java:913)
	at com.bumptech.glide.RequestManager.untrackOrDelegate(RequestManager.java:647)
	at com.bumptech.glide.RequestManager.clear(RequestManager.java:624)
	at com.bumptech.glide.RequestBuilder.into(RequestBuilder.java:811)
	at com.bumptech.glide.RequestBuilder.into(RequestBuilder.java:780)
	at com.bumptech.glide.RequestBuilder.into(RequestBuilder.java:771)
	at com.microsoft.maui.PlatformInterop.prepare(PlatformInterop.java:229)
	at com.microsoft.maui.PlatformInterop.loadInto(PlatformInterop.java:234)
	at com.microsoft.maui.PlatformInterop.loadImageFromStream(PlatformInterop.java:265)

Issue Analytics

  • State:open
  • Created 6 months ago
  • Reactions:1
  • Comments:7

github_iconTop GitHub Comments

4reactions
siggedcommented, Mar 22, 2023

Hi ! I’m having the same problem but im not using local images.

I’m receiving a list of ByteArrays (each one is a different image) and im using the ByteArrayToImageSourceConverter from the toolkit to convert the images to an ImageSource.

I was facing the same problem today, and I found that under certain cases, for example when returning to a View, this problem appears. I suspect there could be an issue in StreamImageSourceService.Android.cs when PlatformInterop.LoadImageFromStream(imageView, stream, callback) is called.

The ByteArrayToImageSourceConverter from the toolkit uses an overload which allows you to create a stream, using ImageSource.FromStream(() => new MemoryStream(value)) which seems to tickle the thread access problem.

I have solved it by modifying the Converter so it uses another overload which accepts a Task. This seems to be handled better in the underlying platform handlers of Maui: return ImageSource.FromStream((token) => Task.FromResult((Stream)new MemoryStream(value)));

Link to my fork: https://github.com/sigged/ctk-maui/blob/bugfix/invalid-thread-bytetoimageconverter/src/CommunityToolkit.Maui/Converters/ByteArrayToImageSourceConverter.shared.cs

1reaction
liveinvaruncommented, Mar 14, 2023

If you make the image buildAction as MauiImage rather than embedded resource and try loading the image as below - i see the app works without issues. private void Button_Clicked(object sender, EventArgs e) {

        this.Items.Clear();
        this.Items.Add(new Item() { Image = ImageSource.FromFile("refresh.png") });
        this.Items.Add(new Item() { Image = ImageSource.FromFile("remove.png") });
     
}
Read more comments on GitHub >

github_iconTop Results From Across the Web

DXCollectionView does not show images in its items when ...
DevExpress Support Team: CLONED FROM T1154812: MAUI, Images are ... Found another issue with loosing images, now it is in DXCollectionView
Read more >
Unable to Display Image Inside CollectionView Using ...
Everything is working, except being able to display the image inside the CollectionView. It seems that Xamarin.Forms won't allow using a ...
Read more >
Images not displayed in collection view cell
I have a collection view cell and inside there's an imageView. On it I display array of images using sd_setImage once loaded from...
Read more >
Grouping in Xamarin ListView (SfListView)
A group represents a collection of items belongs to a category. When grouping is applied, the data is organized into different groups based...
Read more >
CollectionView layout gets mad after scrolling
Hi, I'm setting a collection View in order to display content as instagram Profile view with all the photos in the grid. in...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found