Skip to content

Commit

Permalink
[android] use a custom Resource.designer.cs
Browse files Browse the repository at this point in the history
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)
  • Loading branch information
jonathanpeppers committed Sep 21, 2021
1 parent b4f0e6f commit 2e64c99
Show file tree
Hide file tree
Showing 5 changed files with 433 additions and 0 deletions.
29 changes: 29 additions & 0 deletions DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

To workaround a performance issue, all `Resource.designer.cs`
generation is disabled for class libraries in this repo.

If you need to add a new `@(AndroidResource)` value to be used from C#
code in .NET MAUI:

1. Comment out the `<PropertyGroup>` in `Directory.Build.targets` that
sets `$(AndroidGenerateResourceDesigner)` and
`$(AndroidUseIntermediateDesignerFile)` to `false`.

2. Build .NET MAUI as you normally would. You will get compiler errors
about duplicate fields, but `obj\Debug\net6.0-android\Resource.designer.cs`
should now be generated.

3. Open `obj\Debug\net6.0-android\Resource.designer.cs`, and find the
field you need such as:

```csharp
// aapt resource value: 0x7F010000
public static int foo = 2130771968;
```

4. Copy this field to the `Resource.designer.cs` checked into source
control, such as: `src\Controls\src\Core\Platform\Android\Resource.designer.cs`

5. Restore the commented code in `Directory.Build.targets`.
5 changes: 5 additions & 0 deletions Directory.Build.targets
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
<Import Project="eng\AndroidX.targets" />
<Import Project="eng\Microsoft.Extensions.targets" />

<PropertyGroup Condition="'$(TargetPlatformIdentifier)' == 'Android' and '$(AndroidApplication)' != 'true'">
<AndroidGenerateResourceDesigner>false</AndroidGenerateResourceDesigner>
<AndroidUseIntermediateDesignerFile>false</AndroidUseIntermediateDesignerFile>
</PropertyGroup>

<!-- HACK: Prevent the Platform checks -->
<Target Name="BinPlaceBootstrapDll" />

Expand Down
143 changes: 143 additions & 0 deletions src/Compatibility/Core/src/Android/Resource.designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

140 changes: 140 additions & 0 deletions src/Controls/src/Core/Platform/Android/Resource.designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 2e64c99

Please sign in to comment.