Jon Douglas The rantings of a developer

Being Reactive with Xamarin.Android

Preface

This is a continuation post of https://www.jon-douglas.com/2017/08/01/being-more-reactive/ in which we will explore how we can apply Reactive Extensions(RX) to our Xamarin.Android project.

We will visually show how Reactive Extensions(RX) and a touch of Reactive UI(RxUI) to accomplish our previous example in Xamarin.Android

Introducing ReactiveUI

ReactiveUI is an advanced, composable, functional reactive model-view-viewmodel framework for all .NET platforms. The cool thing about ReactiveUI is that it can be used in small sections of your project, or it can be the main driving force behind your project. It’s really up to you to decide.

ReactiveUI has documentation on each platform for best practices. For Xamarin.Android, we will follow a couple of best practices for our sample:

https://reactiveui.net/docs/guidelines/platform/xamarin-android

Creating the Model

Let’s define the models and helper classes for our BBQ:

 public class Hamburger
    {
        public Meat Meat { get; set; }
        public Lettuce Lettuce { get; set; }
        public Bun Bun { get; set; }
    }

    public class Meat
    {
        public bool Cooked { get; set; }
        public bool Rotten { get; set; }
    }

    public class Lettuce
    {
        public bool Fresh { get; set; } = true;
    }

    public class Bun
    {
        public bool Heated { get; set; }
    }

    public static class BBQHelpers
    {
        public static Meat Cook(Meat meat)
        {
            meat.Cooked = true;
            return meat;
        }

        public static Bun Heat(Bun bun)
        {
            bun.Heated = true;
            return bun;
        }
    }

Creating the ViewModel

Let’s create a BurgerViewModel that inherits ReactiveObject. We can then define our streams similar to how we did in the previous blog post:

public class BurgerViewModel : ReactiveObject
    {
        public IObservable<Bun> BunStream()
        {
            return Observable.Interval(TimeSpan.FromSeconds(5))
                .Select(_ => BBQHelpers.Heat(new Bun()))
                .Take(4)
                .Publish()
                .RefCount();
        }

        public IObservable<Meat> RawMeatStream()
        {
            return Observable.Interval(TimeSpan.FromSeconds(3))
                .Select(_ => new Meat())
                .Take(4)
                .Publish()
                .RefCount();
        }

        public IObservable<Meat> CookedMeatStream()
        {
            return RawMeatStream().Select(meat => BBQHelpers.Cook(meat))
                .Take(4)
                .Publish()
                .RefCount();
        }

        public IObservable<Lettuce> LettuceStream()
        {
            return Observable.Interval(TimeSpan.FromSeconds(2))
                .Select(_ => new Lettuce())
                .Take(4)
                .Publish()
                .RefCount();
        }

        public IObservable<Hamburger> HamburgerStream()
        {
            return Observable.Zip(CookedMeatStream(), BunStream(), LettuceStream(), (meat, bun, lettuce) => new Hamburger { Meat = meat, Bun = bun, Lettuce = lettuce })
                .Take(4)
                .Publish()
                .RefCount();
        }
    }

This ViewModel will serve as our various streams for our BBQ. This sample is only going to take 4 items each as our layout can only hold this amount visually without scrolling.

Defining the Layout

We are going to define a very simple LinearLayout which includes 5 child LinearLayouts to show our BBQ streams:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
              xmlns:tools="https://schemas.android.com/tools"
              android:id="@+id/activity_bbq"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical">

  <LinearLayout
    android:id="@+id/raw_meat_layout"
    style="@style/StreamLayout" />

  <LinearLayout
    android:id="@+id/cooked_meat_layout"
    style="@style/StreamLayout" />

  <LinearLayout
    android:id="@+id/bun_layout"
    style="@style/StreamLayout" />

  <LinearLayout
    android:id="@+id/lettuce_layout"
    style="@style/StreamLayout" />

  <LinearLayout
    android:id="@+id/burger_layout"
    style="@style/StreamLayout" />
</LinearLayout>

Creating the Activity

The activity will have a couple of special setup items. The first is that we need to inherit from a ReactiveActivity<BurgerViewModel>. This makes our definition the following:

public class BurgersActivity : ReactiveActivity<BurgerViewModel>

Now we need to wire up our controls. We can do this by the following definitions:

