[Android] Skip DisposeWindowScope on Destroying to prevent ObjectDisposedException#33765
[Android] Skip DisposeWindowScope on Destroying to prevent ObjectDisposedException#33765PureWeen merged 12 commits intodotnet:mainfrom
Conversation
|
It still crashes for me when I open the host app for example, press the physical back button, and then reopen the app. |
There was a problem hiding this comment.
Pull request overview
This PR adjusts the window-service-scope disposal behavior to avoid ObjectDisposedException crashes on Android when bringing the app back to the foreground, while preserving the leak fix for other platforms.
Changes:
- Wraps
MauiContext.DisposeWindowScope()inIWindow.Destroying()with#if !ANDROIDso that window scopes are still disposed on non-Android platforms. - Leaves Android window scopes undisposed during
Destroying()to account for the single-Activity model where windows are reused across lifecycle events, preventing disposal of still-needed scopes.
@kubaflo I wasn’t able to reproduce the crash in the host app using the back button + reopen scenario. I tested this on multiple devices (Pixel 9, Pixel 4a, Nexus 5, all on API 36) and couldn’t observe the issue. |
Hmmm maybe try with API30? Screen.Recording.2026-01-29.at.15.06.54.mov |
|
@praveenkumarkarunanithi Screen.Recording.2026-01-29.at.15.37.36.mov |
I think we probably need a fix like this What's the characterisitc here of "single-Activity"? is the Activity destroyed a new activity created? Why is Destroying being called it the "Activity" itself isn't being destroyed |
|
@praveenkumarkarunanithi I pushed up a device test that might help repro Can you let me know if this seems accurate to the bug? or helps navigate to a way to at least repro in a device test? |
On Android for this bug, pressing back calls |
Tested your device tests – one worked properly so I optimized and kept it. Also added another device test to cover the HostApp scenario reported by @kubaflo. Both tests now validate the fix. |
I've updated the PR with additional fixes to handle that case. |
|
Can we get this merged for .NET 10.0 SR4 as this is blocking us from updating to .NET 10? |
|
/rebase |
1dec6a8 to
34bc0e4
Compare
🤖 AI Summary📊 Expand Full Review🔍 Pre-Flight — Context & Validation📝 Review Session — Delete WindowTests.Issue33187.cs ·
|
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| PR | PR #33765 | Skip DisposeWindowScope() on Android in Window.Destroying() using #if !ANDROID directive. Preserves DisconnectHandler call. |
⏳ PENDING (Gate) | Window.cs (+8/-3), Application.cs (+6) |
Original PR - Android-only fix for single-Activity model |
Note: try-fix candidates (1, 2, 3...) are added during Phase 3. PR's fix is reference only.
Exhausted: No
Selected Fix: [PENDING]
📋 PR Finalization ReviewTitle: ✅ GoodCurrent: Description: ✅ ExcellentDescription needs updates. See details below. Code Review: ✅ PassedCode Review: PR #33765Date: 2026-02-10 OverviewPR Title: [Android] Skip DisposeWindowScope on Destroying to prevent ObjectDisposedException ✅ Positive Observations1. Correct Platform-Specific ImplementationWindow.cs (Lines 547-558) // On Android, preserve window in collection to enable reuse when Activity is recreated
#if !ANDROID
Application?.RemoveWindow(this);
#endif
var mauiContext = Handler?.MauiContext as MauiContext;
Handler?.DisconnectHandler();
// On Android, preserve window scope to enable reuse when Activity is recreated
#if !ANDROID
mauiContext?.DisposeWindowScope();
#endifStrengths:
2. Window Reuse LogicApplication.cs (Lines 471-475) // On Android, reuse existing window when Activity is recreated due to lifecycle changes
#if ANDROID
if (window == null && _windows.Count > 0)
window = _windows[0];
#endifStrengths:
3. Test CoverageWindowTests.Android.cs Test 1:
Test 2:
Strengths:
🟡 Minor IssuesIssue 1: Trailing WhitespaceFile: Problem: Application?.RemoveWindow(this);
// ^^^^ trailing tab characterRecommendation: Application?.RemoveWindow(this);Impact: Low - cosmetic only, but violates clean code practices Fix: # Remove trailing whitespace
sed -i '' 's/[[:space:]]*$//' src/Controls/src/Core/Window/Window.cs🔵 Architectural ObservationsPlatform Lifecycle DivergenceThis PR correctly handles a fundamental architectural difference:
Why This Matters:
📋 Test Strategy AssessmentWhy No Full Integration Test?The PR description correctly states:
Assessment: This is accurate. The added device tests validate:
These are the testable assertions. The full integration scenario (navigate away, navigate back, resolve services) requires:
Alternative Testing: 🎯 Risk AssessmentLow Risk Areas
Medium Risk Areas
Mitigation:
SummaryOverall Code Quality: ✅ GoodStrengths:
Minor Issues:
Architectural Considerations:
Recommendation: Approve with Optional CleanupBefore Merge (Optional):
After Merge:
Code Style Compliance✅ Follows .NET MAUI patterns Future Considerations
|
This is highly annoying for users indeed (#33923 (comment)) |
|
/azp run maui-pr-uitests |
|
Azure Pipelines successfully started running 1 pipeline(s). |
PureWeen
left a comment
There was a problem hiding this comment.
Can we reduce this change down to just the if/def on the
DisposeWindowScope?
I'm pretty sure the other Fixes are addressing an issue thats been around since net6 right?
Users can workaround that for now by overriding CreateWindow
I worry about the other changes breaking multi window scenarios
Where users might be spawning multiple windows
Getting rid of the DisposeWindowScope will be a bit more precise to the current blocker
@PureWeen You're right - the #if !ANDROID around DisposeWindowScope() is the targeted fix for this issue. |
|
/azp run maui-pr-uitests |
|
Azure Pipelines successfully started running 1 pipeline(s). |
|
/azp run maui-pr-devicetests |
|
Azure Pipelines successfully started running 1 pipeline(s). |
Note
Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue. Thank you!
Root Cause
PR #30196 introduced unconditional disposal of the window service scope in
Window.Destroying()viamauiContext?.DisposeWindowScope(). On Android, when users press the back button, the Activity is destroyed andWindow.Destroying()fires. However, the Application process survives, and pages (likeMainPageorShell) continue to hold references to scoped services resolved from the window scope. When the app reopens and these pages render, they access the disposed services, causingObjectDisposedException.Description of Change
Added a platform-specific guard around
DisposeWindowScope()inIWindow.Destroying():Issues Fixed
Fixes #33187
Fixes #33597
Platforms Tested
Note: Android-only fix via
#if !ANDROIDdirective due to Android's single-Activity window reuse model. Other platforms' code unchanged, no testing required.Breaking PR
PR #30196
Screenshots
BeforeFix.mov
AfterFix.mov