Skip to content

Fix Android crash on back pressed#20963

Open
kerams wants to merge 1 commit intoAvaloniaUI:masterfrom
kerams:aaa
Open

Fix Android crash on back pressed#20963
kerams wants to merge 1 commit intoAvaloniaUI:masterfrom
kerams:aaa

Conversation

@kerams
Copy link
Contributor

@kerams kerams commented Mar 22, 2026

What does the pull request do?

Fixes #20883.

What is the current behavior?

Crash on back pressed/minimize.

What is the updated/expected behavior with this PR?

No crash.

How was the solution implemented (if it's not obvious)?

_view isn't set to null on TopLevelImpl dispose. Since the render call that causes the crash only accesses .NET resources in AvaloniaView, this should be fine. On the flip side, the view's Java handle might not be released immediately when the main activity is destroyed.

Checklist

Breaking changes

No.

@kerams
Copy link
Contributor Author

kerams commented Mar 22, 2026

An alternative solution suggested by Opus would be to add Closed?.Invoke(); to TopLevelImpl.Dispose. This removes it from the rendering pipeline, so there's no crash anymore, but then we get into a deadlock instead (the application is frozen on the splash screen when brought back into the foreground)

  • Back button → SurfaceDestroyed(ISurfaceHolder) fires → OnVisibilityChanged(false) → view unsubscribed from ChoreographerTimer → render thread blocks on _event.WaitOne() (dead)
  • OnDestroy() → AvaloniaView.Dispose() → EmbeddableControlRoot.Dispose() → TopLevelImpl.Dispose()
  • With Closed?.Invoke(): HandleClosed() → Renderer.Dispose() → SyncDisposeCompositionTarget(CompositionTarget) → batch.Processed.Wait() but render thread is dead → Deadlock (app frozen)

The solution to that is

// SurfaceDestroyed fires before OnDestroy and unsubscribes the view from the
// ChoreographerTimer, stopping the render thread. Disposing the root triggers
// HandleClosed ? Renderer.Dispose ? SyncDisposeCompositionTarget, which needs
// the render thread alive to process the batch. Temporarily re-subscribe so the
// choreographer keeps firing while the synchronous wait completes.
IDisposable? tempSubscription = null;
if (AndroidPlatform.Timer is { } timer)
    tempSubscription = timer.SubscribeView(this);

tempSubscription?.Dispose();

in AvaloniaView.Dispose. Let me know if you prefer this, the line removal or something else.

@avaloniaui-bot
Copy link

You can test this PR using the following package version. 12.0.999-cibuild0063809-alpha. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Android crash on back pressed

2 participants