Skip to content

[Android] Fix app crash caused by dynamic template switching in ListView#24808

Merged
PureWeen merged 8 commits intodotnet:mainfrom
BagavathiPerumal:fix-24701
Feb 18, 2025
Merged

[Android] Fix app crash caused by dynamic template switching in ListView#24808
PureWeen merged 8 commits intodotnet:mainfrom
BagavathiPerumal:fix-24701

Conversation

@BagavathiPerumal
Copy link
Copy Markdown
Contributor

@BagavathiPerumal BagavathiPerumal commented Sep 17, 2024

Exception I – Object reference not set to an instance of an object at GraphicsExtensions.AsColor :

During the execution of the device test case - ChangingTemplateTypeDoesNotCrash, a fatal exception was thrown, causing a crash.

Root Cause

The root cause of the NullReferenceException was identified as a failure to check for a valid context or color before calling the AsColor method. Specifically, the line of code responsible for this was:

var color = Current?.Windows?.Count > 0 
            ? Current.Windows[0].MauiContext.Context?.GetAccentColor() 
            : null;

In the UpdateSeparatorColor method, when returning null for Application.AccentColor , the method requests:
bline.SetBackgroundColor(separatorColor.ToPlatform(Application.AccentColor)); Since no default separator color was set, this led to the issue. The failure was particularly observed in our test case that enabled grouping, which is why only the ChangingTemplateTypeDoesNotCrash test case was failing.

Description of Change

To resolve this issue, the following modifications were implemented in the relevant code segment:

Default Color Implementation: A fallback mechanism was implemented to provide a default color (e.g., Color.FromRgba(50, 79, 133, 255)) when the window.count resource is unavailable. This approach prevents the application from crashing and ensures that the UI continues to function smoothly even in command line device test cases.

Exception II – The specified child already has a parent. you must call removeView() on the child’s parent first at ViewGroup.addViewInner.

Root cause

In the ViewCellRenderer class, the platform view was being recreated for each cell without properly removing the existing parent view (i.e., the ViewCellContainer ) before attempting to add the new view. This caused an exception:'The specified child already has a parent. You must call removeView() on the child's parent first. 'The issue arose due to improper handling of the platform view's parent when switching between different DataTemplates in a ListView using the RetainElement caching strategy in .NET MAUI.

Description of Issue Fix

The fix involved checking if the platformView already had a parent (i.e., ViewGroup) and ensuring that the parent was removed before adding the platformView to the new container. By introducing this conditional check, the system prevents the platform view from being associated with multiple parents, which caused the "The specified child already has a parent" exception. This change ensures that dynamic template changes in the ListView are handled correctly, improving view recycling and maintaining stable rendering without conflicts.

Tested the behavior in the following platforms.

  • Android
  • Windows
  • iOS
  • Mac

Issues Fixed

Fixes #24701

Output

Before fix:

ListView_BeforeFix

After fix:

ListView_AfterFix

Testcase:

The Testcase is already available for this fix. Please refer to the PR link below for reference.

Clear out prototype cell when changing ItemSource by PureWeen · Pull Request #24700 · dotnet/maui (github.com)

@dotnet-policy-service dotnet-policy-service bot added the community ✨ Community Contribution label Sep 17, 2024
@BagavathiPerumal BagavathiPerumal marked this pull request as ready for review September 17, 2024 16:33
@BagavathiPerumal BagavathiPerumal requested a review from a team as a code owner September 17, 2024 16:33
@PureWeen
Copy link
Copy Markdown
Member

PureWeen commented Sep 18, 2024

/azp run

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 3 pipeline(s).

Copy link
Copy Markdown
Member

@PureWeen PureWeen left a comment

Choose a reason for hiding this comment

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

@BagavathiPerumal
Copy link
Copy Markdown
Contributor Author

Can you re-enable this device test for android?

https://github.com/dotnet/maui/pull/24700/files#diff-9ef75bbb1d7dbaf152e43bba50d7dfc6ed46b9db1b351e0ff5da3272718f811aR36-R41

@PureWeen, I have modified changes. Could you please validate it once and let me know if there are any further concerns.

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 3 pipeline(s).

Copy link
Copy Markdown
Member

@PureWeen PureWeen left a comment

Choose a reason for hiding this comment

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

Looks like the test is failing

