Jon Douglas The rantings of a developer

Xamarin.Android - UI Performance

Android UI Performance

NOTE: This blog was featured on blog.xamarin.com in which you can view a condensed version here: https://blog.xamarin.com/tips-for-creating-a-smooth-and-fluid-android-ui/

As developers, we want our users to have buttery smooth experiences when using our application. When it comes to UI performance, a buttery smooth experience can be defined as a consistent 60 frames per second(fps). That means that we have to render a frame every 16 ms to achieve this experience.

Note:

You can exceed the 16 ms time to render a frame occasionally, as there are often one or two buffered frames ready to go.

Why 60 fps?

Watch this wonderful video by Google on this topic for a brief introduction:

Why 60 FPS

This gives our whole frame process a little less than 16 ms to fully draw a frame on the screen. If a frame is dropped or delayed, it is known as Jank.

Identifying the problem

Have you ever had an experience like this with your application? Your scrolls are very choppy and your UI doesn’t seem to be that responsive.

Credit: Doug Sillars https://github.com/dougsillars for a great example.

During the time that the application is unresponsive and choppy, if the user attempts to interact with the application and it takes longer than 5000 ms (5 seconds) for the application to respond, the Android OS will give us a lovely message:

This isn’t the best experience we can offer our users.

Digging Deeper

Have you ever seen the following in your adb logcat?

I/Choreographer(1200): Skipped 60 frames!  The application may be doing too much work on its main thread.

This is the Android Choreographer warning you in advance that your application is not performing to the buttery smooth experience we are aiming for. In fact it’s telling you that you are skipping frames and not providing your users a 60 FPS experience. We will need a few tips & tricks to resolve these issues.

Overall Tips & Tricks for UI Performance

  • Measure overall UI performance with Systrace to get a baseline.
  • Use Hierarchy Viewer to identify and flatten view hierarchies.
  • Reduce overdraw by flattening layouts and drawing less pixels on screen.
  • Enable StrictMode to identify and make fewer potentially blocking calls on the main UI thread.

Getting Started

Counting Janky Frames

First you’ll want to get an overview of the total janky frames. After your application has been running and interacted with to reproduce the jank, run the following command:

adb shell dumpsys gfxinfo <PACKAGE_NAME>

Sample Output:

Stats since: 524615985046231ns
Total frames rendered: 8325
Janky frames: 729 (8.76%)
90th percentile: 13ms
95th percentile: 20ms
99th percentile: 73ms
Number Missed Vsync: 294
Number High input latency: 47
Number Slow UI thread: 502
Number Slow bitmap uploads: 44
Number Slow issue draw commands: 135

Numbers never lie, so it’s apparent we have a bit of Janky frames(~9%). Let’s dig in further with Systrace.

Systrace

Systrace gives you an overview of the whole android system and tells you what’s going on at specific intervals of time.

Getting Started

Let’s start a systrace on our device. First open up Android Device Monitor to get started.

Once inside, we see the option to start a Systrace:

We then are prompted with what we would like to trace:

This will generate a trace.html file that will give us information about our system for the trace duration.

EX: Systrace over 30 seconds:

Okay great! But what does this all mean? Let’s take it a step at a time.

Alerts & Frames

Alerts will give you a description of what the current situation is with a respective frame(s). It might let you know that there was a long View.OnDraw() call and it might give you suggestions on how you can fix the relevant frame(s).

You can then dig straight into the frame.

And see how much time spent during each step

Finally you can mark that frame using the m hotkey and see what work is being done on various threads such as various CPU Threads, the UI Thread, and the RenderThread.

This example is showing a Yellow Frame, but we can get a general idea of what an idea performant Frame might look like.

From our Alert, we can see that we might want to avoid significant work in View.OnDraw() or Drawable.Draw(), especially allocations or drawing to Bitmaps. In other words, Google gives us a tip to watch this video:

Avoiding Allocations in onDraw()

For our Frame, we can see that our Adapter.GetView() should recycle the incoming View instead of creating a new one.

Systrace Terms

Frame Color

Green - Great performance

Yellow - Less than ideal performance

Red - Bad performance

Scheduling Delay

Scheduling delays happen when the thread that is processing a specific slice was not scheduled on the CPU for a long amount of time. Thus it takes longer for this thread to fire up and complete.

Wall Duration

The amount of time that passed from the moment a slice is started until it’s finished.

CPU Duration

The amount of time the CPU spent processing that slice.

Overdraw

Debugging overdraw is fairly easy. You can enable this in your Android device’s settings:

Settings -> Developer Options -> Debug GPU overdraw -> Show overdraw areas.

Once enabled, you will see many different colors on your layouts.

  • White - No overdraw
  • Blue - Pixels that are 1x overdrawn
  • Green - Pixels that are 2x overdrawn
  • Pink - Pixels that are 3x overdrawn
  • Red - Pixels that are 4x overdrawn

Bad Layout Performance:

Good Layout Performance:

You can then identify why this layout might be overdrawing so much via a tool like Hierarchy Viewer.

Hierarchy Viewer

The first thing we want to know regarding our View Hierarchy is how deep or nested our layouts are.

Let’s take the previous example of bad layout performance:

We can see that we are 4 layers deep which is less than ideal for our ListView. We really need to flatten this out.

Okay that’s a little better! We are only 3 layers deep now. However it’s still not great. Let’s try to remove one more layer.

Much better! We just optimized our whole view hierarchy and we will reap the performance benefits. Let’s take a look at the overall Layout and Draw timings for proof.

  • Bad Layout - 31 views / Layout: 0.754 ms / Draw: 7.273 ms
  • Better Layout - 26 views / Layout: 0.474 ms / Draw: 6.191 ms
  • Good Layout - 17 views / Layout: 0.474 ms / Draw: 1.888 ms

StrictMode

StrictMode is a very useful tool for battle testing your application. Enabling StrictMode as a means to ensure you are not putting extra work in certain places of your application like disk reads, disk writes, and network calls is ideal for a great user experience. In a nutshell StrictMode does the following:

  1. Logs a message to LogCat under the StrictMode tag
  2. Display a dialog (If PenaltyLog() is enabled)
  3. Crash your application (If PenaltyDeath() is enabled)

This can help you determine what type of policies you’d like to battle test your application with.

Enabling StrictMode in your Android Application

protected override void OnCreate(Bundle bundle)
{
    StrictMode.SetThreadPolicy(new StrictMode.ThreadPolicy.Builder().DetectAll().PenaltyLog().Build());

    StrictMode.SetVmPolicy(new StrictMode.VmPolicy.Builder().DetectLeakedSqlLiteObjects().DetectLeakedClosableObjects().PenaltyLog().PenaltyDeath().Build());

    base.OnCreate(bundle);
}

Summary

There are a plethora of tools available to use on your Xamarin.Android application. Use them to track the important performance-related items about your application such as rendering performance to achieve a buttery smooth 60 fps experience for your customers. Use tools like Systrace, GPU overdraw, Hierarchy Viewer, and StrictMode to pinpoint performance related issues in your application and fix them.

Xamarin.Android - Entity Framework

Preface

Entity Framework has been one of my favorite projects for quite some time. If you didn’t know, Entity Framework split off from the 6.X version to a new re-written 7.X (Core) version, in which the goal is to keep the ORM lightweight and very extensible.

Now the challenge for me has always been:

I want to use Entity Framework in my Xamarin projects.

This hasn’t really been possible for the last 3 years. In fact, I’ve attempted it a few years back and gave up because the tooling was simply just not there. However this is my personal redemption at getting this to work.

Tools

Let’s talk about the tooling needed to make this possible. Consider this more of a high level approach:

  • SQLite Provider- We will need a place to store our data. Seeing that SQLite is a perfect fit for mobile, we will use Entity Framework’s SQLite package:

https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.Sqlite/

  • Entity Framework Core - We will need an ORM to manage our CRUD operations and database migrations:

https://www.nuget.org/packages/Microsoft.EntityFrameworkCore/

Great! Now we’re off to the races.

Getting Started

Creating the NetStandard library