private LinearLayout bunLinearLayout;
private LinearLayout rawMeatLinearLayout;
private LinearLayout cookedMeatLinearLayout;
private LinearLayout lettuceLinearLayout;
private LinearLayout burgerLinearLayout;

We can then wire these up in the OnCreate(Bundle bundle) method:

bunLinearLayout = FindViewById<LinearLayout>(Resource.Id.bun_layout);
rawMeatLinearLayout = FindViewById<LinearLayout>(Resource.Id.raw_meat_layout);
cookedMeatLinearLayout = FindViewById<LinearLayout>(Resource.Id.cooked_meat_layout);
lettuceLinearLayout = FindViewById<LinearLayout>(Resource.Id.lettuce_layout);
burgerLinearLayout = FindViewById<LinearLayout>(Resource.Id.burger_layout);

Because we inherited from ReactiveActivity<BurgerViewModel>, we now need to ensure we are creating a new ViewModel in our OnCreate(Bundle bundle):

this.ViewModel = new BurgerViewModel();

Our final definition of the OnCreate(Bundle bundle) should look like this:

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

    SetContentView (Resource.Layout.activity_bbq);

    this.ViewModel = new BurgerViewModel();

    bunLinearLayout = FindViewById<LinearLayout>(Resource.Id.bun_layout);
    rawMeatLinearLayout = FindViewById<LinearLayout>(Resource.Id.raw_meat_layout);
    cookedMeatLinearLayout = FindViewById<LinearLayout>(Resource.Id.cooked_meat_layout);
    lettuceLinearLayout = FindViewById<LinearLayout>(Resource.Id.lettuce_layout);
    burgerLinearLayout = FindViewById<LinearLayout>(Resource.Id.burger_layout);
}

Creating an extension method to add an image to the LinearLayout

To visually show our BBQ, we will want to create a helper method to resize a Resource, set the ImageView’s Source, and add it to the LineaLayout. Let add this in the BurgersActivity.cs:

private void AddImageToContainer(LinearLayout container, int imageSource)
{
    int width = Resources.GetDimensionPixelSize(Resource.Dimension.image_max_width);
    LinearLayout.LayoutParams viewParams = new LinearLayout.LayoutParams(width, ViewGroup.LayoutParams.WrapContent);
    ImageView imageView = new ImageView(this);
    imageView.SetImageResource(imageSource);
    container.AddView(imageView, viewParams);
}

Creating extension methods for each BBQ task

Now we simply need a few helper methods to add a different image based on the BBQ task. Let’s add this to the BurgersActivity.cs:

private void SetBun(Bun bun)
{
    AddImageToContainer(bunLinearLayout, Resource.Drawable.bun);
}

private void SetRawMeat(Meat meat)
{
    if (meat.Rotten)
    {
        AddImageToContainer(rawMeatLinearLayout, Resource.Drawable.rawMeatRotten);
    }
    else
    {
        AddImageToContainer(rawMeatLinearLayout, Resource.Drawable.rawMeat);
    }
}

private void SetCookedMeat(Meat meat)
{
    AddImageToContainer(cookedMeatLinearLayout, Resource.Drawable.cookedMeat);
}

private void SetLettuce(Lettuce lettuce)
{
    AddImageToContainer(lettuceLinearLayout, Resource.Drawable.lettuce);
}

private void SetHamburger(Hamburger burger)
{
    AddImageToContainer(burgerLinearLayout, Resource.Drawable.burger);
}

Subscribing to our streams with WhenActivated()

