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!

Advertisements

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!