The first thing we’ll do is create a netstandard library. To do this, create a PCL project:

Since this is not a netstandard library quite yet, let’s go ahead and convert that by going to the project’s Properties

Once we have that set, we want to ensure we have the minimum requirements for the two NuGet packages we linked above:

https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.Sqlite/

https://www.nuget.org/packages/Microsoft.EntityFrameworkCore/

By the looks of things, the dependency is netstandard 1.3. So let’s make sure our netstandard project is targeting that version:

Creating the Xamarin.Android project

Nothing too special here, we’re just going to create a File -> New Single-View App (Android) Project:

Adding the NuGet packages

This step is typically easiest to do on the project level rather than the solution level as NuGet is sometimes not that friendly with different project structures(.csproj vs project.json)

Adding the NuGet packages to the netstandard library

Adding the NuGet packages to the Xamarin.Android project

Defining the DbContext

If you’ve used Entity Framework before, you will be very familiar with how we define a DbContext and our underlying Models that define our database schema.

Let’s start with a simple data model that we will call Cat.cs:

    public class Cat
    {
        [Key]
        public int CatId { get; set; }
        public string Name { get; set; }
        public int MeowsPerSecond { get; set; }
    }

Let’s now make sure that this is apart of our DbContext by defining a new context we’ll call CatContext.cs. You may notice that we have a string DatabasePath. We will use this later when we need to tell Entity Framework where to store our Database on disk:

    public class CatContext : DbContext
    {
        public DbSet<Cat> Cats { get; set; }

        private string DatabasePath { get; set; }

        public CatContext()
        {

        }

        public CatContext(string databasePath)
        {
            DatabasePath = databasePath;
        }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlite($"Filename={DatabasePath}");
        }
    }

Implementing the Context

First, we need to make sure our Xamarin.Android project is referencing our netstandard library.

Now that we have that, let’s implement our MainActivity.cs with some Entity Framework code!

[Activity(Label = "EntityFrameworkWithXamarin.Droid", MainLauncher = true, Icon = "@drawable/icon")]
    public class MainActivity : Activity
    {
        int count = 1;

        protected async override void OnCreate(Bundle bundle)
        {
            base.OnCreate(bundle);

            // Set our view from the "main" layout resource
            SetContentView(Resource.Layout.Main);

            // Get our button from the layout resource,
            // and attach an event to it
            Button button = FindViewById<Button>(Resource.Id.MyButton);

            button.Click += delegate { button.Text = string.Format("{0} clicks!", count++); };

            TextView textView = FindViewById<TextView>(Resource.Id.TextView1);

            var dbFolder = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
            var fileName = "Cats.db";
            var dbFullPath = Path.Combine(dbFolder, fileName);
            try
            {
                using (var db = new CatContext(dbFullPath))
                {
                    await db.Database.MigrateAsync(); //We need to ensure the latest Migration was added. This is different than EnsureDatabaseCreated.

                    Cat catGary = new Cat() { CatId = 1, Name = "Gary", MeowsPerSecond = 5 };
                    Cat catJack = new Cat() { CatId = 2, Name = "Jack", MeowsPerSecond = 11 };
                    Cat catLuna = new Cat() { CatId = 3, Name = "Luna", MeowsPerSecond = 3 };

                    List<Cat> catsInTheHat = new List<Cat>() { catGary, catJack, catLuna };

                    if(await db.Cats.CountAsync() < 3)
                    {
                        await db.Cats.AddRangeAsync(catsInTheHat);
                        await db.SaveChangesAsync();
                    }

                    var catsInTheBag = await db.Cats.ToListAsync();

                    foreach(var cat in catsInTheBag)
                    {
                        textView.Text += $"{cat.CatId} - {cat.Name} - {cat.MeowsPerSecond}" + System.Environment.NewLine;
                    }
                }

            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(ex.ToString());
            }
        }
    }

Let’s try to run this code.

Microsoft.Data.Sqlite.SqliteException (0x80004005): SQLite Error 1: 'no such table: Cats'.

It looks like we’re missing a core Entity Framework feature, and that’s Migrations to create our database schema.

