Android: Gradle First Look


Introduction

Gradle favors convention over configuration. It means, Gradle provides default values for settings and properties. This makes Gradle very easy to get started with. However, if you would like to change/override Gradle default settings and properties, you can do that easily.

Gradle uses Groovy DSL (Domain Specific Language) as it’s configuration lanugage. Groovy is a dynamic language for the Java Virtual Machine (JVM).

Gradle supports three different kinds of repositories.

  1. Maven
  2. Ivy
  3. Static files (mostly *.jar or .aar)

Dependencies are fetched from repositories during the execution phase. Gradle keeps a local cache, so a particular dependency will be downloaded for once only.

Gradle also allows you to build other than Java projects. If you want to manage your JS/C++ projects you can do that using Gradle.

We only need to say what we need, not how to achieve it and Gradle will make it happen. For example, we can add a single line to our build file and Gradle will download the dependency from a remote repository and also sub dependencies and it will make sure all the classes are available to our project.

Installation

Download Gradle from, https://gradle.org/gradle-download/
Make sure to add the Gradle/bin path to your Environment variable.

In MacOS, if you have homebrew installed, then you can run the following command,

~$ brew update
~$ brew install gradle

First task

Let’s create our first task in gradle.

task firstTask {
	println "Hello Gradle"
}

Now we will run our first task.

~$ gradle firstTask
Starting a Gradle Daemon (subsequent builds will be faster)
Hello Gradle
:firstTask UP-TO-DATE

BUILD SUCCESSFUL

Total time: 3.838 secs

All available tasks

~$ gradle tasks
Hello Gradle
:tasks

------------------------------------------------------------
All tasks runnable from root project
------------------------------------------------------------

Build Setup tasks
-----------------
init - Initializes a new Gradle build. [incubating]
wrapper - Generates Gradle wrapper files. [incubating]

Help tasks
----------
buildEnvironment - Displays all buildscript dependencies declared in root project 'Temp'.
components - Displays the components produced by root project 'Temp'. [incubating]
dependencies - Displays all dependencies declared in root project 'Temp'.
dependencyInsight - Displays the insight into a specific dependency in root project 'Temp'.
help - Displays a help message.
model - Displays the configuration model of root project 'Temp'. [incubating]
projects - Displays the sub-projects of root project 'Temp'.
properties - Displays the properties of root project 'Temp'.
tasks - Displays the tasks runnable from root project 'Temp'.

Other tasks
-----------
firstTask

To see all tasks and more detail, run gradle tasks --all

To see more detail about a task, run gradle help --task <task>

BUILD SUCCESSFUL

Total time: 1.008 secs

Build a simple Java project

  1. Create this project structure, /SimpleJava/src/main/java/com/genericslab/simplejava
  2. Create a Java file inside simplejava folder
  3. Create a build.gradle file inside SimpleJava fodler, parallel with src folder.
package com.genericslab.simplejava;

public class Main {
	public static void main (String... args) {
		System.out.println("Hello Java!!!");
	}
}
apply plugin: 'java' 

After this, run the ~ $ gradle tasks command again and see the avialable tasks.

~$ gradle tasks
:tasks

------------------------------------------------------------
All tasks runnable from root project
------------------------------------------------------------

Build tasks
-----------
assemble - Assembles the outputs of this project.
build - Assembles and tests this project.
buildDependents - Assembles and tests this project and all projects that depend on it.
buildNeeded - Assembles and tests this project and all projects it depends on.
classes - Assembles main classes.
clean - Deletes the build directory.
jar - Assembles a jar archive containing the main classes.
testClasses - Assembles test classes.

Build Setup tasks
-----------------
init - Initializes a new Gradle build. [incubating]
wrapper - Generates Gradle wrapper files. [incubating]

Documentation tasks
-------------------
javadoc - Generates Javadoc API documentation for the main source code.

Help tasks
----------
buildEnvironment - Displays all buildscript dependencies declared in root project 'SimpleJava'.
components - Displays the components produced by root project 'SimpleJava'. [incubating]
dependencies - Displays all dependencies declared in root project 'SimpleJava'.
dependencyInsight - Displays the insight into a specific dependency in root project 'SimpleJava'.
help - Displays a help message.
model - Displays the configuration model of root project 'SimpleJava'. [incubating]
projects - Displays the sub-projects of root project 'SimpleJava'.
properties - Displays the properties of root project 'SimpleJava'.
tasks - Displays the tasks runnable from root project 'SimpleJava'.

