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

DisposeAsync() on IJSObjectReference causes hanging when called on Android 11 and lower on app suspension #13529

Closed
kanadaj opened this issue Feb 23, 2023 · 4 comments · Fixed by #22496
Assignees
Labels
area-blazor Blazor Hybrid / Desktop, BlazorWebView partner/android Issues for the Android SDK 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

@kanadaj
Copy link

kanadaj commented Feb 23, 2023

Description

On Android 11 and lower, the back button on the home page has different behaviour from the home button - it finishes the current activity. This was changed on Android 12 (see this page), however on earlier versions, this results in a different behaviour.

This, however, means that pressing back, then re-opening the app causes Blazor to dispose of resources during app initialization when the app has re-opened. This means the WebView has already been disposed of, or not yet initialized - I'm not quite sure which one. This causes any DisposeAsync() calls against IJSObjectReference objects to hang synchronously.

Steps to Reproduce

  1. Create a new MAUI app
  2. Create a simple site.js file in wwwroot
  3. Create the following service class:
public class TestJsReferenceService : IAsyncDisposable {
        private readonly IJSRuntime jsRuntime;
        private IJSObjectReference? module = null;
        private const string SomeJsModule = "/site.js";
        public TestJsReferenceService(IJSRuntime jsRuntime)
        {
            this.jsRuntime = jsRuntime;
        }

        public async Task LoadModuleAsync(CancellationToken cancellationToken = default)
        {
            module = await jsRuntime.InvokeAsync<IJSObjectReference>(
                "import", cancellationToken, SomeJsModule);
        }

        public async ValueTask DisposeAsync()
        {
            if (module is not null)
            {
                // The app hangs here
                await module.DisposeAsync();
            }
        }
}
  1. Register the above service as Transient or Scoped
  2. Inject the dependency into any page or component and call LoadModuleAsync()
  3. Deploy the project to Android 11 or earlier and make sure LoadModuleAsync() is called. Note: Any other JS invoke function could work so long as we're left with an IJSObjectReference
  4. Exit the application with the back button, then reopen it from the app list
  5. The app hangs on the loading screen

Link to public reproduction project repository

https://github.com/kanadaj/MauiHangRepro

Version with bug

7.0 (current)

Last version that worked well

7.0 (current)

Affected platforms

Android

Affected platform versions

Android 11 and down

Did you find any workaround?

The only workaround we've found is tying IJSObjectReference lifecycle to their holding component if they aren't on the home page. No idea about the home page. This also breaks most existing Blazor libraries on Android 11 and under.

Relevant log output

No response

@kanadaj kanadaj added the t/bug Something isn't working label Feb 23, 2023
@kanadaj
Copy link
Author

kanadaj commented Feb 23, 2023

Note, this issue affects most major Blazor libraries, including MudBlazor and Blazorise

@ScarletKuro
Copy link

ScarletKuro commented Apr 17, 2023

Any comments from Microsoft side what causing this issue?

Register the above service as Transient or Scoped

To my knowledge, IJSRuntime in MAUI is registered as scoped. Therefore, your services should also be registered as scoped. However, in the repro that you provided, I noticed that TestJsReferenceService is registered as scoped and this is not the issue.

I'm wondering if author considered trying the new API [JSImport] attribute in combination with JSHost.ImportAsync (was added in .NET 7) to see if it resolves the issue? It could be worth investigating.

@kanadaj
Copy link
Author

kanadaj commented Apr 17, 2023

@ScarletKuro From my investigations this is an Android lifecycle issue. Scopes are only ended when the app is resumed, not when it's closed. By then, the WebView itself is likely already disposed or otherwise made so that the IpcSender can't actually reach it, so the dispose calls hang. Since this doesn't throw an exception, I assume the WebView is only partially cleaned up but a handle still exists which returns a noop without a callback to notify the IpcSender that the message has been successfully sent.

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

Verified this on Visual Studio Enterprise 17.7.0 Preview 2.0. This issue does not repro on Android 13.0-API33, repro on Android 11.0-API30 with below Project:
MauiHangRepro.zip

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-blazor Blazor Hybrid / Desktop, BlazorWebView partner/android Issues for the Android SDK 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
7 participants