Now the fun part, we can now subscribe to our ViewModel’s streams. To do this, we will use the WhenActivated() in our BurgersActivity constructor. We will then be creating a CompositeDisposable to create a collection of IDisposable which are created from our subscriptions.

        public BurgersActivity()
        {
            this.WhenActivated(() =>
                {
                    var disposable = new CompositeDisposable();

                    disposable.Add(ViewModel.BunStream()
                        .SubscribeOn(Scheduler.Default)
                        .ObserveOn(RxApp.MainThreadScheduler)
                        .Subscribe(Observer.Create<Bun>(bun => SetBun(bun))));

                    disposable.Add(ViewModel.RawMeatStream()
                        .SubscribeOn(Scheduler.Default)
                        .ObserveOn(RxApp.MainThreadScheduler)
                        .Subscribe(Observer.Create<Meat>(meat => SetRawMeat(meat))));

                    disposable.Add(ViewModel.LettuceStream()
                        .SubscribeOn(Scheduler.Default)
                        .ObserveOn(RxApp.MainThreadScheduler)
                        .Subscribe(Observer.Create<Lettuce>(lettuce => SetLettuce(lettuce))));

                    disposable.Add(ViewModel.CookedMeatStream()
                        .SubscribeOn(Scheduler.Default)
                        .ObserveOn(RxApp.MainThreadScheduler)
                        .Subscribe(Observer.Create<Meat>(meat => SetCookedMeat(meat))));

                    disposable.Add(ViewModel.HamburgerStream()
                        .SubscribeOn(Scheduler.Default)
                        .ObserveOn(RxApp.MainThreadScheduler)
                        .Subscribe(Observer.Create<Hamburger>(hamburger => SetHamburger(hamburger))));

                    return disposable;
                }

            );
        }

You may notice that we have SubscribeOn and ObserveOn items defined. Simply put, SubscribeOn refers to the thread in which the actual call to subscribe happens, and ObserveOn refers to the thread in which the subscription is observed on.

We are then creating a new object and using our extension method to visually add an image to the respective LinearLayout.

Summary

Let’s see what we did in action:

We’ve only seen the tip of the iceberg of what Reactive Extensions and ReactiveUI can do for our applications. It is quite a powerful set of libraries that we can use to ease the complexity of our applications.

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

Xamarin.Android Book

If you enjoyed this post, please consider subscribing to my upcoming book’s email list:

You can signup at the following link: Programming Xamarin.Android Email List

Being Reactive

Preface

One of my goals as a developer has been to learn what the big deal is regarding RX(Reactive Extensions). I’ve always been interested in learning more about it, but I’ve had multiple learning blocks trying to wrap my head around it. I first started learning about RX(Reactive Extensions) back in 2013. It’s been four years since and I’m still struggling to learn it. So I figured what better way to learn it other than writing about it?

Why learn RX(Reactive Extensions) from a developer standpoint?

RX has slowly overtaken various development communities by storm. Here’s a few reasons why I personally dived into RX.

Okay…but Why?

I’m not going to convince you with a pros and cons list, but rather I’m going to give an example of how RX can help you.

Scenario: Family BBQ

Imagine the application we are going to create mimics a family BBQ in which hamburgers are served. Let’s give some roles to our family members based on what they are responsible for preparing.

  • Brother - Responsible for preparing raw meat.
  • Sister - Responsible for heating up buns.
  • Father - Responsible for cooking the meat.
  • Mother - Responsible for cutting fresh vegetables.
  • You - Responsible for putting everything together to make a hamburger.

Brother

Brother is taking out raw meat packages from the freezer and following a few steps:

  1. Making sure the meat has not gone bad.
  2. Seasoning the meat.
  3. Rolling the meat into patties.

It takes Brother 3 seconds to prepare each piece of meat.

In short: He is checking that the meat is not Rotten and then preparing each piece in 3 seconds.

Sister

Sister is taking buns out of the freezer and following a couple of steps:

  1. Defrosting the buns.
  2. Heating up the buns.

It takes Sister 5 seconds to heat each bun.

In short: She is Heating the buns which takes 5 seconds each.

Father

Father is near the grill ready to cook and following a couple of steps:

  1. Waiting for Brother to prepare the meat.
  2. Cooking each piece of meat as it comes to him.

Father is fast! It doesn’t take him anytime to cook the meat.

In short: He is Cooking the raw meat when brother provides it as soon as possible.

Mother

Mother is cutting up lettuce and following a couple of steps:

  1. Making sure the lettuce has not gone bad.
  2. Removing the outer leaves and core from the lettuce head
  3. Chop the lettuce

It takes Mother 2 seconds to prepare the lettuce.

In short: She is preparing the lettuce which takes 2 seconds.

You

You are waiting for your family to finish all of their tasks so you can do yours.

In short: You will be taking a Cooked Meat, Heated Bun, and Lettuce to create a Hamburger as soon as you get each ingredient.

Streams