Verification tasks
------------------
check - Runs all checks.
test - Runs the unit tests.

Rules
-----
Pattern: clean<TaskName>: Cleans the output files of a task.
Pattern: build<ConfigurationName>: Assembles the artifacts of a configuration.
Pattern: upload<ConfigurationName>: Assembles and uploads the artifacts belonging to a configuration.

To see all tasks and more detail, run gradle tasks --all

To see more detail about a task, run gradle help --task <task>

BUILD SUCCESSFUL

Total time: 1.657 secs

As you can see, we have got a number of additional Build Tasks because we had added java plugins.
Let’s run ~ $ gradle build

~$ gradle build
:compileJava
:processResources UP-TO-DATE
:classes
:jar
:assemble
:compileTestJava UP-TO-DATE
:processTestResources UP-TO-DATE
:testClasses UP-TO-DATE
:test UP-TO-DATE
:check UP-TO-DATE
:build

BUILD SUCCESSFUL

Total time: 1.448 secs

A build directory has been generated. You can find a Main.class file in the following directory,
~SimpleJava/build/classes/main/com/genericslab/simplejava

Let’s run it.

~$ java -cp build/classes/main/ com.genericslab.simplejava.Main
Hello Java!!!

Run build with a specific version of Gradle (Gradle Wrapper)

You need to have Gradle already installed.

apply plugin: 'java'

task wrapper(type: Wrapper) {
	gradleVersion = '2.6'
}
~$ gradle wrapper

After running the wrapper task, you will see a folder named gradle and a script file named gradlew and a .bat file named gradlew.bat

From now on, you need to use gradlew instead of gradle to use the particular gradle version 2.6. Any further command started with gradlew will first install the Gradle version 2.6 (if not installed already).

If you have built an application that need to use a particular Gradle version, or you might want to run the application without any hassel in other machine then it is a preferable way to use a wrapper.

Tasks

Task is the unit that Gradle executes. Each task has a lifecycle and may contain properties. Task can also contain dependencies of other tasks.

Let’s create a task

// project level task 
project.task "TaskA"


// in theory, it is a local level task 
// however, practically it is same as before 
// because we are now in project context 
task "TaskB"

// We can add description of a task 
TaskB.description = "This is a description for TaskB"

// Action of a task 
TaskA {
	doLast {
		println "Action#1 for TaskA"
	}
}

// We can define the task and define action together
task "TaskC" {
	doLast {
		println "Action#1 for TaskC"
	}
}

// If we define multiple actions inside a task, 
// actions will be appended
TaskA {
	doLast {
		println "Action#2 for TaskA"
	}
}

// define, description and actions all in one 
task "TaskD" {
	description "This is a description for TaskD"
	doLast {
		println "Action#1 for TaskD"
	}
}

Run ~$ gradle tasks in command line.

~$ gradle tasks
:tasks

------------------------------------------------------------
All tasks runnable from root project
------------------------------------------------------------

Build Setup tasks
-----------------
init - Initializes a new Gradle build. [incubating]
wrapper - Generates Gradle wrapper files. [incubating]

Help tasks
----------
buildEnvironment - Displays all buildscript dependencies declared in root project 'Temp'.
components - Displays the components produced by root project 'Temp'. [incubating]
dependencies - Displays all dependencies declared in root project 'Temp'.
dependencyInsight - Displays the insight into a specific dependency in root project 'Temp'.
help - Displays a help message.
model - Displays the configuration model of root project 'Temp'. [incubating]
projects - Displays the sub-projects of root project 'Temp'.
properties - Displays the properties of root project 'Temp'.
tasks - Displays the tasks runnable from root project 'Temp'.

Other tasks
-----------
TaskA
TaskB - This is a description for TaskB
TaskC
TaskD - This is a description for TaskD

To see all tasks and more detail, run gradle tasks --all

To see more detail about a task, run gradle help --task <task>

BUILD SUCCESSFUL

Total time: 0.852 secs

Task Dependencies

