48

Xamarin: Faster Xamarin.Android Builds & Smaller Dex Files

 5 years ago
source link: https://www.tuicool.com/articles/JZNvi2r
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Jonathan

June 24th, 2019

One of our current focus areas in Xamarin.Android is build performance. The “inner dev loop” directly impacts developer productivity–the time it takes to make a small code change and see the result on a device or emulator . Reevaluating parts of Xamarin.Android’s codebase has been how we’ve been able to make progress.

Dex Limits

We have also noticed that Xamarin.Android developers hit the multidex limit often. If an application is mostly .NET code, the amount of Java code should be under  the multidex limit. We had a suspicion there was more to the problem.

A Story About Aapt

Xamarin.Android runs several command-line tools to create a native Android application. One such tool is aapt (Android Asset Packaging Tool), which is used to process Android resource files and create Android APK files . aapt also generates the identifiers that Android developers are familiar with, such as the Resource class in C# or the R class in Java.

aapt (or  aapt2 ) runs a few times during a typical Xamarin.Android build:

    • R.java
      Resource.designer.cs
      

As you might imagine, aapt takes a bit of time to run, so it became an area of investigation.

Hello World

We began looking at how Xamarin.Android invokes aapt in the case of Java libraries. If we look at the example of a “Hello World” Xamarin.Forms app, the aapt command-line looks something like this:

aapt package 
    -f -m 
    --non-constant-id 
    --auto-add-overlay 
    --max-res-version 28 
    -M obj\Debug\lp\1\jl\manifest\AndroidManifest.xml 
    -J obj\Debug\android\src 
    -S obj\Debug\res 
    -S obj\Debug\lp\1\jl\res 
    -S obj\Debug\lp\2\jl\res 
    -S obj\Debug\lp\3\jl\res 
    -S obj\Debug\lp\4\jl\res 
    -S obj\Debug\lp\5\jl\res 
    -S obj\Debug\lp\6\jl\res 
    -S obj\Debug\lp\7\jl\res 
    -S obj\Debug\lp\8\jl\res 
    -I "C:\Program Files (x86)\Android\android-sdk\platforms\android-28\android.jar"

NOTE: we have numbers & small directory names for workaround theMAX_PATH limit on Windows.

Android Support Libraries

This command produces an R.java file for the first Java library used by the app, placing it in obj\Debug\android\src in a Java directory structure . Xamarin.Android runs eight such commands in parallel: one per library with a different -M switch. The Java libraries in this particular project are the Android support libraries .

We end up with eight files on disk, for example:

    • obj\Debug\android\src\android\support\library1\R.java
      obj\Debug\android\src\android\support\library2\R.java
      obj\Debug\android\src\android\support\library3\R.java
      obj\Debug\android\src\android\support\library4\R.java
      obj\Debug\android\src\android\support\library5\R.java
      obj\Debug\android\src\android\support\library6\R.java
      obj\Debug\android\src\android\support\library7\R.java
      obj\Debug\android\src\android\support\library8\R.java
      

The theory (or question) was if we could avoid running aapt so many times to improve build times? But something else didn’t seem quite right… When comparing the R.java files from a Xamarin project to a similar Java project in Android Studio, the R.java files had quite a few more fields!

Specifically, we compared the android\support\compat\R$drawable class:

      • Android Studio/Java: 11 fields
      • Xamarin.Forms: 300+ fields

Comparing the APK as a whole, the Xamarin app had thousands more fields than the Java app!

Thinking about the aapt command, the command produced an R.java file as if each library referenced every other library . We generated multiple  R.java files containing every field in the application.

The Java libraries, of course, do not need all of the fields, some  of the Java libraries depend on one another. How can we figure out which ones?

Looking at Android Studio

Many parts of Android are open source, so we looked at Google’s implementation. Android Studio uses a file named R.txt , an easy-to-parse, text-based, version of R.java to declare identifiers needed by each Java library. Xamarin.Android already had access to this file in most cases, but our MSBuild targets were not using R.txt yet.

At a high level, Android Studio does the following:

      • aapt  is invoked to generate an R.txt  file for the entire Android app.
      • An R.txt  file that is shipped alongside each library makes it possible to map the values back to the app’s R.txt  file. The library’s R.txt  only lists Android resources that it uses.
      • Smaller R.java  files are generated using the integer values from the app’s R.txt .

The process was not too  complex, so we could implement this in C# for Xamarin.Android. We already had C# code that could parse R.txt files, so the only thing  new  would be something that could generate R.java files.

The Results

We saw around 12,000 less fields just from this change:

22M7vaA.png!web

The APK file was also around 120KB smaller.

We also saw an impact to build times. Xamarin.Android builds no longer ran aapt N times, and we were able to implement the  R.txt parser and  R.java writer in an efficient manner in C#. Using MSBuild’s /clp:performancesummary option, we could see the duration for these MSBuild targets:

Before:
3173 ms  _GenerateJavaDesignerForComponent          1 calls
 
After:
  20 ms  GenerateLibraryResources                   1 calls

Saving three seconds off of “Hello World” is great! We think these improvements could have an even bigger impact on larger Xamarin.Android projects.

Conclusion

If your Xamarin.Android app currently requires multidex, due to an error such as:

trouble writing output: 
    Too many field references to fit in one dex file: 70468; max is 65536.

In future versions of Xamarin.Android, an app hitting the “too many field references” error might not require multidex anymore.

This enhancement are available in the latest Visual Studio 2019 version 16.2 Preview and Visual Studio for Mac 2019 version 8.2 Preview .

Further reading:


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK