XAMARIN ANDROID BINDINGS LIBRARY AND MISSING AAR

Standard

Hey – this is another one, short post about Xamarin Android Bindings Library 🙂

Sometimes you need to bind library but you can’t find .aar/.jar – in this post I will show you how to get library and required dependencies really fast.

Step one – Android Studio

Install Android Studio (you have probably done that before, haven’t you?) and create new empty Android project.

Step two – add dependency you want to bind

I am almost sure that library you want to bind is available on gradle (NuGet in Java world). Most of libraries have its gradle-name written in documentation/github.

In my case it is:

com.hypertrack:android:0.6.25

Edit build.gradle file as follows:

Screen Shot 2018-04-03 at 21.46.14.png

Screen Shot 2018-04-03 at 21.46.20.png

Step three – build and copy your library 🙂

When you add required dependency to gradle just build project. During build process gradle task will download all required dependencies.

Now you are done – just check  ~/.gradle/caches/modules-2/files-2.1 folder on your Mac OSX.

There are all required dependencies right there (even transitive ones). Make sure you check .pom file as well – you will have to add project dependencies to bindings project as well.Screen Shot 2018-04-03 at 21.45.47.png

Advertisements

Xamarin Android Bindings fails? Lets hack into JNI World

Standard

Hey.

I am writing Android application which requires some kind of “Emoji” selector (emoji picker).

The first thing I did – I’ve looked for some ready-to-use implementations and I found brilliant java library by Niklas Baudy Vanniktech called Emoji

This is well developed, modular library – you install main package “Emoji” and then Emoji Providers – so you can use multiple emoji types (Google-like/iOS/Twitter and so on).

Creating Xamarin Android Bindings Project

Library did not have Xamarin Android bindings so I had to generate it manually. I have put output main library and Google Compat Emoji Provider (.aar files) into Android Bindings projects (binding generator).

Adding Android library dependencies to Bindings Project

After I have added .aar files to bindings project I added dependencies required by Emoji library. This is usually a step where less advanced developers fails while creating Xamarin Bindings.
If library A takes dependency to library B and C, the bindings project of library A has to reference bindings or reference jars of library B and C.

To check we need to look at binded library build.gradle or pom file (this is something similar to Nuget packages.xml/project.json we use in .NET).
I have looked at build.gradle of Emoji and Emoji Google Compat Provider:

App build.gradle -> https://github.com/vanniktech/Emoji/blob/master/build.gradle

Emoji build.gradle:

emojibuildgradlegooglecompatbuildgradle

As we can see, Emoji library needs reference to Android Support AppCompat and CardView libraries. The Emoji-Google-Compat (Google Emojis Provider) requires reference to original Emoji library and Android Support Emoji Library.

Those are pretty common libraries in Android world so I can just add Nuget packages of those libraries to the Emoji and Emoji Google Compat Bindings Project.

Screen Shot 2018-03-24 at 15.12.53.png\

Fist error on board

We start building our projects and we get…

Screen Shot 2018-03-24 at 15.21.50.png

GoogleCompatEmoji: cannot derive from sealed type “Emoji”.

Well, we need to check how generated Com.Vanniktech.Emoji.Emoji.Emoji class look like.

Lets look at generated code

To see how generated code look like toggle “Display all files” in Visual Studio for Mac option at solution level.

Screen Shot 2018-03-24 at 15.17.42.png

Then expand “obj”/”generated/src” folders and open “Emoji” class.

Screen Shot 2018-03-24 at 15.26.24.png

As we can see –  class has sealed modifier which means we can not inherit from that. Those kind of a problems arise because of C#/Java languages differences – not all of them are correctly resolved by Xamarin Bindings generator so we have to fix that manually.

Each bindings project has Transforms folder with “metadata.xml” file – which can be used to modify generator output code.

We have path to the generated class – we just need to remove “sealed” attribute.
This can be done by using final metadata attribute.

Screen Shot 2018-03-24 at 15.29.13.png

To read more about available transforms check: https://developer.xamarin.com/guides/android/advanced_topics/binding-a-java-library/customizing-bindings/java-bindings-metadata/ 

Use bindings library in Android application

Both projects builds without any error now – seems as everything works. This is the time to use documentation of original library and plug it into my app.

Screen Shot 2018-03-24 at 15.15.18.png