// define, description and actions all in one 
task "TaskD" {
	description "This is a description for TaskD"
	doLast {
		println "Action#1 for TaskD"
	}
}

// TaskA is dependent on TaskC
// means, TaskC will auto executed when we run TaskA 
TaskA.dependsOn TaskC 

// one task must execute after another task 
TaskB .mustRunAfter TaskA 

// one task should execute after another task 
// avoids circular dependencies 
TaskB.shouldRunAfter TaskA, TaskB 

/*
Remember, mustRunAfter and shouldRunAfer will not kick in 
automatically. If we run both tasks together, or, the tasks
have dependencies on one another, only then those two 
keywords will be meaningful. 

What if we would like to run a task after another task, 
even we don't call it separately, or don't have dependencies.
Similar to final block, we can use finalizedBy 
*/

// might be very usefule for db migration 
// after the migration you might want to run a task for cleanup 
TaskB.finalizedBy TaskC 

Run ~$ gradle TaskA

~$ gradle TaskA
:TaskC
Action#1 for TaskC
:TaskA
Action#1 for TaskA
Action#2 for TaskA

BUILD SUCCESSFUL

Total time: 0.908 secs

Define properties

 
def buildName = "Awesome-20160902a"
println "Build name: $buildName"

// global scope
project.ext.versionName = "Awesome=20160902b"
println "Version name: $project.ext.versionName"


<h3>Use built-in task: Copy</h3>


task copyFiles (type: Copy) {
	exclude 'file1.txt', 'file2.txt' // do not copy these files
	from 'src'  // define the source folder 
	from 'dest' // define the destiantion folder 
}

// this can be re-written to 

def spec = copySpec {
	exclude 'file1.txt', 'file2.txt' // do not copy these files
	from 'src'
}

task copyFilesAgain (type: Copy) {
	with spec 
	into 'dest' 
}

// another spec variable 
def spec = copySpec {
	exclude {
		// iterator: it 
		// exclude all pngs 
		it.file.name.endsWith("png")
	}
	from 'src'
}

Sourceset

Gradle Android projects follows this folder structure:

src/main/java
src/main/res
src/main/jniLibs
src/androidTest/java
src/test/java

if you do not like to change your current folder structure then you can tell Gradle where to look for specific files

android {
    sourceSets {
        main {
            manifest.srcFile 'AndroidManifest.xml'
            java.srcDirs = ['src']
            resources.srcDirs = ['src']
            aidl.srcDirs = ['src']
            renderscript.srcDirs = ['src']
            res.srcDirs = ['res']
            assets.srcDirs = ['assets']
        }

        androidTest.setRoot('tests')
    }
}

Gradle daemon

Everytime we run ~$ gradle build, Gradle launches JVM and starts from the beginning. Instead if Gradle could run as a service and could reuse already runned JVM then it would have been great!

Voila! Gradle can do that and it's called daemon.

Run ~$ gradle --daemon build and you might be prompted to give a permission and gradle daemon will be started.

If you would like to run --daemon all the time, instead of per command basis then write the following code,

or.gradle.daemon=true

in your project's gradle.properties file. That will ensure daemon will run on your project.

If you would like run daemon irrespective of projects, that means for all projects, then add the line in your ~/.gradle/gradle.properties file.

Android: Instrumentation Testing using Espresso


Introduction

Android provides a number of extra components beyond Java, for example, Activity, Service etc. Testing these extra components you will be required Instrumentation testing and it needs to be run on device/emulators. Espresso is an official instrumentation testing framework by Google.

Installation

Add this line in app level build.gradle in dependencies

androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.1'

add this line inside defaultConfig

testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

For more information,

  1. Android dev
  2. Google github

Project Structure

An androidTest folder should automatically be generated when you created the Android application. For example,

In Android view,

Screenshot at Sep 15 11-44-37.png

Or, in Project view,

Screenshot at Sep 15 11-46-31.png

If not,

  1. Create a folder named androidTest inside src
  2. Create a folder named java inside androidTest

Now we will create our first test class.

  1. Go to the MainActivity.java class and click on MainActivity class name
  2. Select Navigate (from menu)
  3. Select Test (Shift + Cmd + T)
  4. Select, Create New Test…

Select JUnit4 and the dialog should look like this,

Screenshot at Sep 15 11-56-52.png