Well shucks…this is awkward. We don’t have a great way to generate Entity Framework Migrations from within a Xamarin.Android project or the netstandard library. Let’s work with a quick workaround by creating a new netcore Console Application so we can generate Migrations.

We need to add the Entity Framework Tools, Entity Framework Core Design, and Entity Framework Core to this project so we can use the command line to generate our Migrations.

https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.Tools.DotNet

https://www.nuget.org/packages/Microsoft.EntityFrameworkCore/

https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.Design/

We now need to move over our Cat.cs and CatContext.cs to ensure there’s a DbContext it can generate Migrations for. Your Console App should now look like this:

Now we can generate a schema for our context. Let’s use the new dotnet tooling to do this. Open up a new console in our current Console App directory:

Now we need to generate an initial Migration.

dotnet ef migrations add Initial

This should generate a migration:

Now we need to take the initial migrations generated in the Migrations folder of our project and simply move them over to our netstandard library.

Note: You can simply change the namespaces of these two generated files to the name of your netstandard namespace.

Let’s try running the Xamarin.Android project again and see if we run into any other exceptions

It looks like it worked! Our simple attempt at adding Cat models and retrieving them works!

If we wanted to take a closer look at the SQLite file that gets generated, use an emulator and open up ADM(Android Device Monitor):

Taking a closer look into the data/data/files folder, we will see our Cats.db that we created.

You can now take that file and open it in any SQLite explorer.

Note: I personally use DB Browser for SQLite (http://sqlitebrowser.org/)

Conclusion

Source Code: https://github.com/JonDouglas/EntityFrameworkWithXamarin

It’s been a three year battle with you Entity Framework. However I have to give my thanks to everyone involved in getting Entity Framework to the state it currently is. It’s been so much fun seeing how these projects turn out after a long period of development.

Xamarin.Android - Where Do These Permissions Come From?

Preface

I get this question quite often:

Why does my application have extra permissions added to my AndroidManifest.xml?

Many developers think it’s a problem with Xamarin.Android, but they aren’t aware of what really happens with third party libraries and their applications.

To clear up that muck, let’s first talk about the general process.

Typically when you have an external library, those libraries have their own manifests. Those manifests can have various <uses-permission> elements inside, or they can even specify them inside the AssemblyInfo.cs like the following:

[assembly: UsesPermission(Android.Manifest.Permission.AccessCoarseLocation)]

Either way, these permissions will get inserted into an AndroidManifest.xml at the end of the day for that library and then eventually into your application’s AndroidManifest.xml.

Manifest Merging

What do we know about Android development thus far? Well we know that each .apk file can only have one AndroidManifest.xml defined. Thus that means at the end of the day, everything needs to merge into a master AndroidManifest.xml.

Every application must have an AndroidManifest.xml file (with precisely that name) in its root directory. The manifest file provides essential information about your app to the Android system, which the system must have before it can run any of the app’s code.

https://developer.android.com/guide/topics/manifest/manifest-intro.html

Great! Now we are one step ahead of the game and can understand a little bit about the Android build process. However that brings us to a little rut…Does Xamarin.Android follow the same build process as say native Android?

The answer is no. They both have their own unique build processes and thus not everything we learn about native Android can be converted directly to Xamarin.Android. In this specific case, if we learned about how native Android merges multiple manifest files we would find out that this is done via Gradle and not MSBuild which Xamarin.Android uses under the hood.

Example

Let’s take a File -> New Blank Android Project. Let’s rebuild it from the start and see a final AndroidManifest.xml. We would find this file in the obj\Debug\android\AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="IDontWantYourPermission.IDontWantYourPermission" android:versionCode="1" android:versionName="1.0">
  <!--suppress UsesMinSdkAttributes-->
  <uses-sdk android:minSdkVersion="16" />
  <uses-permission android:name="android.permission.INTERNET" />
  <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
  <application android:label="IDontWantYourPermission" android:name="android.app.Application" android:allowBackup="true" android:icon="@drawable/icon" android:debuggable="true">
<activity android:icon="@drawable/icon" android:label="IDontWantYourPermission" android:name="md5bdb5a298d3d71fd07b60a60955b14ddd.MainActivity">
  <intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
  </intent-filter>
</activity>
<provider android:name="mono.MonoRuntimeProvider" android:exported="false" android:initOrder="2147483647" android:authorities="IDontWantYourPermission.IDontWantYourPermission.mono.MonoRuntimeProvider.__mono_init__" />
<!--suppress ExportedReceiver-->
<receiver android:name="mono.android.Seppuku">
  <intent-filter>
<action android:name="mono.android.intent.action.SEPPUKU" />
<category android:name="mono.android.intent.category.SEPPUKU.IDontWantYourPermission.IDontWantYourPermission" />
  </intent-filter>
</receiver>
  </application>
</manifest>

We only see two permissions being added by default in a Debug configuration:

  <uses-permission android:name="android.permission.INTERNET" />
  <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

However, now let’s add a NuGet package that we believe might be adding permissions we don’t want in our project. For this example, I’m going to use James Montemagno's http://www.nuget.org/packages/Xam.Plugin.Geolocator

After adding it, rebuilding the solution, and checking the new obj\Debug\android\AndroidManifest.xml:

  <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
  <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

So surely enough we would never have known these items were added to our project unless we checked the final generated AndroidManifest.xml.

Okay…But that doesn’t help me understand where a permission is coming from if my project is huge!

Fair enough. Let’s figure out what we can provide to make this easier for us.

Here’s a few ways we can figure out this information:

1) Search through all third-party dependencies via a decompiler like dotPeek and view the Assembly’s information to see if a permission is being added.