Unfortunately – bindings generator did not generate GoogleCompatEmojiProvider class which is required to setup library. The other classes suggested by documentation like EmojiPopup  are available though.

Resolve errors by analysing original code

First – lets check what classes are generated in Emoji and Emoji Google Compat bindings project.

Screen Shot 2018-03-24 at 15.40.12.png

Screen Shot 2018-03-24 at 15.35.05.png

Right, we do not have GoogleCompatEmojiProvider class. Weird.
We need to find reason why this class has not been generated in Bindings Project.
The only reliable way to do that is by checking original java code – fortunately this is open-source project so we can do that – https://github.com/vanniktech/Emoji/blob/master/emoji-google-compat/src/main/java/com/vanniktech/emoji/googlecompat/GoogleCompatEmojiProvider.java.

(btw. what to do when we do not have access to source code? easy, just look at decompiled original library ^_^  how? look at my MultiDex post)

If class is not generated – the problem almost always lie in fact that it uses type that is not available either in bindings project or referenced project.

Lets look at class definition

public final class GoogleCompatEmojiProvider implements EmojiProvider, EmojiReplacer

This class implements *EmojiReplacer* interface but it is not available in *Emoji library bindings* (see screens above).

Resolve errors by analysing original code part 2

So we now know that in order to get GoogleCompatEmojiProvider we need to get EmojiReplacer interface in Bindings.

Lets look at original code once again – this time at *EmojiReplacer* interface – https://github.com/vanniktech/Emoji/blob/ebe1cb05c38999dc4233655b8d754e33cacd4c71/emoji/src/main/java/com/vanniktech/emoji/EmojiReplacer.java

Screen Shot 2018-03-24 at 15.43.37.png

Honestly, I don’t know if it is possible to make Bindings Generator generate automatically this interface for us. I suspect there is a bug there which prevents to generate classes which have strong cycles – replaceWithImages method takes EmojiReplacer as method  parameter.

Can we bind this library anyway?

Yes, we can 🙂

Although the only way I have found to restore EmojiReplacer class back is by writing it manually 🙂

I have not found a lot documentation about manually putting Java classes into Xamarin world so I have analysed and reverse-engineered Bindings Generator output code.

To use Java types in C#, Xamarin uses bunch of [Register] attributes and pointers (IntPtr).
In fact – it is not really intended to be used in “manual code”.

The mechanism is briefly described here: https://developer.xamarin.com/guides/android/advanced_topics/binding-a-java-library/customizing-bindings/java-bindings-metadata/

Register Attribute

Lets start with analysing [Register] attribute.
To use Java code in C# – we have to use this attribute. Shortly speaking – it tells compiler how to control C#-Java proxy. Screen Shot 2018-03-24 at 15.59.44.png

First parameter is name – this is the path to the type – in case of interface –EmojiReplacer that would be com/vanniktech/emoji/EmojiReplacer

The second parameter is signature – this is used to determine method signature (what kind of parameter it takes, what is the return type) – for interface we should leave it empty -> “”

The third parameter is connector – that is managed class name which connects native code with managed code. This is why bindings output contains bunch of Invoker classes – they are called whenever the original interface method is called.

EmojiReplacer interface bound to C#

Screen Shot 2018-03-24 at 16.05.16.png

1. We have to add IEmojiReplacer interface in Bindings project.
2. We have to register this interface – EmojiReplacer sits in com.vanniktech.emoji package so “name” parameter is equal to “com/vanniktech/emoji/EmojiReplacer“.
3. EmojiReplacer is interface so we leave signature parameter empty.
4. We have to implement connector class – the managed code which will be called when the interface is used – we will create class IEmojiReplacerInvoker in namespace Com.Vanniktech.Emoji so we put “Com.Vanniktech.Emoji.IEmojiReplacerInvoker” in “connector” parameter.

5. Looking at EmojiReplacer we can observer that it has just one method – replaceWithImages and it takes five parameters.
We have to add appropriate [Register] attribute.

6. First parameter – name should be equal to java method name – “replaceWithImages“.

7. Second parameter – should describe method signature.
Primitive types are described with one letter, ex. “F” stands for float, “V” for void
Complex types like objects are prefixed with Lnamespace. There is “;” character after each complex type.

The parameter format is:
(parameters description)ReturnType

We have five parameters:

