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

Fixed Android's StreamImageSourceService.LoadDrawableAsync() #14109

Merged
merged 6 commits into from
May 23, 2023

Conversation

jstedfast
Copy link
Member

@jstedfast jstedfast commented Mar 21, 2023

Description of Change

The original issue is that images were being loaded/set on the Image control in a non-UI thread causing an unhandled exception stating, "Only the original thread that created a view hierarchy can touch its view".

Android's StreamImageSourceService.LoadDrawableAsync() incorrectly had a ConfigureAwait(false) on an awaited call to get the image data stream which meant that the subsequent LoadImageFromStream() call would not happen on the original thread.

As added precaution, modified MauiCustomViewTarget.java to post view.setImageDrawable() calls to the main (UI) thread as well.

Issues Fixed

Fixes #14052
Fixes #15397

@Redth
Copy link
Member

Redth commented Mar 22, 2023

@jonathanpeppers @mattleibow I seem to recall some issues with using post on the android side previously, does that sound familiar at all?

Copy link
Member

@jonathanpeppers jonathanpeppers left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the main thing missing here is some kind of test.

Can we add a test to this file?

https://github.com/dotnet/maui/blob/main/src/Controls/tests/DeviceTests/Elements/Image/ImageTests.cs

Comment on lines 29 to 30
protected void onResourceCleared(@Nullable Drawable placeholder) {
this.view.setImageDrawable(placeholder);
post(() -> this.view.setImageDrawable(placeholder));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It did not fix anything specific, no. I think that, technically, only the ConfigureAwait(false) removal is needed. The post() calls were suggested changes that might help make MAUI more robust against this type of problem if customers did things on other threads.

Comment on lines +49 to +51
post(() -> {
// set the image
this.view.setImageDrawable(resource);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change to post() might be OK, did this fix something?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing the ConfigureAwait(false) from StreamImageSourceService.Android.cs seemed to have worked by itself. The post() changes didn't fix anything on their own (i.e. I tested adding back the ConfigureAwait(false) and ended up with errors again).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we don't need this anymore then? Is there any fears that there are related issues?

@jstedfast
Copy link
Member Author

I'll work on adding a test today

@Eilon Eilon added the area-image Image loading, sources, caching label Mar 22, 2023
@Redth
Copy link
Member

Redth commented Mar 22, 2023

@jstedfast this looks potentially related: #14128

Could you maybe validate your changes against that repro too?

@jstedfast
Copy link
Member Author

@Redth yea, that definitely looks related. I'll test it to verify.

@jstedfast jstedfast force-pushed the dev/jestedfa/android-image-load-wrong-thread branch 3 times, most recently from d26c380 to b526ccb Compare March 27, 2023 17:11
@jstedfast
Copy link
Member Author

@Redth I have confirmed that issue #14128 is a duplicate and is fixed by this change.

@jstedfast
Copy link
Member Author

I've been unable to get the DeviceTests to build or run. Would anyone else be willing to write the test(s) for this?

Copy link
Member

@mattleibow mattleibow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How come the tests are not catching this? Are we missing something in the core device tests that we should be adding this?

Copy link
Member

@mattleibow mattleibow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How come the tests are not catching this? Are we missing something in the core device tests that we should be adding this?

@jstedfast jstedfast force-pushed the dev/jestedfa/android-image-load-wrong-thread branch from 91d41c3 to 59cfd4c Compare May 4, 2023 14:49
@jstedfast
Copy link
Member Author

Any chance we can get this merged?

/cc @Redth @jonathanpeppers

@jonathanpeppers
Copy link
Member

Can we add a test? #14109 (review)

@jstedfast
Copy link
Member Author

@jonathanpeppers How would you write a test for this?

@jonathanpeppers
Copy link
Member

We should be able to add a test here that loads an image from an EmbeddedResource as a Stream:

https://github.com/dotnet/maui/blob/main/src/Controls/tests/DeviceTests/Elements/Image/ImageTests.cs

Previously, any Image using a Stream would break? Correct?

@mattleibow
Copy link
Member

Does the test fail without your changes? Was the issue also related to setting the property from a background thread, or if the stream went to the background thread? Maybe the task should be Source = ImageSource.FromStream(() => Task.Run(() => stream))?

I do know we have images in our sample that I am sure load, but we do basic things.

@jstedfast
Copy link
Member Author

@mattleibow The customer sample did not set the stream in a background thread - they set it in the main thread.

@jstedfast
Copy link
Member Author

Argh, not sure why it's getting a null stream for the embedded image when run on the bots but not locally. Doesn't seem to make sense :(

@jstedfast
Copy link
Member Author

Still getting failures. It looks like the failures are only on iOS.

Is there something specifically different about iOS that embedded resources would be named differently or somehow not embedded, perhaps?

@jonathanpeppers
Copy link
Member

@mattleibow do you know?

System.ArgumentNullException : ArgumentNull_Generic Arg_ParamName_Name, ctx

Stack trace
   at ObjCRuntime.NativeObjectExtensions.GetNonNullHandle(INativeObject self, String argumentName)
   at CoreAnimation.CALayer.RenderInContext(CGContext ctx)
   at Microsoft.Maui.DeviceTests.AssertionExtensions.ToBitmap(UIView view, IMauiContext mauiContext)
   at Microsoft.Maui.DeviceTests.AssertionExtensions.AssertContainsColor(UIView view, UIColor expectedColor, IMauiContext mauiContext, Nullable`1 tolerance)
   at Microsoft.Maui.DeviceTests.ImageTests.<>c__DisplayClass2_0.<<ImageSetFromStreamRenders>b__0>d.MoveNext()
--- End of stack trace from previous location ---
   at Microsoft.Maui.Dispatching.DispatcherExtensions.<>c__DisplayClass3_0.<<DispatchAsync>b__0>d.MoveNext()
--- End of stack trace from previous location ---
   at Microsoft.Maui.Dispatching.DispatcherExtensions.<>c__DisplayClass2_0`1.<<DispatchAsync>b__0>d[[System.Boolean, System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].MoveNext()
--- End of stack trace from previous location ---
   at Microsoft.Maui.DeviceTests.ImageTests.ImageSetFromStreamRenders()
--- End of stack trace from previous location ---

The stream isn't null or this stacktrace would be a lot different.

@jstedfast jstedfast force-pushed the dev/jestedfa/android-image-load-wrong-thread branch from 3046dc3 to 83580c6 Compare May 22, 2023 14:32
Copy link
Member

@jonathanpeppers jonathanpeppers left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, assuming the new test verifies the fix.

@jstedfast jstedfast merged commit ecd03a3 into main May 23, 2023
@jstedfast jstedfast deleted the dev/jestedfa/android-image-load-wrong-thread branch May 23, 2023 16:59
@janseris
Copy link

janseris commented Jun 30, 2023

When is this fix released in a non-preview MAUI version?

@samhouts samhouts added the backport/suggested The PR author or issue review has suggested that the change should be backported. label Aug 4, 2023
@PureWeen
Copy link
Member

PureWeen commented Aug 4, 2023

If we backport this, we should regenerate the android file

@PureWeen
Copy link
Member

PureWeen commented Aug 9, 2023

/backport to net7.0

@github-actions
Copy link
Contributor

github-actions bot commented Aug 9, 2023

Started backporting to net7.0: https://github.com/dotnet/maui/actions/runs/5811072947

@PureWeen PureWeen added the backport/approved After some discussion or review, this PR or change was approved to be backported. label Aug 9, 2023
@github-actions github-actions bot locked and limited conversation to collaborators Dec 12, 2023
@Eilon Eilon added area-controls-image Image control and removed area-image Image loading, sources, caching labels May 10, 2024
@samhouts samhouts added the fixed-in-8.0.0-preview.5.8529 Look for this fix in 8.0.0-preview.5.8529! label Aug 2, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-controls-image Image control backport/approved After some discussion or review, this PR or change was approved to be backported. backport/suggested The PR author or issue review has suggested that the change should be backported. fixed-in-8.0.0-preview.5.8529 Look for this fix in 8.0.0-preview.5.8529! platform/android 🤖
Projects
None yet
8 participants