EX: (Using dotPeek)

2) Use a grep tool to search for a uses-permission and UsesPermission string.

EX: (Using grepWin - https://sourceforge.net/projects/grepwin/files/)

Proguard

Why ProGuard?

ProGuard detects and removes unused classes, fields, methods, and attributes from your packaged application. It can even do the same for referenced libraries which can help you avoid the 64k reference limit.

The ProGuard tool from the Android SDK will also optimize the bytecode, remove unused code instructions, and obfuscates the remaining classes, fields, and methods with short names.

There are four optional steps that ProGuard uses:

  • In the shrinking step, ProGuard starts from these seeds and recursively determines which classes and class members are used. All other classes and class members are discarded.

  • In the optimization step, ProGuard further optimizes the code. Among other optimizations, classes and methods that are not entry points can be made private, static, or final, unused parameters can be removed, and some methods may be inlined.

  • In the obfuscation step, ProGuard renames classes and class members that are not entry points. In this entire process, keeping the entry points ensures that they can still be accessed by their original names.

  • The preverification step is the only step that doesn’t have to know the entry points.

ProGuard reads input jars in which it will shrink, optimize, obfuscate, and preverify them. ProGuard will then write these results to one or more output jars.

Reference Documentation

Please note only the following steps are run in Xamarin.Android:

The Xamarin.Android ProGuard configuration does not obfuscate the .apk and it is not possible to enable obfuscation through ProGuard even through the use of custom configuration files. Thus Xamarin.Android’s ProGuard will only run the shrinking step.

How does ProGuard work with Xamarin.Android

One important item to know ahead of time before using ProGuard is how it works within the Xamarin.Android build process. This can be thought of in two separate steps:

  1. Xamarin Android Linker
  2. Proguard

Xamarin.Android Linker

First, the linker employs static analysis of your application to determine which assemblies are actually used, which types are actually used, and which members are actually used. It will always run before the ProGuard step. Because of this, the linker can strip an assembly/type/member that you might expect ProGuard to run on.

You can find more information on this topic here: https://developer.xamarin.com/guides/android/advanced_topics/linking/

Proguard

Secondly, ProGuard will then run and remove unused Java bytecode to optimize the .apk. (shrinking step)

Enabling ProGuard

ProGuard can be enabled by checking the Enable Proguard option inside of your Packaging Properties. You must also ensure your project is set to the Release configuration as the Linker must run in order for ProGuard to run.

Optional: You can add a custom ProGuard Configuration file for more control with the ProGuard tooling. To do this, you can create a new .cfg file and apply the build action of ProguardConfiguration.

Customize which code to keep

For majority of situations, the default ProGuard configuration file that Xamarin.Android provides will be sufficient to remove all-and only-the unused code.

You can find the default ProGuard Configuration File at obj\Release\proguard\proguard_xamarin.cfg if you wanted to see what by default is added to the configuration.

EX:

# This is Xamarin-specific (and enhanced) configuration.

-dontobfuscate

-keep class mono.MonoRuntimeProvider { *; <init>(...); }
-keep class mono.MonoPackageManager { *; <init>(...); }
-keep class mono.MonoPackageManager_Resources { *; <init>(...); }
-keep class mono.android.** { *; <init>(...); }
-keep class mono.java.** { *; <init>(...); }
-keep class mono.javax.** { *; <init>(...); }
-keep class opentk.platform.android.AndroidGameView { *; <init>(...); }
-keep class opentk.GameViewBase { *; <init>(...); }
-keep class opentk_1_0.platform.android.AndroidGameView { *; <init>(...); }
-keep class opentk_1_0.GameViewBase { *; <init>(...); }

-keep class android.runtime.** { <init>(***); }
-keep class assembly_mono_android.android.runtime.** { <init>(***); }
# hash for android.runtime and assembly_mono_android.android.runtime.
-keep class md52ce486a14f4bcd95899665e9d932190b.** { *; <init>(...); }
-keepclassmembers class md52ce486a14f4bcd95899665e9d932190b.** { *; <init>(...); }

# Android's template misses fluent setters...
-keepclassmembers class * extends android.view.View {
   *** set*(***);
}

# also misses those inflated custom layout stuff from xml...
-keepclassmembers class * extends android.view.View {
   <init>(android.content.Context,android.util.AttributeSet);
   <init>(android.content.Context,android.util.AttributeSet,int);
}

However there might be cases where ProGuard might not be able to properly analyze and can potentially remove code your application actually needs.

If this happens, you can add a -keep line to your custom ProGuard configuration file.

EX:

-keep public class MyClass

What command is ProGuard running?

ProGuard is simply a .jar provided with the Android SDK. Thus it invokes a command like the following:

java -jar proguard.jar options ...

The ProGuard Task

The Proguard task is found inside the Xamarin.Android.Build.Tasks.dll assembly. It is apart of the _CompileToDalvikWithDx Target which is apart of the _CompileDex Target.

EX: Default Parameters in a File->New Project

ProguardJarPath = C:\Android\android-sdk\tools\proguard\lib\proguard.jar
AndroidSdkDirectory = C:\Android\android-sdk\
JavaToolPath = C:\Program Files (x86)\Java\jdk1.8.0_92\\bin
ProguardToolPath = C:\Android\android-sdk\tools\proguard\
JavaPlatformJarPath = C:\Android\android-sdk\platforms\android-25\android.jar
ClassesOutputDirectory = obj\Release\android\bin\classes
AcwMapFile = obj\Release\acw-map.txt
ProguardCommonXamarinConfiguration = obj\Release\proguard\proguard_xamarin.cfg
ProguardGeneratedReferenceConfiguration = obj\Release\proguard\proguard_project_references.cfg
ProguardGeneratedApplicationConfiguration = obj\Release\proguard\proguard_project_primary.cfg
ProguardConfigurationFiles

		{sdk.dir}tools\proguard\proguard-android.txt;
		{intermediate.common.xamarin};
		{intermediate.references};
		{intermediate.application};
		;
	
JavaLibrariesToEmbed = C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\MonoAndroid\v7.0\mono.android.jar
ProguardJarInput = obj\Release\proguard\__proguard_input__.jar
ProguardJarOutput = obj\Release\proguard\__proguard_output__.jar
DumpOutput = obj\Release\proguard\dump.txt
PrintSeedsOutput = obj\Release\proguard\seeds.txt
PrintUsageOutput = obj\Release\proguard\usage.txt
PrintMappingOutput = obj\Release\proguard\mapping.txt

EX: Command generated from File -> New Project

C:\Program Files (x86)\Java\jdk1.8.0_92\\bin\java.exe -jar C:\Android\android-sdk\tools\proguard\lib\proguard.jar -include obj\Release\proguard\proguard_xamarin.cfg -include obj\Release\proguard\proguard_project_references.cfg -include obj\Release\proguard\proguard_project_primary.cfg "-injars 'obj\Release\proguard\__proguard_input__.jar';'C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\MonoAndroid\v7.0\mono.android.jar'" "-libraryjars 'C:\Android\android-sdk\platforms\android-25\android.jar'" -outjars "obj\Release\proguard\__proguard_output__.jar" -optimizations !code/allocation/variable 

Optional Parameter:

Custom proguard.cfg file with Build Action of ProguardConfiguration. This will add the proguard.cfg file to the ProguardConfigurationFiles parameter.

ProGuard Options

Examples of ProGuard Configurations

A simple Android activity

-injars  bin/classes
-outjars bin/classes-processed.jar
-libraryjars /usr/local/java/android-sdk/platforms/android-9/android.jar

-dontpreverify
-repackageclasses ''
-allowaccessmodification
-optimizations !code/simplification/arithmetic

-keep public class mypackage.MyActivity

A complete Android application

-injars  bin/classes
-injars  libs
-outjars bin/classes-processed.jar
-libraryjars /usr/local/java/android-sdk/platforms/android-9/android.jar

-dontpreverify
-repackageclasses ''
-allowaccessmodification
-optimizations !code/simplification/arithmetic
-keepattributes *Annotation*

-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider

-keep public class * extends android.view.View {
public <init>(android.content.Context);
public <init>(android.content.Context, android.util.AttributeSet);
public <init>(android.content.Context, android.util.AttributeSet, int);
public void set*(...);
}

-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet);
}