a) Android.Content.Context – object so – “Landroid/content/Context;“.

b) Android.Text.Spannable – object so – “Landroid/text/Spannable;“.

c) Float – primitive type float so – “F“.

d) Float – primitive type float so – “F“.

e) IEmojiReplacer  – object which we have just registered so – “L/com/vanniktech/emoji/EmojiReplacer”.

The return type is void – so “V“.

Putting that together we get:
(Landroid/content/Context;Landroid/text/Spannable;;FFL/com/vanniktech/emoji/EmojiReplacer;)V

8. Third parameter – should contain path to the managed method of previously described connector class.

The format for this parameter is:
“registeredMethodName:namespace.ConnectorClassName, AssemblyWhereConnectorClassLies”.

We will add this connector class in next step.

EmojiReplacer interface bound to C# – managed connector class

The final connector class is presented below. It had been written partially manually thus I will explain it in a moment.

[ [global::Android.Runtime.Register (“com/vanniktech/emoji/EmojiReplacer”, DoNotGenerateAcw=true)]
    internal class IEmojiReplacerInvoker : global::Java.Lang.Object, IEmojiReplacer {

        static IntPtr java_class_ref = JNIEnv.FindClass (“com/vanniktech/emoji/EmojiReplacer”);

        protected override IntPtr ThresholdClass {
            get { return class_ref; }
        }

        protected override global::System.Type ThresholdType {
            get { return typeof (IEmojiReplacerInvoker); }
        }

        IntPtr class_ref;
 

        static IntPtr Validate (IntPtr handle)
        {
            if (!JNIEnv.IsInstanceOf (handle, java_class_ref))
                throw new InvalidCastException (string.Format (“Unable to convert instance of type ‘{0}’ to type ‘{1}’.”,
                            JNIEnv.GetClassNameFromInstance (handle), “com.vanniktech.emoji.EmojiReplacer”));
            return handle;
        }

        protected override void Dispose (bool disposing)
        {
            if (this.class_ref != IntPtr.Zero)
                JNIEnv.DeleteGlobalRef (this.class_ref);
            this.class_ref = IntPtr.Zero;
            base.Dispose (disposing);
        }

        public IEmojiReplacerInvoker(IntPtr handle, JniHandleOwnership transfer) : base (Validate (handle), transfer)
        {
            IntPtr local_ref = JNIEnv.GetObjectClass (((global::Java.Lang.Object) this).Handle);
            this.class_ref = JNIEnv.NewGlobalRef (local_ref);
            JNIEnv.DeleteLocalRef (local_ref);
        }
        
        static IntPtr _replaceWithImagesMethodPointer;
        
        
        [Register (“replaceWithImages”, “(Landroid/content/Context;Landroid/text/Spannable;FFLcom/vanniktech/emoji/EmojiReplacer;)V”, “”)]
        public virtual unsafe void ReplaceWithImages(Context context, ISpannable text, float emojiSize, float defaultEmojiSize,
            IEmojiReplacer fallback)
        {
            if (_replaceWithImagesMethodPointer == IntPtr.Zero)
                _replaceWithImagesMethodPointer = JNIEnv.GetStaticMethodID (class_ref, “replaceWithImages”, “(Lcom/vanniktech/emoji/EmojiReplacer;)V”);
            try
            {
                JValue* __args = stackalloc JValue[5];
                __args[0] = new JValue(context);
                __args[1] = new JValue(text);
                __args[2] = new JValue(emojiSize);
                __args[3] = new JValue(defaultEmojiSize);
                __args[4] = new JValue(fallback);
                JNIEnv.CallVoidMethod((this).Handle, _replaceWithImagesMethodPointer, __args);
            } finally 
            {
            }        
        }
    }

 

Lets start with class definition – it has to inherit from Java.Lang.Object and implement interface (for which it has been defined as connector).

Diving through binding generator code it turns out that we have to override two members – ThresholdClass and ThresholdType.

ThresholdClass should be a pointer to Java.Lang.Class for which it was registered as connector – use JNIEnv.FindClass(“path”) for that.
TheresholdType should return Invorker C# Type.

The Validate, Dispose and Constructor has been copied from other autogenerated binding class. This is responsible for reference counting of the object – you can just copy-paste that in your Invoker class.

The implementation of IEmojiReplacer is the thing what we really need here.
It finds pointer to the Java Method, create method parameters call stack and finally calls Java method using JNIEnv.CallVoidMethod(..)

We allocate JValue array on stack using C# stackalloc (so it won’t create garbage collector pressure – as far as I know that was one of the optimizations made by Xamarin team to the Bindings Generator – which had some positive performance impact on Xamarin.Java code).

Make sure you set JValue array members in original Java method parameters order – so the method is called properly with proper parameters.

Build and have a fun!

After rebuild – it works! The missing classes suddenly popped out.

Library works but has some annoying issues (for ex. does not work inside fragment, had to hack a bit).

Screenshot_1521931925.png

I have showed you few advanced techniques dealing with Xamarin Android Bindings issues.

Hope it help you someday!

Advanced developer lesson with AdvancedRecyclerView :)

