16 Aug 2017
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
01 Aug 2017
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.
- Influencers have double downed on the library
- Google has moved to a RX mindset with their new Architecture Components
- Other languages/frameworks have implementations (Thus the knowledge is universal)
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:
- Making sure the meat has not gone bad.
- Seasoning the meat.
- 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:
- Defrosting the buns.
- 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:
- Waiting for
Brother
to prepare the meat.
- 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:
- Making sure the lettuce has not gone bad.
- Removing the outer leaves and core from the lettuce head
- 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
.
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
26 Jul 2017
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>
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>
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.
- Pre and Post build events
- Override
BeforeBuild
/ AfterBuild
target
- 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:
- Use logs of logging statements and examine the logger
- Use
Debugger.Launch()
to prompt for a debugger attachment
- 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
20 Jul 2017
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
13 Apr 2017
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