-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet, int);
}

-keepclassmembers class * implements android.os.Parcelable {
static android.os.Parcelable$Creator CREATOR;
}

-keepclassmembers class **.R$* {
public static <fields>;
}

Please note that in these cases above, the Xamarin.Android build process will supply the input, output, and library jars. Thus you can focus on other options like -keep.

Troubleshooting

File Issues

Unknown option '-keep' in line 1 of file 'proguard.cfg'

This issue happens mainly on Windows because the .cfg file has the wrong encoding. You need to ensure it has encoding set to UTF-8 in your favorite text editor.

Proguard Processing Issues

Majority of the issues that can happen when ProGuard is processing can be found here.

ProGuard Versions

You can find all of the ProGuard versions at the SourceForge page.

Note: If you are trying to use ProGuard against Android 7.0, you will need to download a newer version of ProGuard as the Android SDK does not ship a new version that is compatible with JDK 1.8

http://stackoverflow.com/questions/39514518/xamarin-android-proguard-unsupported-class-version-number-52-0/39514706#39514706

You will be able to provide a custom ProGuard path in the future via the following Pull Request:

https://github.com/xamarin/xamarin-android/pull/267

Otherwise you can use this NuGet package to support a new version of proguard.jar.

P/Invoking Native Calls

There was a great question the other day. I figured I’d blog about this for others trying to figure out the answer.

How does one invoke a SIGABRT on a device?

For those unfamiliar with what a SIGABRT is, here’s the definition from wikipedia:

SIGABRT is sent by the process to itself when it calls the abort libc function, defined in stdlib.h. The SIGABRT signal can be caught, but it cannot be blocked; if the signal handler returns then all open streams are closed and flushed and the program terminates (dumping core if appropriate). This means that the abort call never returns. Because of this characteristic, it is often used to signal fatal conditions in support libraries, situations where the current operation cannot be completed but the main program can perform cleanup before exiting. It is used when an assertion fails.

Here’s what the community has to say about it:

http://stackoverflow.com/questions/3413166/when-does-a-process-get-sigabrt-signal-6

http://stackoverflow.com/questions/11161126/what-causes-a-sigabrt-fault

Well now that question becomes a bit morphed into:

How does one invoke a SIGABRT signal in Xamarin on both iOS and Android?

Seeing that we already found out the answer via these stack overflow links, we might want to dig in a little further to the actual potential library call we want to use. In our case, we want to look at abort():

http://www.cplusplus.com/reference/cstdlib/abort/

Aborts the current process, producing an abnormal program termination.

The function raises the SIGABRT signal (as if raise(SIGABRT) was called). This, if uncaught, causes the program to terminate returning a platform-dependent unsuccessful termination error code to the host environment.

Perfect! This is exactly what we need to call into. The next question is…How do we do that?

Based on the Xamarin iOS documentation, we are introduced into the world of P/Invoke(We can assume the same for Xamarin Android as well):

https://developer.xamarin.com/guides/ios/advanced_topics/native_interop/#Accessing_C_Methods_from_C

There’s four steps to doing just this:

  • Determine which C function you want to invoke
  • Determine its signature
  • Determine which library it lives in
  • Write the appropriate P/Invoke declaration

Let’s take a look at this first step now.

Determine which C function you want to invoke

We already determined that we need to invoke abort().

Determine its signature

The signature for this call is fairly straight forward. It’s void and it has 0 arguments. We can define the structure as the following:

static extern void abort ();

Determine which library it lives in

This is the tough one. Especially for those of us who have not dwelled into the native library side of things for awhile. Let’s use Github as a tool here to see if there’s previous examples. My first search is using DllImport to see examples of this in the Xamarin.iOS / Mac repository.

https://github.com/xamarin/xamarin-macios/search?p=2&q=DllImport&type=Code&utf8=%E2%9C%93

Unfortunately that’s not what I want. I want to invoke this method from the standard C library. Let’s refine again, this time with the following query: DllImport libc

https://github.com/xamarin/xamarin-macios/search?utf8=%E2%9C%93&q=DllImport+libc&type=Code

Oh perfect! I’m starting to see some libc paths. There’s two that seem to be useful here:

[DllImport ("/usr/lib/libc.dylib")]

and

[DllImport("libc")]

However what about Xamarin.Android? Let’s do the same thing using our refined query of DllImport libc:

https://github.com/xamarin/xamarin-android/search?utf8=%E2%9C%93&q=DllImport+libc

Perfect! It looks like both Xamarin.iOS and Xamarin.Android can use the path of:

[DllImport("libc")]

Note: You can make use of the following command to see the symbol table

nm -a <path to .dylib>

If you need to find dependencies of a .dylib you can use:

otool -L <path to .dylib>

Write the appropriate P/Invoke declaration

Now to figure out “How many licks does it take to get to the Tootsie Roll center of a Tootsie Pop?”

A one…A two-Hoo!…A three…

[DllImport ("libc")]
static extern void abort ();

Android:

Calling this function in the OnCreate method:

11-08 12:48:36.172 D/Mono( 6570): DllImport attempting to load: '/system/lib/libc.so'.
11-08 12:48:36.172 D/Mono( 6570): DllImport loaded library '/system/lib/libc.so'.
11-08 12:48:36.172 D/Mono( 6570): DllImport searching in: '/system/lib/libc.so' ('/system/lib/libc.so').
11-08 12:48:36.172 D/Mono( 6570): Searching for 'abort'.
11-08 12:48:36.172 D/Mono( 6570): Probing 'abort'.
11-08 12:48:36.172 D/Mono( 6570): Found as 'abort'.
11-08 12:48:36.180 I/monodroid-gref( 6570): +g+ grefc 9 gwrefc 0 obj-handle 0x100019/L -> new-handle 0x1004ae/G from thread '(null)'(1)
11-08 12:48:36.212 F/libc( 6570): Fatal signal 6 (SIGABRT), code -6 in tid 6570 (App23.App23)

iOS:

Calling this function in the FinishedLaunching method:

Exception Type:  EXC_CRASH (SIGABRT)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Exception Note:  EXC_CORPSE_NOTIFY
Triggered by Thread:  6

Big thanks to Brendan Zagaeski for helping make my initial theory turn into a reality and blog post!