Standard

Hey!

Have you ever tried to implement all those fancy features seen in Google applications like: Swipe To Delete, Grouped lists with Expand Collapse features?

Have you ever needed to implement RecyclerView Header or Footer quickly ?

Maybe you have accomplished those tasks but you are one of #MvvmCross-fanatic (as I am 🙂 ) – and you are angry at the lack of data bindings..?

If you have said “yes” at least once during reading those questions I am coming with solution to your problems with my new library… MvvmCross.AdvancedRecyclerView !

MvvmCross.AdvancedRecyclerView is an AdvancedRecyclerView Mvx wrapper. It adds MvvmCross-style data bindings to AdvancedRecyclerView – a library which implements inter alia: high performance Expandable Grouped Lists, Swipe-To-Delete, Header and Footer RecyclerView support.
Besides MvvmCross support package I have published bindings library for AdvancedRecyclerView.

Just check this awesome library in action: https://github.com/h6ah4i/android-advancedrecyclerview – preview video available. Thanks Haruki Hasegawa !

Check documentation I have created today and feel free to type:

Install-Package MvvmCross.AdvancedRecyclerView
… or
Install-Package Xamarin.Bindings.AdvancedRecyclerView (if you plan to use pure library without MvvmCross bindings)

in your NuGet Package Manager Console 😉

… by the way – full story of MvvmCross grouping support  🙂

Some of you might remember my attempt/implementation of Grouping in MvvmCross RecyclerView support package which got to the official nuget channel.
I have even presented it on Xamarines – ..but well as it was my first public presentation I personally think I have failed there as well – buried with stress 🙂

No worries though – I am getting better 😀 … and I think I have solved Xamarin.Android grouped lists problem as well now 🙂

The things is – I have made biggest mistake software developer can make. Tried to be smarter than others and I tried to reimplement the wheel.

During testing it brought a lot of bugs, performance was not sufficient – therefore it had to be removed for a while – turns out implementing grouping is a really difficult task. Instead of trying to fix it at the all cost I decide to seek smarter way of doing grouping.

I recalled that I have used and even published some Pull Requests to library which implemented expandable lists (AdvancedRecyclerView). I looked at this library Java Code once again and it was pretty complex. Instead of coding all the logic in Xamarin – I decided to just create MvvmCross bindings around this java library. It wasn’t as easy as I thought though.

Lesson learned – if you plan to implement some pretty complex feature look at similar, open source code – if it looks difficult, try to take as much as possible from other people work (IF LICENSE ALLOWS THAT, I don’t want to visit my readers in jail 😀)

PS:
Thanks once again to InsaneLab – my developer home for giving me time to work on this awesome project!

Cache my Refit with Refit Insane PowerPack

Standard

Hey!

My developer home of the best responsive and elegant mobile applications InsaneLab.com goes really insane !

We have just published my new .NET/Xamarin library – Refit.Insane.PowerPack which adds attribute based auto-retry and response cache with just few steps!

Check what we have for you here: https://github.com/thefex/Refit.Insane.PowerPack 

Presentation (can be used as documentation) which is available on my GitHub will be presented in few hour on Xamarines.com – the best Cracow, Xamarin meet up organised by SII and In’sanelab 🙂

See you soon!

I have pushed PUSH Library – Insane Notifications

Standard

Hey!

I have not written for sooooo long time but I was really, really busy. However it is in a good manners to come back with some gift…

…and that’s why I am presenting you now my new library – Xamarin PUSH plugin – Insane Notifications, created with passion thanks to cooperation with In`saneLab – a Polish software house which provides Mobile and Web solutions located in Cracow.

Plugin uses concepts I have learnt in MvvmCross – like Remote Notifications presenter (similar to MvxViewPresenter) and enormously reduces code amount you have to write to get PUSH Notifications works in your app.

I have prepared code samples for Android, iOS, UWP + Backend Azure Notification Hub sample + presentation which can be used as documentation.

Check it now on my GitHub and install nuget package (search for InsaneNotifications)!
As a learning documentation I have prepared presentation which has been shown on Warsaw Xamarin DevDays – get it here 🙂

By the way, stay tuned and observe my blog around 22 June 🙂

Another neat library is coming out soon… 😉

MultiDex in Xamarin

Standard

Hey!

First – lets start with a question:

What is Dex format and why should I even care? (theory)

Nowadays modern mobile users are very exigent – they want great, unique Material design, in-app maps, fast and precise geolocation, social (let’s use synonym – Facebook 🙂 ) sign-in and so on…

The problem is all those elegant features takes space.. Every line of code you write is transformed to some weird thing called virtual machine bytecode – which will eventually going to be executed by Virtual Machine.

Android Runtime uses two virtual machines – Dalvik and pretty new ART (available since Android 4.4, standard since Android Lolipop – 5.0). Both of those virtual machines execute Dalvik instructions stored in .dex files. The problem with Dalvik instruction set is: they are capable of referencing at most 65536 methods (every method gets its unique id from to 0 to 65536 – id has 16 bytes size)

…you might ask – so what? 65536 is pretty big number, I will never reach that!

Well in theory – yes. In practice – no:)  Every library you add, every .dll you reference adds methods/classes/and so on (remember it has to be transformed to dex file!) For instance: adding Google Play Services adds about 30000 methods to your small, tiny Android project. Almost half of available limit! Oops..

Let’s see what happens when you reach this limit.

65536 limit in practice

We will start with small sample.

  1. Go to your Tools/Options/Build and Run options (VS)
  2. Check MSBuild project build output verbosity to “Detailed” – we want to get detailed error message in case of build failure.
  3. Create clean Android project and add packages.xml file (see below).
  4. Type in Nuget Packages Manager Console Update-Package -Reinstall

<?xml version=”1.0″ encoding=”utf-8″?>
<packages>
<package id=”Bolts” version=”1.4.0″ targetFramework=”monoandroid60″ />
<package id=”HockeySDK.Xamarin” version=”4.1.0″ targetFramework=”monoandroid60″ />
<package id=”MvvmCross.Binding” version=”4.3.0″ targetFramework=”monoandroid60″ />
<package id=”MvvmCross.Core” version=”4.3.0″ targetFramework=”monoandroid60″ />
<package id=”MvvmCross.Droid.Shared” version=”4.3.0″ targetFramework=”monoandroid60″ />
<package id=”MvvmCross.Droid.Support.V4″ version=”4.3.0″ targetFramework=”monoandroid60″ />
<package id=”MvvmCross.Platform” version=”4.3.0″ targetFramework=”monoandroid60″ />
<package id=”NineOldAndroids” version=”2.4.0″ targetFramework=”monoandroid60″ />
<package id=”Xam.Plugins.Android.SlidingUpPanel” version=”3.3.0″ targetFramework=”monoandroid60″ />
<package id=”Xamarin.Android.Support.Animated.Vector.Drawable” version=”23.4.0.1″ targetFramework=”monoandroid60″ />
<package id=”Xamarin.Android.Support.CustomTabs” version=”23.4.0.1″ targetFramework=”monoandroid60″ />
<package id=”Xamarin.Android.Support.Design” version=”23.4.0.1″ targetFramework=”monoandroid60″ />
<package id=”Xamarin.Android.Support.v4″ version=”23.4.0.1″ targetFramework=”monoandroid60″ />
<package id=”Xamarin.Android.Support.v7.AppCompat” version=”23.4.0.1″ targetFramework=”monoandroid60″ />
<package id=”Xamarin.Android.Support.v7.CardView” version=”23.4.0.1″ targetFramework=”monoandroid60″ />
<package id=”Xamarin.Android.Support.v7.Palette” version=”23.4.0.1″ targetFramework=”monoandroid60″ />
<package id=”Xamarin.Android.Support.v7.Preference” version=”23.4.0.1″ targetFramework=”monoandroid60″ />
<package id=”Xamarin.Android.Support.v7.RecyclerView” version=”23.4.0.1″ targetFramework=”monoandroid60″ />
<package id=”Xamarin.Android.Support.Vector.Drawable” version=”23.4.0.1″ targetFramework=”monoandroid60″ />
<package id=”Xamarin.Azure.NotificationHubs.Android” version=”0.4.0″ targetFramework=”monoandroid60″ />
<package id=”Xamarin.Bindings.AdvancedRecyclerView” version=”0.86.0″ targetFramework=”monoandroid60″ />
<package id=”Xamarin.Bindings.CircleImageView” version=”2.1.0″ targetFramework=”monoandroid60″ />
<package id=”Xamarin.Bindings.MaterialDateTimePicker” version=”2.5.0″ targetFramework=”monoandroid60″ />
<package id=”Xamarin.Bindings.UniversalImageLoader” version=”1.0.4″ targetFramework=”monoandroid60″ />
<package id=”Xamarin.Facebook.Android” version=”4.13.2″ targetFramework=”monoandroid60″ />
<package id=”Xamarin.GooglePlayServices.Ads” version=”29.0.0.2″ targetFramework=”monoandroid60″ />
<package id=”Xamarin.GooglePlayServices.Base” version=”29.0.0.2″ targetFramework=”monoandroid60″ />
<package id=”Xamarin.GooglePlayServices.Basement” version=”29.0.0.2″ targetFramework=”monoandroid60″ />
<package id=”Xamarin.GooglePlayServices.Location” version=”29.0.0.2″ targetFramework=”monoandroid60″ />
<package id=”Xamarin.GooglePlayServices.Maps” version=”29.0.0.2″ targetFramework=”monoandroid60″ />
</packages>

Make sure you have Java Max Heap Size set to 1G in your Android project options. Without that Java process used during app compilation will crash due to OutOfMemoryException 😉

If you try to build this project you will get:

 trouble writing output: Too many field references: 65723; max is 65536.

C:\Program Files (x86)\MSBuild\Xamarin\Android\Xamarin.Android.Common.targets(2056,3): error MSB6006: “java.exe” exited with code 2.

.. and we even haven’t written single line of code 🙂

We have Material Design libraries – either provided by Android Support Libraries or custom one, we have HockeyApp crash-log support, Geolocation, Maps, Ads support and… and we failed 🙂

MultiDex to rescue

As I have previously described – problem lies in .dex / Dalvik Instruction set limitation. MultiDex is technique of splitting your application code into multiple .dex files – each containing at most 65 KB of bytecode.

To enable MultiDexing in Xamarin.Android just go to your project settings and check “Enable Multi-Dex” (pic rel.)

blog

Now try to build project. Build error should go away, application should work 🙂

Make sure you use Android SDK Build Tools with version 23.0.3 – so you will have same reproduction sample as the one built on my computer.
To force Xamarin to use custom build tools version edit your .csproj file – add this line:

<AndroidSdkBuildToolsVersion>23.0.3</ AndroidSdkBuildToolsVersion>

Inside your Configuration PropertyGroup.

Too easy to be true?

Sample app with packages config presented above works fine on all API levels. However change packages.xml to this:

<?xml version=”1.0″ encoding=”utf-8″?>
<packages>
<package id=”Bolts” version=”1.4.0″ targetFramework=”monoandroid60″ />
<package id=”HockeySDK.Xamarin” version=”4.1.0″ targetFramework=”monoandroid60″ />
<package id=”MvvmCross.Binding” version=”4.3.0″ targetFramework=”monoandroid60″ />
<package id=”MvvmCross.Core” version=”4.3.0″ targetFramework=”monoandroid60″ />
<package id=”MvvmCross.Droid.Shared” version=”4.3.0″ targetFramework=”monoandroid60″ />
<package id=”MvvmCross.Droid.Support.V4″ version=”4.3.0″ targetFramework=”monoandroid60″ />
<package id=”MvvmCross.Platform” version=”4.3.0″ targetFramework=”monoandroid60″ />
<package id=”NineOldAndroids” version=”2.4.0″ targetFramework=”monoandroid60″ />
<package id=”Xam.Plugins.Android.SlidingUpPanel” version=”3.3.0″ targetFramework=”monoandroid60″ />
<package id=”Xamarin.Android.Support.Animated.Vector.Drawable” version=”23.4.0.1″ targetFramework=”monoandroid60″ />
<package id=”Xamarin.Android.Support.CustomTabs” version=”23.4.0.1″ targetFramework=”monoandroid60″ />
<package id=”Xamarin.Android.Support.Design” version=”23.4.0.1″ targetFramework=”monoandroid60″ />
<package id=”Xamarin.Android.Support.Percent” version=”23.4.0.1″ targetFramework=”monoandroid60″ />
<package id=”Xamarin.Android.Support.v13″ version=”23.4.0.1″ targetFramework=”monoandroid60″ />
<package id=”Xamarin.Android.Support.v4″ version=”23.4.0.1″ targetFramework=”monoandroid60″ />
<package id=”Xamarin.Android.Support.v7.AppCompat” version=”23.4.0.1″ targetFramework=”monoandroid60″ />
<package id=”Xamarin.Android.Support.v7.CardView” version=”23.4.0.1″ targetFramework=”monoandroid60″ />
<package id=”Xamarin.Android.Support.v7.MediaRouter” version=”23.4.0.1″ targetFramework=”monoandroid60″ />
<package id=”Xamarin.Android.Support.v7.Palette” version=”23.4.0.1″ targetFramework=”monoandroid60″ />
<package id=”Xamarin.Android.Support.v7.Preference” version=”23.4.0.1″ targetFramework=”monoandroid60″ />
<package id=”Xamarin.Android.Support.v7.RecyclerView” version=”23.4.0.1″ targetFramework=”monoandroid60″ />
<package id=”Xamarin.Android.Support.Vector.Drawable” version=”23.4.0.1″ targetFramework=”monoandroid60″ />
<package id=”Xamarin.Azure.NotificationHubs.Android” version=”0.4.0″ targetFramework=”monoandroid60″ />
<package id=”Xamarin.Bindings.AdvancedRecyclerView” version=”0.86.0″ targetFramework=”monoandroid60″ />
<package id=”Xamarin.Bindings.CircleImageView” version=”2.1.0″ targetFramework=”monoandroid60″ />
<package id=”Xamarin.Bindings.MaterialDateTimePicker” version=”2.5.0″ targetFramework=”monoandroid60″ />
<package id=”Xamarin.Bindings.MaterialProgressBar” version=”1.1.7″ targetFramework=”monoandroid60″ />
<package id=”Xamarin.Bindings.Roughike.BottomBar” version=”2.0.2″ targetFramework=”monoandroid60″ />
<package id=”Xamarin.Bindings.UniversalImageLoader” version=”1.0.4″ targetFramework=”monoandroid60″ />
<package id=”Xamarin.Facebook.Android” version=”4.13.2″ targetFramework=”monoandroid60″ />
<package id=”Xamarin.GooglePlayServices.Ads” version=”29.0.0.2″ targetFramework=”monoandroid60″ />
<package id=”Xamarin.GooglePlayServices.AppInvite” version=”29.0.0.2″ targetFramework=”monoandroid60″ />
<package id=”Xamarin.GooglePlayServices.Base” version=”29.0.0.2″ targetFramework=”monoandroid60″ />
<package id=”Xamarin.GooglePlayServices.Basement” version=”29.0.0.2″ targetFramework=”monoandroid60″ />
<package id=”Xamarin.GooglePlayServices.Gcm” version=”29.0.0.2″ targetFramework=”monoandroid60″ />
<package id=”Xamarin.GooglePlayServices.Identity” version=”29.0.0.2″ targetFramework=”monoandroid60″ />
<package id=”Xamarin.GooglePlayServices.Location” version=”29.0.0.2″ targetFramework=”monoandroid60″ />
<package id=”Xamarin.GooglePlayServices.Maps” version=”29.0.0.2″ targetFramework=”monoandroid60″ />
<package id=”Xamarin.GooglePlayServices.Measurement” version=”29.0.0.2″ targetFramework=”monoandroid60″ />
<package id=”Xamarin.GooglePlayServices.Wearable” version=”29.0.0.2″ targetFramework=”monoandroid60″ />
</packages>

and reinstall packages once again.

The next step is to add custom Application class:

[Application]
[Register(“com.app.CustomApp”)]
public class CustomApp :  Application
{
protected CustomApp(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
{
}

public CustomApp()
{
}

public override void OnCreate()
{
base.OnCreate();

new CustomClass();
}

class CustomClass
{

}
}

 

Lets run our new sample application on device/emulator with Android 5.0 or higher.
Works as expected – app does nothing 🙂

Try that on a bit older device with Android less than 5.0 – I run it on Jellybean 4.2.
It’s crashing. By examining logcat logs we can see it crashed with NoClassDefFoundException, pic:

2.png

 Why application crash on API less than 21?

As I have previously mentioned MultiDex technique rely on splitting your Application code into multiple .dex files. To explain why it has crashed on API lower than 21 you first need to understand how Android handles .dex files on different API levels – from Android documentation:

Android 5.0 (API level 21) and higher uses a runtime called ART which natively supports loading multiple dex files from application APK files. ART performs pre-compilation at application install time which scans for classes(..N).dex files and compiles them into a single .oat file for execution by the Android device. For more information on the Android 5.0 runtime, see Introducing ART.

Versions of the platform prior to Android 5.0 (API level 21) use the Dalvik runtime for executing app code. By default, Dalvik limits apps to a single classes.dex bytecode file per APK. In order to get around this limitation, you can use the multidex support library, which becomes part of the primary DEX file of your app and then manages access to the additional DEX files and the code they contain.

Problem is: some important code related to MutliDex/Application initialization can accidentally land in secondary .dex file – therefore we will see exception similar to the one shown on picture from previous paragraph (cause code from secondary dex can’t be loaded on app Startup!).
The reason why we have added loads of libraries and custom application class is to “manually” provoke moving Application initialization classes to secondary dex file (this step usually is done automatically – by your in-app source code 🙂 ).

Sometimes your application might even work fine on API less than 21 but it can crash when it’s loaded by some kind of Service (like application launched by push notification handler service) – code needed to launch app by push notification can be moved to secondary dex file (real world example I had in past)!

Can we fix that, please?

In ideal world it should work automatically – however… As every software Android SDK Build tools is not bug free. During build Android SDK tools automatically detects what is important and what should be kept in first .dex file. It does not always work perfectly though 🙂

Android has a concept of some kind of “MultiDex.keep that classes in first dex” file. You add class names you want to preserve in text file and they won’t be moved to secondary dex file.

Xamarin has not always supported this feature – but after my feature request (https://bugzilla.xamarin.com/show_bug.cgi?id=35491) they have added Build action called “MultiDexMainDexList”

We just need to find out which classes we need to preserve… ugh sounds difficult?

The fastest and most reliable way to do that is by examining and decompiling .dex byte code.
Download two tools:

  1. https://github.com/pxb1988/dex2jar – convert .dex bytecode to jar
  2. https://github.com/java-decompiler/jd-gui/releases – decompile and browse jar

Steps to examine dex file:

  1. Go to your %Project Root Folder%/obj/Debug/android/bin, copy classes2.dex file to your dex2jar tool folder.
  2. Type in command line: dex2jar classes2.dex -o classes2Jar.jarclasses2jar
  3. Launch jd-gui tool and open generated classes2jar.jarjd gui.png

The missing class is mono/MonoPackageManager.class, lets use this information.

Add new text file to project (ex. multidex.keep), set it’s build action to “MutliDexMainDexList”:

mono/android/app/ApplicationRegistration.class
mono/android/app/NotifyTimeZoneChanges.class
mono/MonoRuntimeProvider.class
mono/MonoPackageManager.class
mono/MonoPackageManager_Resources.class

I have added classes from /android/app/ as well – as it looks that they might be used during app initialization too.

Rebuild app – it should work now:)

Summary

I have described you how to pass 65kB limit and how MultiDex process works – if you use it, make sure you completely test your app on API less than 21. We have to be careful because MultiDex has it’s pitfalls.

Before going with MultiDexing – try to decrease application size using Proguard or Xamarin Linker. MultiDex can increase Startup Time up to 15% on Dalvik-based Android devices (due to secondary dex file loading on startup)

I will probably write comprehensive guide of Advanced Techniques of reducing Xamarin application size and improving startup times in next month 😉

See ya!