{"id":48597,"date":"2023-11-20T10:05:00","date_gmt":"2023-11-20T18:05:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/dotnet\/?p=48597"},"modified":"2023-11-20T10:05:00","modified_gmt":"2023-11-20T18:05:00","slug":"android-resource-designer-dotnet-8","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/android-resource-designer-dotnet-8\/","title":{"rendered":"Improvements &amp; Changes in Android resource generation in .NET 8"},"content":{"rendered":"<p>With the release of .NET 8 we are introducing a new system for generating the C# code used to access Android Resources. The system which generated a <code>Resource.designer.cs<\/code> file in Xamarin.Android, .NET 6 and .NET 7 has been\u00a0deprecated. The new system generates a single <code>_Microsoft.Android.Resource.Designer<\/code> assembly. \u00a0This will contain all the final resource classes for every assembly.<\/p>\n<h3>What are Android Resources?<\/h3>\n<p>All Android applications will have some sort of user interface resources in them. They often have the user interface <code>layouts<\/code> in the form of XML file, <code>images<\/code> and icons in the form of png or svg files and <code>values<\/code> which contain things like styles and theming. See the <a href=\"https:\/\/developer.android.com\/guide\/topics\/resources\/providing-resources\">Google&#8217;s documentation<\/a> for an in-depth look at Android resources.<\/p>\n<p>Part of the android build process is to compile these resources into a binary form, this is done by the android sdk tool <a href=\"https:\/\/developer.android.com\/tools\/aapt2\"><code>aapt2<\/code><\/a>.\nTo access these resources android exposed an API which allows you to pass an integer <code>id<\/code> to retrieve the resource.<\/p>\n<pre><code class=\"language-csharp\">SetContentView (2131492864);<\/code><\/pre>\n<p>As part of the <a href=\"https:\/\/developer.android.com\/tools\/aapt2\"><code>aapt2<\/code><\/a> build process the file <code>R.txt<\/code> is generated which contains a mapping from the &#8220;string&#8221; name of the resource to the id. For example <code>layout\/Main.xml<\/code> might map to the id 2131492864. To access this data from C# we need a way to expose this in code. This is handled by a <code>Resource<\/code> class in the projects <code>$(RootNamespace)<\/code>. We take the values from the <code>R.txt<\/code> and expose them in this class.\nIn the system that shipped with .NET 7 and prior releases, this class was written to the <code>Resource.designer.cs<\/code> file. And it allowed users to write maintainable code by not hard coding ids. So the call from above would actually look like this<\/p>\n<pre><code class=\"language-csharp\">SetContentView (Resource.Layout.Main);<\/code><\/pre>\n<p>The <code>Resource.Id.Main<\/code> would map to the Id which was produced by <a href=\"https:\/\/developer.android.com\/tools\/aapt2\"><code>aapt2<\/code><\/a>.<\/p>\n<h3>Why make this new system?<\/h3>\n<p>The old system had some issues which impact both app size and startup performance. In the old system every Android assembly had its own set of Resource classes in it. So we effectively had duplicate code everywhere. So if you used AndroidX in your project every assembly which referenced AndroidX would have a Resource designer Id class like this<\/p>\n<pre><code class=\"language-csharp\">public class Resource {\n    public class Id {\n        \/\/ aapt resource value: 0x7F0A0005\n        public const int seekBar = 2131361797;\n        \/\/ aapt resource value: 0x7F0A0006\n        public const int menu = 2131361798;\n    }\n}<\/code><\/pre>\n<p>This code would be duplicated in each library. There are may other classes such as <em>Layout\/Menu\/Style<\/em>, all of which have these duplicate code in them.<\/p>\n<p>Also each of these Resource classes needed to be updated at runtime to have the correct values. This is because it is only when we build the final app and generate the <code>R.txt<\/code> file do we know the ids for each of these resources. So the application Resource classes are the only ones with the correct ids.<\/p>\n<p>The old system used a method called <code>UpdateIdValues<\/code> which was called on startup. This method would go through ALL the library projects and update the resource ids to match the ones in the application. Depending on the size of the application this can cause significant delays to startup. Here is an example of the code in this method<\/p>\n<pre><code class=\"language-csharp\">public static void UpdateIdValues()\n{\n    global::Library.Resource.Id.seekBar = global::Foo.Foo.Resource.Id.seekBar;\n    global::Library.Resource.Id.menu = global::Foo.Foo.Resource.Id.menu;\n}<\/code><\/pre>\n<p>Even worse because of the <code>UpdateIdValues<\/code> code the trimmer could not remove any of these classes. So even if the application only used one or two fields , all of them are preserved.\u00a0<\/p>\n<p>The new system reworks all of this to make it trimmer-friendly, almost ALL of the code shown above will no longer be produced.\nThere is no need to even have an <code>UpdateIdValues<\/code> call at all. This will improve both app size and startup time.\u00a0<\/p>\n<h3>How it works<\/h3>\n<p>.NET 8 Android will have the MSBuild property <code>$(AndroidUseDesignerAssembly)<\/code> set to <code>true<\/code> by default. This will turn off the old system completely. Manually changing this property to <code>false<\/code> will re-enable the old system.<\/p>\n<p>The new system relies on parsing the <code>R.txt<\/code> file which <a href=\"https:\/\/developer.android.com\/tools\/aapt2\"><code>aapt2<\/code><\/a> generates as part of the build process. Just before the call to run the C# compiler, the <code>R.txt<\/code> file will be parsed and generate a new assembly. The assembly will be saved in the the <code>IntermediateOutputPath<\/code>. It will also be automatically added to the list of References for the app or library.\u00a0<\/p>\n<p>For library projects we generate a reference assembly rather than a full assembly. This signals to the compiler that this assembly will be replaced at runtime. (A <a href=\"https:\/\/learn.microsoft.com\/dotnet\/standard\/assembly\/reference-assemblies\">reference assembly<\/a> is an assembly which contains an assembly-level <a href=\"https:\/\/learn.microsoft.com\/dotnet\/api\/system.runtime.compilerservices.referenceassemblyattribute?view=net-7.0\">ReferenceAssemblyAttribute<\/a>.)<\/p>\n<p>For application projects we generate a full assembly as part of the <code>UpdateAndroidResources<\/code> target. This ensures that we are using the final values from the <code>R.txt<\/code> file. It is this final assembly which will be deployed with the final package.<\/p>\n<p>In addition to the assembly a source file will be generated. This will be <code>__Microsoft.Android.Resource.Designer.cs<\/code>, or <code>__Microsoft.Android.Resource.Designer.fs<\/code> if you use F# . This contains a class which will derive from the <code>Resource<\/code> class. It will exist in the projects&#8217; <code>$(RootNamespace)<\/code>. This is the glue which allows existing code to work. Because the namespace of the <code>Resource<\/code> class will not change. For application projects the <code>Resource<\/code> class in the project RootNamespace will be derived from the <code>ResourceConstants<\/code> class in the designer assembly. This is to maintain backward compatibility with the way the older <code>Resource.designer.cs<\/code> files worked for application projects.<\/p>\n<p>Tests show we can get about <strong>8%<\/strong> improvement in startup time. And about a <strong>2%-4%<\/strong> decrease in overall package size.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2023\/10\/startup-graph.png\" alt=\"Graph showing Android Startup time improvements\" \/><\/p>\n<h3>Will my NuGet packages still work?<\/h3>\n<p>Some of you might be worried that with this change your existing package references will stop working. Do not worry, the new system has introduced a Trimmer step which will upgrade assembly references which use the old system to use the new one. This will be\u00a0done automatically as part of the build. This trimmer step analyses the IL in all the assemblies looking for places where the old Resource.designer fields were used. It will then update those to use the new Designer assembly properties. It will also completely remove the old Resource.designer in that assembly. So even if you use old packages you should still see the benefit of this new system.<\/p>\n<p>The Linker step should cover almost all of the code where the Resource.designer.cs fields are accessed. However if you come across a problem please open an issue at https:\/\/github.com\/xamarin\/xamarin-android\/issues\/new\/choose. <\/p>\n<p>This will apply to any android assembly reference which is pre net8.0-android.<\/p>\n<p>Packages built with the new system cannot be used with previous versions of .NET Android. Please consider using multi targeting if you need to support .NET 7 or Classic Xamarin.Android.<\/p>\n<h3>NuGet Package Authors<\/h3>\n<p>Do you maintain a NuGet package which contains Android Resources? If so you will need to make some changes.\nFirstly there is no need to ship the new _Microsoft.Android.Resource.Designer.dll with your NuGet. It will be generated at build time by the application consuming the NuGet. <\/p>\n<p>The new system is incompatible with the Classic Pre .NET Xamarin.Android and .NET 6\/7 Android Packages. So if you want to continue to support Classic Xamarin.Android as well as .NET 8 you will need to multitarget your assemblies. If you no longer need to support Class Xamarin.Android you can upgrade your project to the\n.NET Sdk Style project and use the following<\/p>\n<pre><code class=\"language-xml\">&lt;TargetFrameworks&gt;net7.0-android;net8.0-android&lt;\/TargetFrameworks&gt;<\/code><\/pre>\n<p>Classic Xamarin.Android is going out of Support next year so this is probably the best option. <\/p>\n<p>If you need to support both systems, you can use <a href=\"https:\/\/www.nuget.org\/packages\/Xamarin.Legacy.Sdk\/\"><code>Xamarin.Legacy.Sdk<\/code><\/a> to both for both Xamarin.Android and net8.0-android. <a href=\"https:\/\/www.nuget.org\/packages\/Xamarin.Legacy.Sdk\/\"><code>Xamarin.Legacy.Sdk<\/code><\/a> is un-supported, so it will only be useful as a stop gap measure while your users upgrade to .NET 8. See the <a href=\"https:\/\/www.nuget.org\/packages\/Xamarin.Legacy.Sdk\/\"><code>Xamarin.Legacy.Sdk<\/code><\/a> GitHub site <a href=\"https:\/\/github.com\/xamarin\/Xamarin.Legacy.Sdk\">https:\/\/github.com\/xamarin\/Xamarin.Legacy.Sdk<\/a> for details on how to use this package.<\/p>\n<p>Since .NET 6 android the <code>AndroidResource<\/code>, <code>AndroidAsset<\/code>, <code>AndroidEnvironment<\/code>, <code>AndroidJavaLibrary<\/code>, <code>EmbeddedNativeLibrary<\/code> and <code>AndroidNativeLibrary<\/code> items are no longer packaged\ninside the assembly. There is an .aar file generated at build time which contains this\ndata and will be named the same as the assembly. For things to work correctly this .aar\nfile will need to be shipped in the NuGet along side the assembly. If the .aar is not included it will\nresult in missing resource errors at runtime, such as<\/p>\n<pre><code class=\"language-csharp\">System.MissingMethodException: 'Method not found: int .Style.get_MyTheme()'<\/code><\/pre>\n<p>The <code>.aar<\/code> will be included by default if you use <code>dotnet pack<\/code> on your project and specify your NuGet properties and settings in the csproj. However if you are using a <code>.nuspec<\/code> you will need to manually add the <code>.aar<\/code> file to your list of files to be included. <\/p>\n<p>The changes related to <code>.aar<\/code> files and embedded files are documented in <a href=\"https:\/\/github.com\/xamarin\/xamarin-android\/blob\/main\/Documentation\/guides\/OneDotNetEmbeddedResources.md\">OneDotNetEmbeddedResources.md<\/a><\/p>\n<h2>Summary<\/h2>\n<p>So the new system should result in slightly smaller package sizes, and fast start up times.\nThe impact will be greater the more resources you are using in your app.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In .NET 8 we have made some vast improvements in changes to how Android resources work in regards to the designer assembly. If you are building Android apps and libraries there are some important changes to know about.<\/p>\n","protected":false},"author":4328,"featured_media":48598,"comment_status":"open","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[685,7233,756,636],"tags":[7701,7238,7246],"class_list":["post-48597","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dotnet","category-maui","category-csharp","category-fsharp","tag-dotnet-8","tag-net-maui","tag-android"],"acf":[],"blog_post_summary":"<p>In .NET 8 we have made some vast improvements in changes to how Android resources work in regards to the designer assembly. If you are building Android apps and libraries there are some important changes to know about.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/48597","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/users\/4328"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=48597"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/48597\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/48598"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=48597"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=48597"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=48597"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}