You may also notice that each one of our family members have a role in which they are producing something. However each stream has a different set of dependencies, filters, transformations, and combinations they must adhere to. Each stream also might take different amounts of time to complete.

Filters

This is a way to ensure that we are assembling a perfectly edible hamburger so we do not get sick during our family BBQ.

We only have one example of a filter in this sample:

Brother - Checks to ensure each meat is not Rotten.

Transformations

We are also transforming our ingredients to different states.

We have two examples of tranformations in this sample:

Father - Puts the raw meat on the grill which makes them Cooked.

Sister - Puts the buns in the microwave which make them Heated.

Combinations

Now we need to finally create our hamburger based on the other streams our family is producing. I’ve color coordinated each ingredient to show what hamburger it belongs to. The items marked in Red sadly get discarded because we don’t have enough Meat that is Cooked, sadly Brother has already thrown away the Rotten meat.

RX in Action

Why is this better?

This is because reactive programming lets you focus on what you’re trying to achieve rather than on the technical details of making it work. This leads to simple and readable code and eliminates most boilerplate code (such as change tracking or state management) that distracts you from the intent of your code logic. When the code is short and focused, it’s less buggy and easier to grasp.

Quoted from the book RX.NET in Action

The Code

You can find the code which you can run locally in Visual Studio or LINQPad here:

https://gist.github.com/JonDouglas/56b61d43c60d987efeef7c9d294adfe8

This sample will run continuously to demonstrate assembling a hamburger.

Note: Don’t forget to install RX - Main into your project!

https://www.nuget.org/packages/System.Reactive/

Summary

It took me over 4 slow years to realize that Reactive Extensions(RX) is a good idea. It’s never too late to start learning something new. I’m learning something new about RX everyday at a snail’s pace, but at least I’m learning something!

