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

[android] use a custom Resource.designer.cs #2606

Merged
merged 1 commit into from
Sep 21, 2021

Conversation

jonathanpeppers
Copy link
Member

@jonathanpeppers jonathanpeppers commented Sep 20, 2021

Description of Change

Context: https://github.com/jonathanpeppers/CustomResourceDesigner
Context: dotnet/android#6310

We found a systemic problem with Xamarin.Android class libraries:

  • Include AndroidX & Google Material
  • Include at least one @(AndroidResource) and use the ID from C#
  • Resource.designer.cs has 2,700+ fields. That's a lot!

This problem compounds itself as you include more class libraries that
depend on each other. The main app will end up repeatedly setting
these fields at startup for each library that contains fields in
Resource.designer.cs...

Reviewing the .NET MAUI fields, I found:

src\Core\src\obj\Debug\net6.0-android\Resource.designer.cs
5310
src\Controls\src\Core\obj\Debug\net6.0-android\Resource.designer.cs
5167 fields
src\Controls\src\Xaml\obj\Release\net6.0-android\Resource.designer.cs
5167 fields
src\Compatibility\Core\src\obj\Debug\net6.0-android\Resource.designer.cs
5333 fields
src\Essentials\src\obj\Debug\net6.0-android\Resource.designer.cs
204 fields

In fact, I found 21,497 fields were set at startup for a dotnet new maui app in Resource.designer.cs!

This shows up as a "hot path" if you profile Android startup:

image

In many projects you can simply set $(AndroidGenerateResourceDesigner)
to false, but the issue is .NET MAUI actually uses some of the C#
Resource.designer.cs values at runtime.

So to solve the problem here, I came up with a new pattern:

https://github.com/jonathanpeppers/CustomResourceDesigner

We can copy the contents of Resource.designer.cs and manually delete
all the fields we don't need. This allows
$(AndroidGenerateResourceDesigner) to be turned off.

We are working on a long-term solution for this issue in
Xamarin.Android, but we can do this workaround in .NET MAUI now.

Results

Building a dotnet new maui then dotnet build -c Release and
running on a Pixel 5.

Before:

  • 21,497 fields set at startup in UpdateIdValues()
  • Activity Displayed: 1s454ms
  • .apk size: 17300275 bytes

After:

  • 65 fields set at startup in UpdateIdValues()
  • Activity Displayed: 1s079ms
  • .apk size: 16677683 bytes
> apkdiff -f before.apk after.apk
Size difference in bytes ([*1] apk1 only, [*2] apk2 only):
-         233 assemblies/Microsoft.Maui.Controls.Compatibility.Android.FormsViewGroup.dll
-       5,264 assemblies/Microsoft.Maui.Essentials.dll
-     103,010 assemblies/Microsoft.Maui.dll
-     103,260 assemblies/Microsoft.Maui.Controls.Compatibility.dll
-     103,811 assemblies/Microsoft.Maui.Controls.Xaml.dll
-     106,127 assemblies/Microsoft.Maui.Controls.dll
-     201,031 assemblies/foo.dll
Summary:
+           0 Other entries 0.00% (of 2,139,558)
-     622,736 Assemblies -6.93% (of 8,987,664)
+           0 Dalvik executables 0.00% (of 6,440,988)
+           0 Shared libraries 0.00% (of 9,860,264)
-   1,340,928 Uncompressed assemblies -6.55% (of 20,465,016)
-     622,592 Package size difference -3.60% (of 17,300,275)

Additions made

None

PR Checklist

  • Targets the correct branch
  • Tests are passing (or failures are unrelated)
  • Targets a single property for a single control (or intertwined few properties)
  • Adds the property to the appropriate interface
  • Avoids any changes not essential to the handler property
  • Adds the mapping to the PropertyMapper in the handler
  • Adds the mapping method to the Android, iOS, and Standard aspects of the handler
  • Implements the actual property updates (usually in extension methods in the Platform section of Core)
  • Tags ported renderer methods with [PortHandler]
  • Adds an example of the property to the sample project (MainPage)
  • Adds the property to the stub class
  • Implements basic property tests in DeviceTests