09-23 03:31:56.106  4471  4471 E AndroidRuntime: FATAL EXCEPTION: main
09-23 03:31:56.106  4471  4471 E AndroidRuntime: Process: com.microsoft.maui.controls.devicetests, PID: 4471
09-23 03:31:56.106  4471  4471 E AndroidRuntime: android.runtime.JavaProxyThrowable: [System.NullReferenceException]: Object reference not set to an instance of an object.
09-23 03:31:56.106  4471  4471 E AndroidRuntime: 	at Microsoft.Maui.Graphics.Platform.GraphicsExtensions.AsColor(Unknown Source:0)
09-23 03:31:56.106  4471  4471 E AndroidRuntime: 	at Microsoft.Maui.Platform.ColorExtensions.ToPlatform(Unknown Source:0)
09-23 03:31:56.106  4471  4471 E AndroidRuntime: 	at Microsoft.Maui.Platform.ColorExtensions.ToPlatform(Unknown Source:0)
09-23 03:31:56.106  4471  4471 E AndroidRuntime: 	at Microsoft.Maui.Controls.Handlers.Compatibility.ListViewAdapter.UpdateSeparatorColor(Unknown Source:0)
09-23 03:31:56.106  4471  4471 E AndroidRuntime: 	at Microsoft.Maui.Controls.Handlers.Compatibility.ListViewAdapter.GetView(Unknown Source:0)
09-23 03:31:56.106  4471  4471 E AndroidRuntime: 	at Microsoft.Maui.Controls.Handlers.Compatibility.ListViewRenderer.GetDesiredSize(Unknown Source:0)
09-23 03:31:56.106  4471  4471 E AndroidRuntime: 	at Microsoft.Maui.Controls.Handlers.Compatibility.VisualElementRenderer`1[[Microsoft.Maui.Controls.ListView, Microsoft.Maui.Controls, Version=1.0.0.0, Culture=neutral, 

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 3 pipeline(s).

@jfversluis
Copy link
Copy Markdown
Member

/rebase

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 3 pipeline(s).

@praveenkumarkarunanithi
Copy link
Copy Markdown
Contributor

/rebase

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 3 pipeline(s).

@PureWeen PureWeen marked this pull request as draft October 4, 2024 22:16

if (platformView.Parent != null && platformView.Parent is ViewGroup parentViewGroup)
{
parentViewGroup.RemoveView(platformView);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The RemoveFromParent extension method can be used here.

platformView.RemoveFromParent();

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@jsuarezruiz, Thank you for your suggestion regarding the use of the RemoveFromParent extension method. I implemented this fix; however, the device test case is still experiencing app crashes.

Upon further investigation, we discovered that the crash occurs during the execution of the ChangingTemplateTypeDoesNotCrash test case. The issue arises because grouping is enabled, but the data provided is not in a grouped format, leading to the app crashing when running the command-line device test case.
We validated this by running a standalone sample with the fix applied, and the same test case also passed without any issues when checked manually. The crashes seem to only occur during command-line execution.

We are currently delving deeper into this issue and would greatly appreciate any insights you may have.

@BagavathiPerumal
Copy link
Copy Markdown
Contributor Author

Looks like the test is failing

09-23 03:31:56.106  4471  4471 E AndroidRuntime: FATAL EXCEPTION: main
09-23 03:31:56.106  4471  4471 E AndroidRuntime: Process: com.microsoft.maui.controls.devicetests, PID: 4471
09-23 03:31:56.106  4471  4471 E AndroidRuntime: android.runtime.JavaProxyThrowable: [System.NullReferenceException]: Object reference not set to an instance of an object.
09-23 03:31:56.106  4471  4471 E AndroidRuntime: 	at Microsoft.Maui.Graphics.Platform.GraphicsExtensions.AsColor(Unknown Source:0)
09-23 03:31:56.106  4471  4471 E AndroidRuntime: 	at Microsoft.Maui.Platform.ColorExtensions.ToPlatform(Unknown Source:0)
09-23 03:31:56.106  4471  4471 E AndroidRuntime: 	at Microsoft.Maui.Platform.ColorExtensions.ToPlatform(Unknown Source:0)
09-23 03:31:56.106  4471  4471 E AndroidRuntime: 	at Microsoft.Maui.Controls.Handlers.Compatibility.ListViewAdapter.UpdateSeparatorColor(Unknown Source:0)
09-23 03:31:56.106  4471  4471 E AndroidRuntime: 	at Microsoft.Maui.Controls.Handlers.Compatibility.ListViewAdapter.GetView(Unknown Source:0)
09-23 03:31:56.106  4471  4471 E AndroidRuntime: 	at Microsoft.Maui.Controls.Handlers.Compatibility.ListViewRenderer.GetDesiredSize(Unknown Source:0)
09-23 03:31:56.106  4471  4471 E AndroidRuntime: 	at Microsoft.Maui.Controls.Handlers.Compatibility.VisualElementRenderer`1[[Microsoft.Maui.Controls.ListView, Microsoft.Maui.Controls, Version=1.0.0.0, Culture=neutral, 

@PureWeen, The test failure is caused by the UpdateSeparatorColor method, which calls the default separator color through
Application.AccentColor. We have addressed this issue by implementing a fix at this line of code, where a default color is now provided to prevent a NullReferenceException.

@BagavathiPerumal BagavathiPerumal marked this pull request as ready for review October 11, 2024 09:47
@jsuarezruiz
Copy link
Copy Markdown
Contributor

/azp run

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 3 pipeline(s).

@praveenkumarkarunanithi
Copy link
Copy Markdown
Contributor

/rebase

@jsuarezruiz
Copy link
Copy Markdown
Contributor

jsuarezruiz commented Oct 14, 2024

/azp run

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 3 pipeline(s).

var platformView = _viewCell.View.ToPlatform(Element.FindMauiContext());
_viewHandler = (IPlatformViewHandler)_viewCell.View.Handler;

if (platformView?.Parent is ViewGroup)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If you're using RemoveFromParent I don't think you need the if statement here

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@PureWeen, I have modified the code based on your concerns, could you please validate once and let me know if there are any concerns.

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 3 pipeline(s).

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 3 pipeline(s).

@@ -17,7 +17,7 @@ public static AColor ToPlatform(this Color self, int defaultColorResourceId, Con
=> self?.ToPlatform() ?? new AColor(ContextCompat.GetColor(context, defaultColorResourceId));

public static AColor ToPlatform(this Color? self, Color defaultColor)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This extension is used every time we convert a MAUI Color to an Android Color (everywhere, in any View). If the color is null (default value) and not pass a default value, will use this #ff33b5e5 color
image
And could cause unexpected behaviors.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

In this case, the separator line must use the color from the SeparatorColor property, but if it is not set and is null, a color from an Android Resource such as global::Android.Resource.Attribute.ColorAccent should be used. So, we need to identify why the AccentColor is null.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@jfversluis, Thank you for the feedback! I've updated the code to address the potential null values effectively and to avoid unintended defaulting to #ff33b5e5 across views. Specifically, I’ve ensured that the color retrieval method now uses the primary context when available via Current.Windows[0].MauiContext.Context, and if not, it falls back to Current?.FindMauiContext()?.Context (During device tests).

Please review and let us know if any further changes are required.

@jsuarezruiz
Copy link
Copy Markdown
Contributor

/azp run

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 3 pipeline(s).

return Current.Windows[0].MauiContext.Context?.GetAccentColor();
var context = Current?.Windows?.Count > 0 ? Current.Windows[0].MauiContext.Context : Current?.FindMauiContext()?.Context;
if (context is not null)
return context?.GetAccentColor();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

If the current Window MauiContext is null, we keep having the same null Color exception, right?
We could fallback to Application.Current.FindMauiContext() in that case.

Copy link
Copy Markdown
Contributor

@SuthiYuvaraj SuthiYuvaraj Oct 29, 2024

Choose a reason for hiding this comment

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

@jsuarezruiz , Thank you for the feedback. I’ve added a fallback to ensure context is retrieved effectively in all cases, even when the primary window context might not be accessible.

Please review and let us know if any further changes are required.

@jsuarezruiz
Copy link
Copy Markdown
Contributor

/azp run

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 3 pipeline(s).

@PureWeen PureWeen added this to the .NET 9 Servicing milestone Oct 31, 2024
@praveenkumarkarunanithi
Copy link
Copy Markdown
Contributor

/rebase

@jsuarezruiz
Copy link
Copy Markdown
Contributor

/azp run

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 3 pipeline(s).

@praveenkumarkarunanithi
Copy link
Copy Markdown
Contributor

/rebase

@jsuarezruiz
Copy link
Copy Markdown
Contributor

/azp run

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 3 pipeline(s).

@sheiksyedm sheiksyedm added the partner/syncfusion Issues / PR's with Syncfusion collaboration label Dec 6, 2024
#elif ANDROID
if (Current?.Windows?.Count > 0)
return Current.Windows[0].MauiContext.Context?.GetAccentColor();
var context = (Current?.Windows?.Count > 0 && Current.Windows[0].MauiContext is not null) ? Current.Windows[0].MauiContext.Context : Current?.FindMauiContext()?.Context;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Did you validate if Current?.FindMauiContext()?.Context will return an accent color? That's the application context on the Activity so I feel like it won't have the theme resources loaded into it.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@PureWeen

Thank you for the feedback. Here are the findings:

Windows.Count Unavailability: The condition where Current?.Windows?.Count is not available and the fallback to FindMauiContext()?.Context is triggered occurs specifically during Android device tests. Notably, this behavior is not observed when running individual device tests locally, suggesting it is tied to the test execution environment rather than regular application behavior.

Accent Color Validation: When falling back to FindMauiContext()?.Context, we are still able to retrieve an accent color, which is: [Color: Red=0.101960786, Green=0.4509804, Blue=0.9098039, Alpha=1]

In contrast, when Windows.Count is available and the primary path is used (Current.Windows[0].MauiContext.Context), the accent color retrieved is: [Color: Red=0.20392157, Green=0.59607846, Blue=0.85882354, Alpha=1]

While there is a slight variation between these colors, both provide valid accent values for their respective scenarios.
Please let us know if there’s anything further you would like us to address.

@PureWeen PureWeen merged commit f06c119 into dotnet:main Feb 18, 2025
@github-actions github-actions bot locked and limited conversation to collaborators Mar 21, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-controls-listview ListView and TableView community ✨ Community Contribution partner/syncfusion Issues / PR's with Syncfusion collaboration platform/android

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

[Android] ListView + DataTemplateSelector + HasUnevenRows issue

8 participants