Big thanks to Shane Neuville(https://twitter.com/PureWeen) and Paul Betts(https://twitter.com/paulcbetts) for various tips and tricks on earlier drafts of this blog post!

Note: The diagrams in this blog post are RTL when in reality most diagrams show LTR such as marble diagrams. This was a bit easier for me to learn with.

Icons

Icons were used from the Noun Project .

Bun - Karina M
Hamburger - chiara galli
Romaine Lettuce - Imogen Oh
Microwave - Nook Fulloption
Grill - Aleksandr Vector
Meat - Vladimir Belochkin
Meat -  Rutmer Zijlstra
Flies - Blaise Sewell
Heat -  Stan Diers

Xamarin.Android Book

If you enjoyed this post, please consider subscribing to my upcoming book’s email list:

You can signup at the following link: Programming Xamarin.Android Email List

MSBuild Basics

MSBuild Basics

MSBuild is the music conductor of build tooling. It is extremely powerful for understanding and customizing your build process.

Quick Installation

You should have MSBuild installed already on your computer. Ensure you are in the correct path or add the msbuild path to your Environment Variables.

Basic MSBuild command:

msbuild [INPUT].csproj /t:[TARGET]

Project File (.csproj is the MSBuild file)

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
</Project>

Xamarin.Android Example:

<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

All content will be placed inside of the <Project> tag. This includes, properties, items, targets, etc.

Think of this as the files being build, parameters for build, and much more.

Properties

MSBuild properties are key-value pairs. The key is the name that you use to refer the property. The value is the value of the property.

When declaring a property, they must be contained inside a <PropertyGroup> element. This element must be inside of the <Project> element.

Note: You can have separate properties as long as they are in their own element.

Xamarin.Android Example:

<PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProductVersion>8.0.30703</ProductVersion>
    <SchemaVersion>2.0</SchemaVersion>
    <ProjectGuid>{221B57B2-7897-411E-992F-D8046688CFA3}</ProjectGuid>
    <ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
    <OutputType>Library</OutputType>
    <AppDesignerFolder>Properties</AppDesignerFolder>
    <RootNamespace>App8</RootNamespace>
    <AssemblyName>App8</AssemblyName>
    <FileAlignment>512</FileAlignment>
    <AndroidApplication>True</AndroidApplication>
    <AndroidResgenFile>Resources\Resource.Designer.cs</AndroidResgenFile>
    <GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
    <AndroidUseLatestPlatformSdk>True</AndroidUseLatestPlatformSdk>
    <TargetFrameworkVersion>v7.1</TargetFrameworkVersion>
    <AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
  </PropertyGroup>

Tasks and Targets

Task

A Task is the smallest unit of work. Typically known as an action or routine.

Example of a Task:

<Message Text="Hello Support Team Members!" />

Target

A Target is a series of executable steps/tasks.

Example of an empty Target:

<Target Name="HelloSupportTeam">
</Target>

Default Tasks

Copy, Move, Exec, ResGen, Csc - Copy Files, Move Files, Execute a program, Generate Resources, C# Compiler

Message - Sends a message to the loggers that are listening in the build process.

<Target Name="HelloSupportTeam">
    <Message Text="Hello Support Team Members!" />
</Target>

Running a Target

A Target can be run in MSBuild simply by the following syntax

msbuild MyProject.csproj /t:<TargetName> or msbuild MyProject.csproj /target:<TargetName>

Let’s try running the HelloSupportTeam target now.

msbuild MyProject.csproj /t:HelloSupportTeam

You should see the following output:

HelloSupportTeam:
  Hello Support Team Members!

Properties inside a Task

Let’s now define a <PropertyGroup> with our message instead.

<PropertyGroup>
    <HelloSupportTeamMessage>Hello again brownbaggers!</HelloSupportTeamMessage>
</PropertyGroup>

Now we need to use the property variable instead. Let’s edit our <Message> Task. The syntax for a property in MSBuild is $(PropertyName):

<Target Name="HelloSupportTeam">
    <Message Text="$(HelloSupportTeamMessage)" />
</Target>

Items (Aka Files)

Items are file-based references. Similar to <PropertyGroup>, you define them by using an <ItemGroup> instead.

Xamarin.Android Example:

  <ItemGroup>
    <Compile Include="MainActivity.cs" />
    <Compile Include="Resources\Resource.Designer.cs" />
    <Compile Include="Properties\AssemblyInfo.cs" />
  </ItemGroup>

To evaulate an item, you would use the @(ItemType) syntax. Let’s now spit out a Message to see what is all going to be compiled here.

<Target Name="PrintCompileInfo">
    <Message Text="Compile: @(Compile)" />
</Target>

Item Metadata

Items have metadata to them as well. There are many well-known metadata items that we can query, however I will not be going into detail on them all. Here is a sample of getting the directory of an item:

<Target Name="PrintMetadata">
    <Message Text="%40(Compile-> '%25(Directory)'): @(Compile->'%(Directory)')" />
</Target>

Conditions (https://youtu.be/yhOKhJaM1QE?t=8)

Condition are used to evaluate a true or false condition.

Xamarin.Android Example:

  <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>

You can check multiple types of conditions such as:

  • == - Equality
  • != - Inequality
  • Exists - If something exists
  • !Exists - If something does not exist

Conditions are especially useful for adding an Item based on a condition such as a $(Configuration) being Debug or Release

Initial Targets

You may be asking yourself…How the hell do we know what Target get executed first? Well the truth is, I slipped a detail past you really quickly. Let’s go back to our original definition of our <Project>:

<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

This will default any Target named Build will be the default target if no other Target is defined when invoking.

Extending MSBuild

Okay now for the actual fun part. Extending the build process.

There’s a few ways to extend the MSBuild process in the sense of a pre or post build action.

  1. Pre and Post build events
  2. Override BeforeBuild / AfterBuild target
  3. Extend the BuildDependsOn list
<Target Name="BeforeBuild">
</Target>

<Target Name="AfterBuild">
</Target>

Common files names

.proj or .csproj

.proj is the generic. .csproj is a C# specific. .vbproj is the Visual Basic specific

.targets

.targets is a file that contains shared targets which are imported into other files

.props

.props is a file that contains settings for the build process

.tasks

.tasks is a file that contains UsingTask definitions

Intellisense of items

There’s a few xsd files that are used in VS for general intellisense. You can also look for an item directly by opening the schema:

C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\Xml\Schemas\1033\MSBuild

Debugging Tasks

Three main ways:

  1. Use logs of logging statements and examine the logger
  2. Use Debugger.Launch() to prompt for a debugger attachment
  3. Start MSBuild as an external program, and then debug normally

Other Resources

The most invaluable resource you’ll have on MSBuild is Sayed’s book:

http://msbuildbook.com/

Video Resources:

https://channel9.msdn.com/Shows/Code-Conversations/Introduction-to-MSBuild-in-NET-Core-with-Nate-McMaster

https://channel9.msdn.com/Shows/Code-Conversations/Advanced-MSBuild-Extensibility-with-Nate-McMaster

https://channel9.msdn.com/Shows/Code-Conversations/Sharing-MSBuild-Tasks-as-NuGet-Packages-with-Nate-McMaster

Xamarin.Android Proguard Notes

Preface

So you’ve heard of this tool called “Proguard” which can Shrink and Optimize unused code from our apps and the libraries they reference. Little did you know that you actually have to do a bit of work so Proguard can work for you.

Holy Notes Batman!

Have you ever enabled Proguard in your project and now your Build Output is plagued by logs of Note:?

Here’s a sample of a Build Output of a project that has the Xamarin Android Support Library - Design(https://www.nuget.org/packages/Xamarin.Android.Support.Design/) installed. This of course has a large list of dependencies on other support libraries.

https://gist.github.com/JonDouglas/fe19e8e7be658779e8b68b90c8fc7aad

You may notice hundreds of notes saying

Note: the configuration doesn't specify which class members to keep for class `package.ClassName`

Proguard Summary

However let’s first get some numbers about just how much is affecting us. At the bottom of our Proguard Task, we will find a summary that let’s us know exactly how much.

1>  Note: there were 109 references to unknown classes. (TaskId:247)
1>        You should check your configuration for typos. (TaskId:247)
1>        (http://proguard.sourceforge.net/manual/troubleshooting.html#unknownclass) (TaskId:247)
1>  Note: there were 1 classes trying to access generic signatures using reflection. (TaskId:247)
1>        You should consider keeping the signature attributes (TaskId:247)
1>        (using '-keepattributes Signature'). (TaskId:247)
1>        (http://proguard.sourceforge.net/manual/troubleshooting.html#attributes) (TaskId:247)
1>  Note: there were 10 unkept descriptor classes in kept class members. (TaskId:247)
1>        You should consider explicitly keeping the mentioned classes (TaskId:247)
1>        (using '-keep'). (TaskId:247)
1>        (http://proguard.sourceforge.net/manual/troubleshooting.html#descriptorclass) (TaskId:247)
1>  Note: there were 5 unresolved dynamic references to classes or interfaces. (TaskId:247)
1>        You should check if you need to specify additional program jars. (TaskId:247)
1>        (http://proguard.sourceforge.net/manual/troubleshooting.html#dynamicalclass) (TaskId:247)
1>  Note: there were 6 accesses to class members by means of introspection. (TaskId:247)
1>        You should consider explicitly keeping the mentioned class members (TaskId:247)
1>        (using '-keep' or '-keepclassmembers'). (TaskId:247)
1>        (http://proguard.sourceforge.net/manual/troubleshooting.html#dynamicalclassmember) (TaskId:247)

Proguard Reminder

Before jumping into these items one by one, let’s first understand what’s going on when we are calling Proguard.

Simply put, we are calling the Proguard.jar and providing various Configuration files that depict how Proguard should process the shrinking.

The three main files to care about are the following:

proguard_xamarin.cfg

These are rules unique to Xamarin. Such as ensuring to keep the mono, android, and java classes that Xamarin.Android relies on.

ProguardCommonXamarinConfiguration=obj\Release\proguard\proguard_xamarin.cfg

proguard_project_references.cfg

These are rules that are generated based on the ACW(Android Callable Wrappers) of your references. In this case the Xamarin.Android.Support.Design library and it’s dependencies.

ProguardGeneratedReferenceConfiguration=obj\Release\proguard\proguard_project_references.cfg

proguard_project_primary.cfg

These are rules that are generated based on your Application.

ProguardGeneratedApplicationConfiguration=obj\Release\proguard\proguard_project_primary.cfg

Custom proguard.cfg

Finally it will include your custom Proguard.cfg file which is defined with the Build Action of ProguardConfiguration.

Let’s break these notes down one by one:

references to unknown classes

1>  Note: there were 109 references to unknown classes. (TaskId:247)
1>        You should check your configuration for typos. (TaskId:247)
1>        (http://proguard.sourceforge.net/manual/troubleshooting.html#unknownclass) (TaskId:247)

Your configuration refers to the name of a class that is not present in the program jars or library jars. You should check whether the name is correct. Notably, you should make sure that you always specify fully-qualified names, not forgetting the package names.

We can now search for the keywords unknown class within our Build Output and see what type of classes it’s throwing this Note: for.

EX: Note: the configuration refers to the unknown class 'com.google.vending.licensing.ILicensingService'

So it is saying that our configuration refers to this unknown class, but if we did a bit of searching, we don’t see this class anywhere within our .cfg files above. Let’s add some diagnostics by adding the -printconfiguration config.txt rule into our custom Proguard.cfg file.

If we now look through this config.txt file, we will see the rule caught red handed:

-keep public class com.google.vending.licensing.ILicensingService

Unfortunately this is a limitation of generation of Proguard rules. Some of these will be completely out of your control.

access generic signatures using reflection

1>  Note: there were 1 classes trying to access generic signatures using reflection. (TaskId:247)
1>        You should consider keeping the signature attributes (TaskId:247)
1>        (using '-keepattributes Signature'). (TaskId:247)
1>        (http://proguard.sourceforge.net/manual/troubleshooting.html#attributes) (TaskId:247)

Your code uses reflection to access metadata from the code, with an invocation like “class.getAnnotations()”. You then generally need to preserve optional class file attributes, which ProGuard removes by default. The attributes contain information about annotations, enclosing classes, enclosing methods, etc. In a summary in the log, ProGuard provides a suggested configuration, like -keepattributes Annotation. If you’re sure the attributes are not necessary, you can switch off these notes by specifying the -dontnote option.

This one however is straight forward by just adding the -keepattributes Signature to our Custom Proguard.cfg file.

unkept descriptor classes in kept class members.

1>  Note: there were 10 unkept descriptor classes in kept class members. (TaskId:247)
1>        You should consider explicitly keeping the mentioned classes (TaskId:247)
1>        (using '-keep'). (TaskId:247)
1>        (http://proguard.sourceforge.net/manual/troubleshooting.html#descriptorclass) (TaskId:247)

Your configuration contains a -keep option to preserve the given method (or field), but no -keep option for the given class that is an argument type or return type in the method’s descriptor. You may then want to keep the class too. Otherwise, ProGuard will obfuscate its name, thus changing the method’s signature. The method might then become unfindable as an entry point, e.g. if it is part of a public API. You can automatically keep such descriptor classes with the -keep option modifier includedescriptorclasses (-keep,includedescriptorclasses …). You can switch off these notes by specifying the -dontnote option.

You know the process now, let’s search for “descriptor class” in our Build Output.

EX: Note: the configuration keeps the entry point 'android.support.design.widget.BaseTransientBottomBar$SnackbarBaseLayout { void setOnLayoutChangeListener(android.support.design.widget.BaseTransientBottomBar$OnLayoutChangeListener); }', but not the descriptor class 'android.support.design.widget.BaseTransientBottomBar$OnLayoutChangeListener'

Again, search the config.txt for the first class to ensure it’s there:

i.e. -keep class android.support.design.widget.BaseTransientBottomBar$SnackbarBaseLayout

We do see it inside. So it’s saying we need a -keep rule for android.support.design.widget.BaseTransientBottomBar$OnLayoutChangeListener. Let’s now add that to our Custom Proguard.cfg file:

-keep class android.support.design.widget.BaseTransientBottomBar$OnLayoutChangeListener

unresolved dynamic references to classes or interfaces.

1>  Note: there were 5 unresolved dynamic references to classes or interfaces. (TaskId:247)
1>        You should check if you need to specify additional program jars. (TaskId:247)
1>        (http://proguard.sourceforge.net/manual/troubleshooting.html#dynamicalclass) (TaskId:247)

ProGuard can’t find a class or interface that your code is accessing by means of introspection. You should consider adding the jar that contains this class.

Let’s search for dynamically referenced class in our Build Output.

EX: Note: android.support.v4.media.ParceledListSliceAdapterApi21: can't find dynamically referenced class android.content.pm.ParceledListSlice

Unfortunately this one isn’t that straight forward as something is referencing classes or interfaces that are not present in our project. To solve this, we would have to find the dependency and add it via NuGet.

accesses to class members by means of introspection.

1>  Note: there were 6 accesses to class members by means of introspection. (TaskId:247)
1>        You should consider explicitly keeping the mentioned class members (TaskId:247)
1>        (using '-keep' or '-keepclassmembers'). (TaskId:247)
1>        (http://proguard.sourceforge.net/manual/troubleshooting.html#dynamicalclassmember) (TaskId:247)

Your code uses reflection to find a fields or a method, with a construct like “.getField(“myField”)”. Depending on your application, you may need to figure out where the mentioned class members are defined and keep them with an option like “-keep class MyClass { MyFieldType myField; }”. Otherwise, ProGuard might remove or obfuscate the class members, since it can’t know which ones they are exactly. It does list possible candidates, for your information. You can switch off these notes by specifying the -dontnote option.

EX: Note: android.support.v4.app.NotificationCompatJellybean accesses a declared field 'icon' dynamically

We may just want to let Proguard know “Hey please don’t note me on this”. We can use the -dontnote rule for this specific class. Let’s add this to our Custom Proguard.cfg file:

-dontnote android.support.v4.app.NotificationCompatJellybean

Summary

Now you should know the basics of how Proguard shrinks our code and how we can adjust to the notes that proguard gives us. For the most part, Proguard does a great job of documenting what is going on and how you can fix certain scenarios. Take the time to read through your Proguard logs to optimize the ruleset so Proguard can work for you!

As a bonus, you can always start off with some of these community created Proguard rules to help give you a jump start with these libraries and proguard.

https://github.com/yongjhih/android-proguards

https://github.com/krschultz/android-proguard-snippets

If you enjoyed this post, please consider subscribing to my upcoming book’s email list:

You can signup at the following link: Programming Xamarin.Android Email List

Xamarin.Android Linker Tricks Part 1 - Bitdiffer

Preface

The linker used in Xamarin.Android applications has historically been a painpoint for developers.

This is mainly because as developers, we only use a small portion of the assemblies we consume. For the rest of those assemblies, we consider using a linker mechanism so we reap the benefits of having a smaller assembly at the end of the day.

To give a real world example of what the linker does, I’ve created three .apk files with the different linker options:

  • None
  • Sdk Assemblies Only
  • Sdk and User Assemblies
04/13/2017  11:15 AM        52,160,463 LinkerSample.None.apk
04/13/2017  11:12 AM        21,995,031 LinkerSample.SDK.apk
04/13/2017  11:16 AM        10,130,214 LinkerSample.UserAndSDK.apk

Note: This project includes a reference to the Xamarin.Android.Support.Design NuGet to demonstrate something more than “Hello World”

As you can see, we see the size of 52mb(None), 21mb(SDK), and 10mb(User and SDK). What this means is that in a perfect world, we would want to use Sdk and User Assemblies so we can keep our .apk size down. However using that setting is considered the most aggressive linker setting and it can strip away things that you need to use in your application. Finding out exactly what it strips out is a bit difficult and we should fallback to other tooling.

Using bitdiffer

bitdiffer is a tool that helps compare assembly files. It is extremely useful in the sense of a practical GUI/CLI that lets us see the difference between assemblies. You can download it here:

https://github.com/bitdiffer/bitdiffer

One of the most useful things bitdiffer does is allows you to add not only 2 assemblies, but a full list for comparison. This is extremely useful for us as developers to see what exactly each linker option does to our assembly.

Consider the following example with the three .apk we created earlier comparing against the Mono.Android.dll assembly. They are ordered None, Sdk Assemblies Only, Sdk and User Assemblies in the tabs:

You can notice off a quick glance that we had quite a difference in Members Changed or Removed between the linker setting None -> Sdk Assemblies Only -> Sdk and User Assemblies.

Now that’s not the only cool feature of this tool, you can also dive down into a specific class to see the diff between your assemblies:

And finally we can dig into what the difference is between members in our class:

Although this is only part 1 of various linker tricks I use to diagnose a problem, this is one of the more powerful ways to approach any linker issues in your project to understand the problem.

If you enjoyed this post, please consider subscribing to my upcoming book’s email list:

You can signup at the following link: Programming Xamarin.Android Email List