Does this PR touch anything that might affect accessibility?

No

@jonathanpeppers
Copy link
Member Author

I need to enable $(AndroidGenerateResourceDesigner) in various test projects:

/Users/builder/azdo/_work/1/s/src/Compatibility/ControlGallery/src/Android/CustomRenderers.cs(429,46): error CS0117: 'Resource.Id' does not contain a definition for 'Image'

@jonathanpeppers jonathanpeppers force-pushed the resource.designer.cs branch 2 times, most recently from 3b05b51 to 2e64c99 Compare September 21, 2021 14:32
Context: https://github.com/jonathanpeppers/CustomResourceDesigner
Context: dotnet/android#6310

We found a systemic problem with Xamarin.Android class libraries:

* Include AndroidX & Google Material
* Include at least one `@(AndroidResource)` and use the ID from C#
* `Resource.designer.cs` has 2,700+ fields. That's a lot!

This problem compounds itself as you include more class libraries that
depend on each other. The main app will end up repeatedly setting
these fields at startup for each library that contains fields in
`Resource.designer.cs`...

Reviewing the .NET MAUI fields, I found:

    src\Core\src\obj\Debug\net6.0-android\Resource.designer.cs
    5310
    src\Controls\src\Core\obj\Debug\net6.0-android\Resource.designer.cs
    5167 fields
    src\Controls\src\Xaml\obj\Release\net6.0-android\Resource.designer.cs
    5167 fields
    src\Compatibility\Core\src\obj\Debug\net6.0-android\Resource.designer.cs
    5333 fields
    src\Essentials\src\obj\Debug\net6.0-android\Resource.designer.cs
    204 fields

In fact, I found 21,497 fields were set at startup for a `dotnet new
maui` app in `Resource.designer.cs`!

In many projects you can simply set `$(AndroidGenerateResourceDesigner)`
to `false`, but the issue is .NET MAUI actually uses some of the C#
`Resource.designer.cs` values at runtime.

So to solve the problem here, I came up with a new pattern:

https://github.com/jonathanpeppers/CustomResourceDesigner

We can copy the contents of `Resource.designer.cs` and manually delete
all the fields we don't need. This allows
`$(AndroidGenerateResourceDesigner)` to be turned off.

We are working on a long-term solution for this issue in
Xamarin.Android, but we can do this workaround in .NET MAUI now.

~~ Results ~~

Building a `dotnet new maui` then `dotnet build -c Release` and
running on a Pixel 5.

Before:

* 21,497 fields set at startup in UpdateIdValues()
* Activity Displayed: 1s454ms
* .apk size: 17300275 bytes

After:

* 65 fields set at startup in UpdateIdValues()
* Activity Displayed: 1s079ms
* .apk size: 16677683 bytes

    > apkdiff -f before.apk after.apk
    Size difference in bytes ([*1] apk1 only, [*2] apk2 only):
    -         233 assemblies/Microsoft.Maui.Controls.Compatibility.Android.FormsViewGroup.dll
    -       5,264 assemblies/Microsoft.Maui.Essentials.dll
    -     103,010 assemblies/Microsoft.Maui.dll
    -     103,260 assemblies/Microsoft.Maui.Controls.Compatibility.dll
    -     103,811 assemblies/Microsoft.Maui.Controls.Xaml.dll
    -     106,127 assemblies/Microsoft.Maui.Controls.dll
    -     201,031 assemblies/foo.dll
    Summary:
    +           0 Other entries 0.00% (of 2,139,558)
    -     622,736 Assemblies -6.93% (of 8,987,664)
    +           0 Dalvik executables 0.00% (of 6,440,988)
    +           0 Shared libraries 0.00% (of 9,860,264)
    -   1,340,928 Uncompressed assemblies -6.55% (of 20,465,016)
    -     622,592 Package size difference -3.60% (of 17,300,275)