Click OK.

Check the correct directory is selected. We need to select androidTest directory,

Screenshot at Sep 15 11-57-34.png

Click OK.

Now Sync Gradle.

If you get the following error,

Error:Conflict with dependency 'com.android.support:support-annotations'. Resolved versions for app (24.2.1) and test app (23.0.1) differ. See http://g.co/androidstudio/app-test-app-conflict for details.

The add the following line in dependencies

androidTestCompile 'com.android.support:support-annotations:24.2.1'

Note that the above line version number need to match with the expected version number indicated with the error.

Now let’s go to the newly created MainActivityTest.java


package com.genericslab.espressodemo;

import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;

import org.junit.Rule;
import org.junit.runner.RunWith;

@RunWith(AndroidJUnit4.class)
public class MainActivityTest {

    // This will launch the MainActivity before testing each method/case.
    @Rule
    public ActivityTestRule<MainActivity> activityTestRule = new ActivityTestRule<>(MainActivity.class);

}

Now we will write our first test case

@RunWith(AndroidJUnit4.class)
public class MainActivityTest {

    // This will launch the MainActivity before testing each method/case.
    @Rule
    public ActivityTestRule<MainActivity> activityTestRule = new ActivityTestRule<>(MainActivity.class);

    @Test
    public void testCase1() {
        // Find a view with id = R.id.text
        // Check the text of that view matches with string = R.string.app_name
        onView(withId(R.id.text)).check(matches(withText(R.string.app_name)));
    }
}

Now Right click on the file MainActivityTest.java from project explorer and select “Create MainActivityTest…” -> OK.
Click on Run.

Now we will add our second test case

@Test
public void testCase2() {

    // Open options menu, whether as overflow or using a physical button
    // This will open the option menu as a pop up menu. Next we will click on an item.
    // If you need context then pass, InstrumentationRegistry.getTargetContext()
    openActionBarOverflowOrOptionsMenu(InstrumentationRegistry.getTargetContext());

    // From the options menu, we are going to click on an item having text = R.string.label_help
    onView(withText(R.string.label_help)).perform(click());

    // alternatively, you can leverage id property.
    onView(withId(R.id.menu_help)).perform(click());

    // verifying
    onView(withId(R.id.text)).check(matches(withText(R.string.label_updated)));
}

Android: SharedPreferences


There are several data storage options in Android. Such as,

  1. Shared Preferences
    • Stores primitive data types (int, String, float, boolean)
    • Stores as key-value pair
    • Not accessible to other applications
  2. Internal Storage
    • Stores data inside apps private storage
    • Not accessible to other applications
  3. External Storage
    • Stores data on shared external storage (SD Card)
    • Accessible to other applications/user
  4. SQLite Database
    • Stores structured data in apps private storage
    • Not accessible to other applications in general, but can be shared using ContentProviders.
  5. Network
    • Stores data in your own/company server
    • You need an active network connection for accessing/storing the data

We have two options to get the SharedPreferences object,

  1. getPreferences(int mode)
  2. getSharedPreferences(String name, int mode)

There is only one valid value for int mode and that is Context.MODE_PRIVATE

Previously, there were other valid values, such as Context.MODE_WORLD_READABLE and Context.MODE_WORLD_WRITEABLE, but these values got deprecated in API 17 and will throw a SecurityException from Android N.

If we use Context.MODE_PRIVATE then it means, no other apps will be allowed to access our shared preference values.

getPreferences

If we use getPreferences(int mode) then an XML file will be created by name of the Activity and it’s access level will be within the Activity. Separate XML files will be created for each Activity.

getSharedPreferences

If we use getSharedPreferences(String name, int mode) then an XML file will be created by name of String name and it’s access level will be within the Application (i.e any Activity can access the values). If you use a different name then a different XML by that name will be created.

Eaxmple#1

private void saveDataInActivitySharedPreferences() {
    SharedPreferences sharedPrefs = getPreferences(Context.MODE_PRIVATE);
    SharedPreferences.Editor editor = sharedPrefs.edit();

    editor.putString("key_string", "value_string");
    editor.putBoolean("key_boolean", true);
    editor.putInt("key_int", 5);

    editor.apply();
    // apply will not return any value and work asynchronously

    // or you could call, editor.commit();
    // commit returns boolean based on un/successful operation and works synchronously
}

private void loadDataFromActivitySharedPreferences() {
    SharedPreferences sharedPrefs = getPreferences(Context.MODE_PRIVATE);

    String valueString = sharedPrefs.getString("key_string", "Default value");
    boolean valueBoolean = sharedPrefs.getBoolean("key_boolean", false);
    int valueInt = sharedPrefs.getInt("key_int", 0);

    String logMsg = valueString + " " + valueBoolean + " " + valueInt;

    Log.d("DEBUG_TAG", logMsg);
}

You can see the XML file content by selecting Tools -> Android -> Android Device Monitor -> (Tab) File Explorer -> data -> data -> APP_PACKAGE_NAME -> shared_prefs -> ACTIVITY_NAME.xml

File content should like below,

<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
    <string name="key_string">value_string</string>
    <boolean name="key_boolean" value="true" />
    <int name="key_int" value="5" />
</map>

If you tap on CLEAR DATA from Settings then this fill will be deleted. DB and Cache files will be deleted as well.

We can not load the values from another Activity because the created file has Activity level access. If we want to access the values from another Activity then we need to call getSharedPreferences instead of getPreferences. Remember that, in case of getSharedPreferences too we have only one value value for int mode = Context.MODE_PRIVATE

Example#2

editor.remove("key_string");
// the key and the associated value will be deleted.
editor.apply();
// do not forget to call apply() method.

editor.clear();
// clear all the data, but the XML file will not be deleted.
editor.apply();
// do not forget to call apply() method.

Demo contacts for Android emulator/genymotion


When we create an emulator or genymotion device, it is kind of blank. What if we need a list of contacts to experiment with. In that case we have two options

  1. We can use our PERSONAL device. urgh!
  2. We can insert a bunch of demo contacts into emulator/ genymotion.

Cons

What if we accidentally delete/call/send text to our personal contacts? shoot!

Or, we might need to discard the emulator/genymotion and create a new one. In that case all our demo data will be gone!

Besides, our demo data will always look like a DEMO data. I mean, anybody can take a look at our contacts list and can identify the data are dummy.

Solution

I have created some dummy data (which would look like real data to most of sane human beings, hopefully) and exported the contacts into a .vcf file format. Anybody can transfer the file into your emulator/genymotion and import the file from contacts app.

And voila! you will get a bunch of contacts (52 contacts as of now). I have also post the file in github repository and of course you will get the latest data from there.

Github repo: github.com/tausiq/demo-contacts-for-Android

Contacts.vcf: https://github.com/tausiq/demo-contacts-for-Android/blob/master/contacts.vcf

 

 

screenshot_1488355668

screenshot_1488355704

Data source: http://www.fakenamegenerator.com and http://pngimg.com

Android Library: Gson (Usage and Example)


Serialization

Serialization is a process of writing the state of an object into a byte stream. Similarly, deserialization is the process of turning the byte stream into an object.

Installation

You can get the latest version of Gson library from here.
Right now the latest version is 2.7. Click on the version from the link and you will get the instruction to add the library for your favorite build system.

For Gradle, I would use,

compile group: 'com.google.code.gson', name: 'gson', version: '2.7'

this can be simplified to,

compile "com.google.code.gson:gson:2.7"

Example#1

We have a class named User

public class User {

    private String firstName;

    private String lastName;

    private int age;

    private List<String> emails;

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public List<String> getEmails() {
        return emails;
    }

    public void setEmails(List<String> emails) {
        this.emails = emails;
    }
}

Now let’s create an object of class User and convert the object to String and after that we will create an User object from the String.


User user = new User();
user.setFirstName("Android");
user.setLastName("Studio");
user.setAge(3);
user.setEmails(Arrays.asList("android.studio@gmail.com", "android.studio@android.com"));

// converting the User object to string
Gson gson = new Gson();
String jsonStringUser = gson.toJson(user, User.class);

// Creating the User object from string
User returnedUser = gson.fromJson(jsonStringUser, User.class);

Example#2

Now, suppose, we have a generic class.

public class Employee<T> {

	private T person;

	public T getPerson() {
		return person;
	}

	public void setPerson(T person) {
		this.person = person;
	}
}

Now let’s create an object of class Employee and convert the object to String and after that we will create an Employee object from the String.

User user = new User();
user.setFirstName("Android");
user.setLastName("Studio");
user.setAge(3);
user.setEmails(Arrays.asList("android.studio@gmail.com", "android.studio@android.com"));

Employee<User> employee = new Employee<>();
employee.setPerson(user);

Gson gson = new Gson();

// This will not work
String jsonString = gson.toJson(employee, Employee.class);

We could have put any class object inside Employee. There is no way the JVM will know the object type until runtime. There is a concept of Java type erasure that will explain why the simple Employee.class will not work with Gson. To resolve the issue we need to get the type at runtime using TypeToken

User user = new User();
user.setFirstName("Android");
user.setLastName("Studio");
user.setAge(3);
user.setEmails(Arrays.asList("android.studio@gmail.com", "android.studio@android.com"));

Employee<User> employee = new Employee<>();
employee.setPerson(user);

Gson gson = new Gson();

Type type = new TypeToken<Employee<User>>() {
}.getType();

String jsonString = gson.toJson(employee, type);

// load the value
Employee<User> returnedEmployee = gson.fromJson(jsonString, type);
User returnedUser = returnedEmployee.getPerson();

Android: Speed up Gradle build process in Android Studio


These techniques will help you to make the Gradle build process faster.

Technique #1

  1. Open up gradle.properties file
  2. add the following line

org.gradle.daemon=true

Side effects

  1. Gradle daemon is a background process. If you use this, it might take (300-400) MB memory or more from your system.
  2. After adding the above line, try to Rebuild the project. If you get error, something like, Unable to locate a Java Runtime to invoke, very obvious solution, remove the line.

Technique #2

  1. Open up Android Studio Settings (PC/Linux) or Preference (Mac)
  2. check Compile independent modules in parallel

prefs_as

Alternatively,

  1. Open up gradle.properties file
  2. add the following line

org.gradle.parallel=true

Technique #3

  1. Open up Android Studio Settings (PC/Linux) or Preference (Mac)
  2. check Configure on demand

prefs_as2

Alternatively,

  1. Open up gradle.properties file
  2. add the following line

org.gradle.configureondemand=true

Technique #4

  1. Open up gradle.properties file
  2. add the following line

org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8

Side effects

The above line means, you are allowing Java compilers to have available memory up to 2 GB (2048 MB). If you do not have available memory to give out 2 GB to Java then you should not add this line.

Another way to say this, If you have enough memory then you can try to tweak the settings.

Technique #5

All the above changes to gradle.properties file can be configures globally instead of changing the settings for every individual project.

Modify or create a file named gradle.properties in the following directory.

For Mac,
/Users/USER_NAME/.gradle/gradle.properties

For Linux,
/home/USER_NAME/.gradle/gradle.properties

For Windows,
C:\Users\USER_NAME\.gradle\gradle.properties

and apply the above changes. Finally it should look like,


org.gradle.daemon=true

# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
org.gradle.parallel=true

org.gradle.configureondemand=true

# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx10248m -XX:MaxPermSize=256m
org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8

Technique #6

  1. Open up Android Studio Settings (PC/Linux) or Preference (Mac)
  2. check Compile independent modules in parallel

prefs_as3

Side effects

Apply this option after all of your libraries are downloaded and configured correctly. Gradle try to resolve the dependencies from internet, every time we build our project. This option will force the gradle to resolve the dependencies from cache. You may need to uncheck the option if you add/modify a library.

Technique #7

If you are using google play services, then try to use only the library you need. For example, if you need maps, then use

compile 'com.google.android.gms:play-services-maps:7.5.0'

instead of:

compile 'com.google.android.gms:play-services:7.5.0'

Technique #8

Consider using separate flavors for development and production.

android {
    productFlavors {
        // Define separate dev and prod product flavors.
        dev {
            // dev utilizes minSDKVersion = 21 to allow the Android gradle plugin
            // to pre-dex each module and produce an APK that can be tested on
            // Android Lollipop without time consuming dex merging processes.
            minSdkVersion 21
        }
        prod {
            // The actual minSdkVersion for the application.
            minSdkVersion 14
        }
    }
          ...
    buildTypes {
        release {
            runProguard true
            proguardFiles getDefaultProguardFile('proguard-android.txt'),
                                                 'proguard-rules.pro'
        }
    }
}
dependencies {
  compile 'com.android.support:multidex:1.0.0'
}

for further details,

https://developer.android.com/tools/building/multidex.html#dev-build

Technique #9

Try to use Gradle 2.4, it has a huge performance boost over previous versions.

  1. Open up Android Studio Project Structure option. File -> Project Structure
  2. Write value 2.4 as Gradle Version

prefs_as4

Technique #10

  1. Open up app level build.gradle file. (Inside app directory)
  2. add the following line

dexOptions {
incremental true
}

android {
    compileSdkVersion 22
    buildToolsVersion 1.2.3

    defaultConfig {
        applicationId "com.wordpress.tausiq.sampleapp"
        minSdkVersion 16
        targetSdkVersion 22
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

    dexOptions {
        incremental true
    }
}

Side effects

Enabling this could cause your builds to fail (especially on consecutive runs). If error occurs you can try to build again. Or, if it doesn’t work for you then skip it.

Technique #11

Try not to use Build -> Rebuild Project every time to check for compile errors or after a small change. Rather use, Build -> Make Project, it is much faster than previous option.

References:

Using Android Studio to increase productivity


Android Studio has been released officially. A couple of bug fix releases have already been rolled out since the first stable release. You can download the latest stable version from here.

Android Studio

There are 3 more release channels along with Stable channel. Namely,

  1. Canary channel: rough edges build
  2. Dev channel: slightly more stable than Canary
  3. Beta channel: Beta version
  4. Stable channel: Stable release

If you would like to test out Canary builds or Dev builds, then you can look at this page.

After running Android Studio (AS) you can set the default channel from which it will look for the next updates.

Set default update channel

Fire up AS.

  1. On Mac, go to Preference (cmd + ,) and on Windows go to Settings
  2. look for Updates from left pane
  3. Select channel from the drop down list on right pane
  4. Hit Apply -> OK

Dracula theme

Currently there are two themes in AS. I would suggest to use Dracula theme. It’s background is black and the syntax highlighting color combination is more soothing to look at. If you are a full-time developer then you might work on AS for long hours and Dracula theme would be a preferable choice.

  1. On Mac, go to Preference (cmd + ,) and on Windows go to Settings
  2. look for Appearance from left pane
  3. Select themes from the drop down list on right pane
  4. Hit Apply -> OK

Auto import

AS can auto import classes for you. On ambiguous occassion it would provide a dialog box for you to choose, otherwise it will done it automatically. To turn on this feature,

  1. On Mac, go to Preference (cmd + ,) and on Windows go to Settings
  2. look for Editor from left pane then Auto Import
  3. Check: Optimize imports on the fly
  4. Check: Add unambiguous imports on the fly
  5. Hit Apply -> OK

Useful shortcuts

Duplicates:
Place cursor on a code line. Hit Cmd + d. (The line will be duplicated on next line)

Go to a class:
Hit Cmd + n. Type out the name of a class and hit enter.

Go to a file:
Hit Cmd + Shift + n. Type out the name of a file and hit enter.

Rename:
place cursor on the “thing” you would like to rename. Hit Shift + F6

Quick fix:
Alt + Enter.
This shortcut also heavily used to extract hardcoded string resources from java to xml

Plugins

Following plugins can be handy for Android developers.

  • Genymotion
  • .gitignore Support

Genymotion

Genymotion is a third-party emulator made by Genymobile. It a faster and more responsive than official Android emulator. Get Genymotion from here.

.gitignore Support

Nice plugins to edit .gitingore file. Details can be found here.

Invalidated Caches / Restart

Often times when you add a jar file or library and also added the necessary code on build.gradle file and then sync the gradle file but still the jar is not added to the project then you might need this option. Go to File -> Invalidated Caches / Restart and Hit on “Invalidate and Restart” button.

Code completion

  1. On Mac, go to Preference (cmd + ,) and on Windows go to Settings
  2. look for “Editor” from left pane and then “Code Completion”
  3. Check: Parameter Info -> Autopopup in (ms): 1000 (This will display the parameter info of a method in 1 sec.
  4. Hit Apply -> OK