Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
AndreyMarten opened this issue Mar 14, 2023 · 9 comments
Labels
area-controls-collectionview CollectionView, CarouselView, IndicatorView area-image Image loading, sources, caching platform/android 🤖 s/triaged Issue has been reviewed s/verified Verified / Reproducible Issue ready for Engineering Triage t/bug Something isn't working
Milestone

Comments

@AndreyMarten
Copy link

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)
@AndreyMarten AndreyMarten added the t/bug Something isn't working label Mar 14, 2023
@liveinvarun
Copy link

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

@Eilon Eilon added area-image Image loading, sources, caching area-controls-collectionview CollectionView, CarouselView, IndicatorView labels Mar 14, 2023
@AndreyMarten
Copy link
Author

Thank you, @liveinvarun!

@ghost
Copy link

ghost commented Mar 15, 2023

We've added this issue to our backlog, and we will work to address it as time and resources allow. If you have any additional information or questions about this issue, please leave a comment. For additional info about issue management, please read our Triage Process.

@cardoso20
Copy link

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 still have not found a workaround for this as I assume the workaround proposed by @liveinvarun won't work in this scenario. Is anyone doing something similar and found a possible solution/workaround ? I'm losing my mind over this

@sigged
Copy link

sigged commented 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

@Kanat9494
Copy link

Привет ! У меня такая же проблема, но я не использую локальные изображения.

Я получаю список ByteArrays (каждый из них представляет собой другое изображение), и я использую ByteArrayToImageSourceConverter из набора инструментов для преобразования изображений в ImageSource.

Я до сих пор не нашел обходного пути для этого, поскольку я предполагаю обходной путь, предложенный@liveinvarunне будет работать в этом сценарии. Кто-нибудь делает что-то подобное и нашел возможное решение/обходной путь? я схожу с ума из-за этого

I have the same problem, I have collection view with images from url, and when i am switching tabs and came back, collection view loses all images, to show lost images i have to scroll collectionview items

@jinxinjuan jinxinjuan added s/triaged Issue has been reviewed s/verified Verified / Reproducible Issue ready for Engineering Triage labels Jun 15, 2023
@jinxinjuan
Copy link

Verified this issue with Visual Studio Enterprise 17.7.0 Preview 1.0. Can repro on android platform with sample project.
maui-android-image-from-resource-issue

@pablo0219
Copy link

pablo0219 commented Oct 8, 2023

I have similar problems. I was creating some kind of slide show with thumbnail images in a CollectionView and a bigger image that show the current index selected. When i clicked on an item in the CollectionView, I'm getting that error "Only the original thread that created the view ..." The error could happen in the first, second, or third selection. This used to work in xamarin.
Screenshot 2023-10-08 155707-12

@SliemBeji-FBC
Copy link

I've got the same issue if i use ImageSource.FromStream it trows that error "Only the original thread that created the view ..."
Its just a shame that it still is unfixed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-controls-collectionview CollectionView, CarouselView, IndicatorView area-image Image loading, sources, caching platform/android 🤖 s/triaged Issue has been reviewed s/verified Verified / Reproducible Issue ready for Engineering Triage t/bug Something isn't working
Projects
None yet
Development

No branches or pull requests

10 participants