@jonathanpeppers
Copy link
Member Author

Manually checked Microsoft.Maui.Core.Runtime.android.6.0.101-preview.9.7280.nupkg from the PR build, and ILSpy shows it has a small Resource class:

// Microsoft.Maui.Resource
using System.CodeDom.Compiler;
using Android.Runtime;

[GeneratedCode("Xamarin.Android.Build.Tasks", "12.0.99.31")]
public class Resource
{
	public class Attribute
	{
		public static int colorSwitchThumbNormal;

		public static int maui_splash;

		public static int scrollViewStyle;

		static Attribute()
		{
			colorSwitchThumbNormal = 2130903268;
			maui_splash = 2130903657;
			scrollViewStyle = 2130903809;
			ResourceIdManager.UpdateIdValues();
		}
	}

	public class Animation
	{
		public static int nav_default_enter_anim;

		public static int nav_default_exit_anim;

		public static int nav_default_pop_enter_anim;

		public static int nav_default_pop_exit_anim;

		static Animation()
		{
			nav_default_enter_anim = 2130772000;
			nav_default_exit_anim = 2130772001;
			nav_default_pop_enter_anim = 2130772002;
			nav_default_pop_exit_anim = 2130772003;
			ResourceIdManager.UpdateIdValues();
		}
	}

	public class Drawable
	{
		public static int abc_ic_clear_material;

		static Drawable()
		{
			abc_ic_clear_material = 2131165213;
			ResourceIdManager.UpdateIdValues();
		}
	}

	public class Id
	{
		public static int automation_tag_id;

		public static int maui_toolbar;

		public static int nav_host;

		public static int search_button;

		public static int search_close_btn;

		static Id()
		{
			automation_tag_id = 2131230809;
			maui_toolbar = 2131230983;
			nav_host = 2131231021;
			search_button = 2131231082;
			search_close_btn = 2131231083;
			ResourceIdManager.UpdateIdValues();
		}
	}

	public class Layout
	{
		public static int navigationlayout;

		static Layout()
		{
			navigationlayout = 2131427417;
			ResourceIdManager.UpdateIdValues();
		}
	}

	public class Style
	{
		public static int Maui_MainTheme_NoActionBar;

		public static int scrollViewTheme;

		static Style()
		{
			Maui_MainTheme_NoActionBar = 2131624184;
			scrollViewTheme = 2131624673;
			ResourceIdManager.UpdateIdValues();
		}
	}

	static Resource()
	{
		ResourceIdManager.UpdateIdValues();
	}
}

@jonathanpeppers jonathanpeppers marked this pull request as ready for review September 21, 2021 17:04
@@ -71,3 +71,32 @@ To build and run WinUI 3 support, please install the additional components menti
dotnet tool restore
dotnet cake --target=VS-WINUI
```

### Android
Copy link
Member Author

@jonathanpeppers jonathanpeppers Sep 21, 2021

Choose a reason for hiding this comment

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

@PureWeen are you the one that would know: How often do you add new @(AndroidResource) files?

Are the steps below reasonable?

Copy link
Member

Choose a reason for hiding this comment

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

Rarely, especially once we're GA

I'd say these steps are fine

Copy link
Member

Choose a reason for hiding this comment

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

As luck would have it, I have a PR currently open that this will break :-)

So, I'll be able to give the steps a test right after merging.

@jonathanpeppers jonathanpeppers merged commit 386af46 into dotnet:main Sep 21, 2021
@jonathanpeppers jonathanpeppers deleted the resource.designer.cs branch September 21, 2021 18:56
@Redth Redth added this to the 6.0.101-preview.9 milestone Sep 27, 2021
@github-actions github-actions bot locked and limited conversation to collaborators Dec 24, 2023
@samhouts samhouts added the fixed-in-6.0.101-preview.10 Look for this fix in 6.0.101-preview.10! label Aug 2, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
fixed-in-6.0.101-preview.10 Look for this fix in 6.0.101-preview.10! platform/android 🤖
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants