Monday, 15 June 2015

Android + Gradle: Build different APKs with different dependencies

Introduction
Recently, I switched my projects, to Android Studio.
Two years ago when Google announced the new IDE, I was skeptic: android studio was buggy and gradle a new complicated tool to learn.
Today I'm an happy user of android studio.

In this tutorial, I will talk about my experience with gradle and some useful customization I made on the build process of my app, called Qoffee.

The problem
Qoffee is available on Play Store and Amazon App Store and allows users to find the best coffee in town.
One of the last features I added, it was the support for Android Wear: now it is possible to search coffees from the smartwatch!

With the new features:
- the new apk is passed from 1.2 mb to 3.9 mb
- the new apk contains also the apk to install into the smartwatch
- there is a new Service, precisely a WearableListenerService. This service is the one that talks with the watch.

New classes/features were not useful on the apk for other app store (eg. amazon).
In the new solution, it is now possible to automatically build 2 separate apk:
- one for play store: with all the features (size: 3.9 mb)
- one for other store: without android wear and google play services support (size: 1.2 mb)

Remove unused files: Separate the Code and merge manifests
I searched a way to create 2 different builds for my app, in order to create a custom Manifest and to remove the WearableListenerService from the non-play store apk.
In the src folder of my project on android studio I created 2 new “Java Folder”:
- playstore
- genericstore

Create a new Java Folder


New project tree


The original folder for my project was called “main”.
I created 2 other AndroidManifest.xml files, and placed them in the new folder playstore and genericstore like this:
- AndroidManifest.xml in main folder contains common data for both apk: eg. all the common activities
- AndroidManifest.xml in playstore folder contains only informations specific to the play store apk: eg. reference to the WearableServices class, keys for playstore services
- AndroidManifest.xml in genericstore folder contains information for non-playstore apk: mainly, empty

See the differences in the following image:

Differences between Manifest

After this operation I created two product flavors in the gradle file of my app like this:

android {
     compileSdkVersion 20
     buildToolsVersion '20.0.0'
     productFlavors {
          playstore{
               applicationId "com.andrea.degaetano.coffelover.playstore"
          }
          genericstore{
               applicationId "com.andrea.degaetano.coffelover.genericstore"
          }
     }
… … (other unchanged configurations here..)
}

The new build.gradle file generated 4 new “Build Variants” in Android Studio.
This new menu allows the developer to select which kind of build generate:

All my modules.. and the selected variant
you can change the selected variant on any module

At this point, if you try to export the apks, the genericstore apk fails, because the android wear apk is present in the genericstore apk and the package of the genericstore is different from the one in the android wear apk.
Not only, but the genericstore apk is always 3.9mb.

In order to remove the dependency from android wear in the generic apk, I have changed the dependencies of my application from:

dependencies {
     compile project(':parseLoginUI')
     compile 'com.android.support:support-v4:20.0.0'
     compile 'com.google.android.gms:play-services-wearable:7.5.0'
     compile 'com.android.support:appcompat-v7:20.0.0'
     compile files('libs/android-async-http-1.4.6.jar')
     WearApp project(':qoffeewear')
}

to

dependencies {
     compile project(':parseLoginUI')
     compile 'com.android.support:support-v4:20.0.0'
     playstoreCompile 'com.google.android.gms:play-services-wearable:7.5.0'
     compile 'com.android.support:appcompat-v7:20.0.0'
     compile files('libs/android-async-http-1.4.6.jar')
     playstoreWearApp project(':qoffeewear')
}

Important: The package of the android wear app is the same of the playstore variant: com.andrea.degaetano.coffelover.playstore