Friday, October 17, 2014

9b. Gradle: AndroidStudio with submodules (library projects)

In this tutorial we will overview integration basics of Android Studio and Gradle build tools.



Step 1: Verify local.properties file

## This file is automatically generated by Android Studio.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file should *NOT* be checked into Version Control Systems,
# as it contains information specific to your local configuration.
#
# Location of the SDK. This is only used by Gradle.
# For customization when using a Version Control System, please read the
# header note.
sdk.dir=/Users/uki/Documents/Dropbox/Android/android-sdk-macosx


As noted, this is for us to only verify that we have the right Android tools directory specified.

I also have $ANDROID_HOME  in my ~/.profile which serves the same purpose

# ANDROID - updated: Oct 17, 2014
export ANDROID_HOME=~/Documents/Dropbox/Android/android-sdk-macosx
export PATH=${PATH}:${ANDROID_HOME}/build-tools/21.0.0
export PATH=${PATH}:${ANDROID_HOME}/platform-tools
export PATH=${PATH}:${ANDROID_HOME}/tools

Remember to update build-tools to latest stable version, in my case Android Lollipop 5.0. API 21

If you are developing code with multiple people, verify that .gitignore file contains files that are specific only to you.

local.properties

Step 2: Gradle Project structure with library projects







Step 3: Gradle Wrapper "gradlew" permission to execute


I had to add execute (+x) permissions to my gradlew script

$ ./gradlew
-bash: ./gradlew: Permission denied
chmod +x gradlew


Run ./gradlew (.bat)

$ ./gradlew
Relying on packaging to define the extension of the main artifact has been deprecated and is scheduled to be removed in Gradle 2.0
:help
Welcome to Gradle 1.10.
To run a build, run gradlew <task> ...
To see a list of available tasks, run gradlew tasks
To see a list of command-line options, run gradlew --help

If you inspect gradle/wrapper/gradle-wrapper.properties you will notice it uses older Gradle jar:


$ more gradle/wrapper/gradle-wrapper.properties#Fri Oct 17 10:58:51 CDT 2014
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=http\://services.gradle.org/distributions/gradle-1.10-all.zip




However updating it to a newer one did not produce correct results, so I left it at original 1.10.

Step: Preventing duplicate dependencies

UNEXPECTED TOP-LEVEL EXCEPTION:
com.android.dex.DexException: Multiple dex files define Landroid/support/v7/appcompat/R$anim;

Make sure that if your Library project has:

dependencies {
    compile 'com.android.support:appcompat-v7:19.+'
...

then you main App should not have the different version of the same jar.

Also do:

Build > Clean Project

Step: before writing of  a new build script always update Android SDK



Step: Writing example app build.gradle

While writing Gradle script use Android Studio tips:



The sections listed below are in the same order as in my project/app/build.grade file:

Step: plugin Android or Java?


apply plugin: 'com.android.application'



Step: buildscript{}



// build script is not necessary unless you want to provide non-standard configuration
buildscript {
repositories {
//mavenCentral()
// even if not necessary, I am giving example of other repo in case failure of mavenCentral
//jcenter()
}
dependencies {
// This is Android plugin for Gradle, not actual Gradle 2.1
// classpath 'com.android.tools.build:gradle:0.12.+'
}
}

Step: dependencies{}


dependencies {
    /**
     *  Include all jars from the lib directory.
     */
    compile fileTree(dir: 'libs', include: ['*.jar'])
    /**
     * You must install or update the Support Repository through the SDK Manager to use this dependency.
     * v4 is needed for Theme.AppCompat.Light.DarkActionBar
     **/
    compile 'com.android.support:support-v4:21.0.0'
    /**
     * Even if NOT recommended I use 19.+ notation, also I am not using 21.+ because of errors:
     * No resource found that matches the given name 'android:TextAppearance.Material'.
     * even if my SDK Manager is up to date with API 21.
     * since support-v7 is included in the libraries project it is not needed here.
     */
    // compile 'com.android.support:appcompat-v7:21.0.0'
    compile 'com.android.support:appcompat-v7:19.+'
    /**
     * The main app depends on LIBRARY ListViewSwipeActivity
     */
    compile project(':libraries:ListViewSwipeActivity')
}

Step: repositories{}



/**
 * needed only if you have to specify non-standard configuration
 **/
repositories {
    mavenCentral()
    /**
     * even if not necessary, I am giving example of other repo in case failure of mavenCentral
     */
    //jcenter()
}


android{}

android {
    /**
     * compileSdkVersion is the same as the target property in the project.properties
     */
    compileSdkVersion 21 // Lollipop 5.0
    /**
     * Check Android SDK Manager for the NEWEST tools version:
     */
    buildToolsVersion '21.0.2' // Lollipop 5.0
    defaultConfig {
        versionCode 1
        versionName "1.0"
        /**
         * minSdkVersion should be set to 7 if you depend on support-v7.
         */
        minSdkVersion 7
        targetSdkVersion 21  // Lollipop 5.0
    }
    buildTypes {
        release {
            runProguard false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
        }
    }
    /**
     * I am using default Gradle locations,
     * but if you use Ant based projects you may have to adjust
     */
    sourceSets {
        main {
            manifest.srcFile 'src/main/AndroidManifest.xml'
            java.srcDirs = ['src/main/java']
            aidl.srcDirs = ['src/main/aidl']
            res.srcDirs = ['src/main/res']
            assets.srcDirs = ['src/main/assets']
            //renderscript.srcDirs = ['src']
            //resources.srcDirs = ['src']
        }
        /**
         * androidTest.setRoot moves the whole sourceSet (and its sub folders) to a new folder.
         * This moves src/androidTest
         */
        androidTest.setRoot('tests')
    }
}