Colophon

Android for iOS Developers: Kotlin Edition 2018

ISBN: 978-3-906926-15-5

© Copyright 2016-2018 by Adrian Kosmaczewski – All Rights Reserved.

logo

AKOSMA Training Adrian Kosmaczewski

Ringkengässchen 11 – 8200 Schaffhausen – Switzerland

This document is geared towards providing exact and reliable information in regards to the topic and issue covered. The publication is sold with the idea that the publisher is not required to render accounting, officially permitted, or otherwise, qualified services. If advice is necessary, legal or professional, a practiced individual in the profession should be ordered.

In no way is it legal to reproduce, duplicate, or transmit any part of this document in either electronic means or in printed format. Recording of this publication is strictly prohibited and any storage of this document is not allowed unless with written permission from the publisher. All rights reserved.

The information provided herein is stated to be truthful and consistent, in that any liability, in terms of inattention or otherwise, by any usage or abuse of any policies, processes, or directions contained within is the solitary and utter responsibility of the recipient reader. Under no circumstances will any legal responsibility or blame gbe held against the publisher for any reparation, damages, or monetary loss due to the information herein, either directly or indirectly.

Respective authors own all copyrights not held by the publisher.

The information herein is offered for informational purposes solely, and is universal as so. The presentation of the information is without contract or any type of guarantee assurance.

The trademarks that are used are without any consent, and the publication of the trademark is without permission or backing by the trademark owner. All trademarks and brands within this book are for clarifying purposes only and are owned by the owners themselves, not affiliated with this document. Android is a trademark of Google Inc. iOS is a trademark or registered trademark of Cisco in the U.S. and other countries and is used under license by Apple Inc. Java is a trademark of Oracle Corporation and/or its affiliates. Kotlin is a trademark of Google LLC.

The Android robot is reproduced or modified from work created and shared by Google and used according to terms described in the Creative Commons 3.0 Attribution License.

Written and published in Switzerland. Created with the eBook Template Toolchain by Adrian Kosmaczewski based on Asciidoctor and PlantUML.

Abstract

This book provides a quick introduction of Android for iOS developers using the Kotlin programming language. It targets iOS developers with medium or advanced level, having shipped some iOS applications already in either Objective-C and Swift.

Dedication

To hernún.

Preface

The world of mobile development is a ground in constant motion. However, for the past five years, Android and iOS have both reached the level of dominant players in the field, moving other platforms out of sight. Due to the complexity of these systems, developers tend to concentrate their efforts in just one platform; however businesses must target both platforms to remain competitive in the mobile market.

This book provides an iOS developer’s perspective on Android, using the Kotlin programming language, highlighting the similarities and the major differences between both platforms. The author hopes that these lines will help other developers to jump to the fascinating world of Android using their hard earned iOS knowledge.

Target Audience

This book is intended as a step-by-step guide to guide developers well versed in the arts of iOS into the realm of Android mobile application development.

How to Read this Book

The author assumes that the reader has never written Android applications before; at most, maybe, the reader has played with an Android device at some point, but nothing else. If you are already familiar with Android Studio and Kotlin, you can skip directly to chapter 2, and start creating apps right away.

If you are not familiar with the Android developer tools, it is strongly recommended to read this book linearly, and to build the sample applications one after the other. This will help you build your skills step by step.

You can use this book as reference, later during your development work, using the provided source code as a basis for your own projects. The book can work as a "cookbook" text, providing specific help about some common (and not-so-common) tasks.

Requirements

This book assumes that the reader is using a Mac – after all, the reader is supposed to be an iOS developer!

It also assumes working programming knowledge in Objective-C and Swift, and of the most common iOS frameworks, such as Foundation, UIKit, Core Location, Core Data and others. Given their similarities, the text will heavily draw from the Swift knowledge of the reader as the guiding path to learn Kotlin, highlighting differences and similarities whenever possible.

Most importantly, it is not assumed that the reader has seen any Kotlin code yet. This book provides a quick introduction to the language, even though it is not by any means a complete reference. The "Bibliography" section at the end of the book provides a few useful titles for starting your exploration of Kotlin. In particular I’d recommend JetBrains' own Kotlin Tutorials and Try Kotlin websites as an excellent starting point.

In terms of software requirements, this book assumes that the latest version of Android Studio (3.0.1 at the time of publishing) is installed in the development machine, as well as Homebrew.

Source Code

The code bundled with the book has been prepared and tested with the latest version of Android Studio; make sure to download and install it in your system before starting.

All applications use the same baseline (or minSdkVersion): API 21, also known as Lollipop 5.0. This version of Android was released in November 2014, and at the time of this writing, 80% of all Android devices in the wild run a version equal or older to Lollipop. This should hopefully give this book the wide possible reach as well as a solid foundation for the future of the platform. Similarly, all code projects target the latest Android version available at the time of publication, API 27, Oreo 8.1 (targetSdkVersion and compileSdkVersion in the module Gradle files.)

Every time that the text of the book references some sample code, a "Follow Along" callout section will appear with the path of the project, which you can open on Android Studio to run the project directly on your device or the emulator:

Follow along

The code of this section is located in the Graphics/Draw folder.

Each application is as simple as possible, but not simpler. All the applications are working examples, tested at least in four environments:

  • The official Android Emulator.

  • The Genymotion Android Emulator.

  • A OnePlus 3 Android smartphone.

  • A Samsung Galaxy Tab S2 tablet.

Given the large variety of the Android device market, it is possible that some bits and pieces of the source code will not work in some devices; I remember having trouble with some Android devices during my career, so I would not be surprised if some of you encounter difficulties. I will not be able to provide support for your particular device, but I have made every possible effort so that the code works in the environments enumerated above.

Structure

This book is structured around code. The chapters are meant to be read with Android Studio open, in the order they have been written; I have reused bits and pieces of knowledge from previous chapters in many others, so you should be better served by reading them in order.

To help readers get up and running as fast as possible, every chapter features a section called "TL;DR" at its very beginning, including a handy summary of the most important similarities and differences between Android and iOS. You can use the tables in this section as a reference, and if you find them useful you can print a copy of the Appendix C, which contains all the TL;DR tables together in the same place.

The source code included in the book points directly to the projects available in the code zip file, which contains all the sample applications showcased in the pages of this book.

Thanks

I would like to thank all the readers of the first edition of this book; your support is what made this second edition possible! In particular I would like to mention Nick Ager, Caylan Larson, Luca Torella, Patrick Balestra and Nick K., who sent me lots of feedback and errata via e-mail and Twitter. And a very, very special shoutout to Florent Pillet who not only provided lots of feedback and errata, but actually gave me the idea of creating a Kotlin version for this second edition. Thanks a lot!

About the Author

Adrian Kosmaczewski is a writer, a software developer and a trainer. He is the author of many books about mobile software development, and has shipped mobile, web and desktop apps for iOS, Android, Mac OS X, Windows and Linux since 1996. Adrian holds a Master in Information Technology from the University of Liverpool.

When not coding or teaching, Adrian likes to spend time with his wife Claudia, his cat Max and his Olivetti Lettera 22 typewriter.

Part 1: Introduction

Getting Started

This first part of the book will guide the reader in the world of Android app development. We will first learn how to install and use Android Studio, we are going to get familiar with the tools and ecosystem, not only to create applications but to be able to debug them effectively.

1. Toolchain

Each platform vendor tries – and, to a large extent, succeeds at – locking third-party developers into their own ecosystem. This is true of many software platforms, and neither iOS nor Android are the exception to this rule.

One of the biggest efforts for iOS developers new to the Android ecosystem is getting used to a new set of tools, paradigms, workflows and even new keyboard shortcuts all over the place. This chapter will present an introduction to the various tools used in the everyday life of a seasoned Android developer.

1.1. TL;DR

As an introduction, these are the most important differences that distinguish the iOS developer experience from that of Android.

Table 1. Android vs. iOS Toolkits
Android iOS

IDE

Android Studio

Xcode

Profiling

Android Device Monitor

Instruments

Preview

Android Emulator

iOS Simulator

Programming Language

Kotlin or Java

Swift or Objective-C

Command Line

gradlew – ant

xcodebuild

Hacking

Rooting

Jailbreaking

Application metadata

AndroidManifest.xml

Info.plist

Dependency Manager

Gradle

CocoaPods – Carthage

Distribution

APK

IPA

Debugger

ADB + DDMS

LLDB

Logger

LogCat

NSLog() or print()

View Debugging

Hierarchy viewer

Xcode view debugging

Static Analysis

Android Lint

Clang Static Analyzer

1.2. Kotlin

For almost a decade, the only official language proposed by Google to create Android applications was Java. Since 2017, however, developers can use a new language called Kotlin, created by the same team that provides the Android Studio IDE.

Kotlin is a language that is 100% compatible and interoperable with Java; that means that all existing Java libraries can be used with Kotlin, and any binaries compiled with Kotlin can be integrated into standard Java projects. But it provides a much more "modern" syntax, with the following features:

  • Optionals and strict null checks

  • Type inference

  • Generics

  • Functional and object oriented features

  • Operator overloading

  • Pattern matching

Overhead

Using Kotlin in your Android project requires adding a small library used at runtime for interoperability with Java; this library increases the size of the final application in around one megabyte.

Kotlin compiles its code to native Android Runtime bytecode, which means that setting the small overhead of the runtime library aside, an application created with Kotlin behaves and is distributed exactly like one created with Java. Not only that, but you can mix and match Kotlin and Java code files in the same project without problem. Kotlin is able to access any JAR file compiled with Java, and Java is able to access anything defined in Kotlin.

This chapter offers a small overview of Kotlin; during the following chapters we are going to learn more about it just by using it in small projects.

Follow along

The code of this section is located in the Toolchain/Kotlin folder.

Variables and Constants

In Kotlin there are two types of in-memory storage: variables and constants. As the name implies, the former can be modified at runtime, while the latter cannot. To create them, just use var for variables and val for constants. The same syntax is used for class properties and for inline variables and constants.

Variables and constants
var variable = 43
val constant = "Hello"

var typedVariable: Float = 5.6F
val typedConstant: Boolean = true

var optionalVariable: Employee? = null
val optionalConstant: URL? = URL("https://sbb.ch")

var anyVariable: Any = "This can be anything"

var manager = Manager.managerFactory()
let is val

A very common problem when writing Kotlin with a Swift background is writing let instead of val. You have been warned.

String Interpolation

Whenever you need to compose a string with other values, you can simply interpolate variables using the $ sign.

String interpolation
val date = Date().toString()
val person = Employee("Johnny", 70)
print("Today is $date and ${person.name}'s age is ${person.age}")

You can use the ${obj.property} syntax to interpolate complex values or even calculations.

Parentheses and Brackets

There are two major differences between Kotlin and Swift regarding the use of parentheses and brackets in control flow statements such as if, while and for:

  1. Kotlin does require parentheses for if and while conditions, as well as the for statement.

  2. Kotlin does not require the use of curly brackets for their associated code blocks, just like in Java and many other languages.

Parentheses and brackets in Kotlin
val str = "Hello, Kotlin"

fun increase(value: Int): Int = value + 1

fun statements() {
    var i = 0
    while (i < 10) i = increase(i)
    if (str == "something") print(str)
}

The same code in Swift would look like this (pay attention to the placement of parentheses and brackets):

Parentheses and brackets in Swift
let str = "Hello, Swift"

func increase(_ value: Int) -> Int { return value + 1 }

func statements() {
    var i = 0
    while i < 10 { i = increase(i) }
    if str == "something" { print(str) }
}

Also pay attention to the fact that in Kotlin, if and when are expressions, not statements; this means that the following code is legal, although quite esoteric at first glance:

Expressions
val test = if (true) "Test" else "False"

val state = State.Off
fun decide() = when(state) {
    State.Off -> "Off"
    State.On -> "On"
}
val decision = decide()

Ranges

Ranges are very simple to create and use, and very similar to those in Swift.

Ranges
for (index in 1..5) {
    println("$index times 5 is ${index * 5}")
}
Double dot!

Please remember that to create ranges in Kotlin you need two dots, instead of just three as is the case in Swift.

Optional Types

Just like in Swift, Optional types are specified using the ? sign at the end of the class or type name; they indicate that a variable or constant can hold the null value. This makes it obvious in the code and to the compiler that some values might be null at runtime, while others may not. This makes your code safer and stronger.

Kotlin optionals
val optionalEmployee: Employee? = Employee("Olivia", 45)

val greeting = optionalEmployee?.greet()
println("greeting: $greeting")

val age = optionalEmployee?.age ?: 30 (1)

if (optionalEmployee != null) {
    val greetingAgain = optionalEmployee.greet()
    println("greeting: $greetingAgain")
    manager.addPerson(optionalEmployee)
}
1 The "Elvis" operator ?: assigns a default value to the age variable if the null check fails.

Instead of using the classic if (optional != null) construction, Kotlin provides the let() method, which works similarly to Swift’s if let statement:

Using the let optional methods
optionalEmployee?.let { (1)
    it.greet()
    println("Employee ${it.name} is ${it.age} years old")
}

optionalEmployee?.let { employee -> (2)
    employee.greet()
    println("Employee ${employee.name} is ${employee.age} years old")
}
1 By default, the "unboxed" value of the optional is stored inside of the it variable.
2 You can provide your own variable name if you want, using this syntax.

Optionals are a very modern and practical way to avoid null pointer exceptions. Code using optionals will clearly state the possibility that a reference contains a null value or not; in those cases, the use of a ? sign helps everyone who reads the code to understand what is going on.

Double bang!

Please remember that to unwrap a Kotlin optional you must use a "double bang" !! unlike Swift, which only requires one.

As a corollaire to their use, and similarly to the corresponding recommendation in Swift, you should pay attention never to abuse of the !! operator, which effectively tells the compiler "believe me, I know this reference is not null at this point in time." Once you tell your compiler this, it is your responsibility to actually make sure that the reference is never null, or your code could crash at runtime.

Smart Casting of Optionals

Kotlin automatically unwraps optionals that have been checked using if (optional != null), similarly to how Swift uses conditional binding with the if let syntax. This is shown in Android Studio as follows:

Smart casting in action
Figure 1. Smart casting in action

Collections

Kotlin can use the standard collection types available to Java, but it provides a simpler syntax, which makes them easier to use.

Collections
val stringArray = arrayOf<String>()
val stringList = listOf<String>()
val stringFloatMap = mapOf<String, Float>()
val stringSet = setOf<String>()

// Iterating over the elements of arrays, lists, maps and sets
for (str in stringArray) {
    println("A value in the array is '$str'")
}

for (str in stringList) {
    println("A value in the list is '$str'")
}

for ((str, num) in stringFloatMap) {
    println("Pair => $str: $num")
}

for (str in stringSet) {
    println("Set element: $str")
}

// Arrays vs Lists: which one to choose?
// An array has a fixed size and is usually very fast
val shoppingList = arrayOf("salt", "sugar", "milk", "cheese")
// You cannot add or remove items! This won't work:
// shoppingList.add("bread")
// but you can modify an individual item if needed
shoppingList[1] = "bottle of wine"

// If you need to add or remove items at runtime,
// consider using a mutable list instead:
val countries = mutableListOf<String>("Switzerland", "France", "Germany")
countries.add("Italy")
countries.remove("France")

val jobs = mutableMapOf(
        "Roger" to "CEO",
        "Martin" to "CTO"
)
jobs["Adrian"] = "Writer"
Collection creation syntax

As a Swift developer, please pay attention to the fact that in Kotlin one does not use [] to instantiate an empty array, but rather the arrayOf() function; the same applies to listOf(), mapOf(), and setOf(). This is a very common mistake for Swift developers new to Kotlin.

Pattern matching

Kotlin bundles powerful pattern matching capabilities, and includes the when expression, which is much more flexible than its switch counterpart in Java.

Pattern matching
val number = 42
when (number) {
    in 0..7, 8, 9 -> println("1 digit")
    10 -> println("2 digits")
    in 11..99 -> println("2 digits")
    in 100..999 -> println("3 digits")
    else -> println("4 or more digits")
}

Functions and Methods

Kotlin uses the keyword fun to define functions and methods. When functions are one-liners, they can have their body defined with the = sign, and they do not need to explicitly have a return type; it will be inferred from the returned value.

Pattern matching
fun method1(input: String): Int {
    return input.length
}

fun method2(input: String) = input.length

fun <T>method3(input: T) = input.toString()
In Kotlin, methods without an explicit return type do not use Void but Unit, and that means that they are actually returning a value even if this value is discarded at runtime.

Object-Oriented Programming

Kotlin is a fully featured object oriented language, including different useful features very similar to their Swift counterparts. Not only does it contain concepts such as classes and interfaces, but it also provides useful abstractions such as data classes and interface extensions.

Data classes provides a quick way to create typical "POJOs" (Plain Old Java Objects) in your apps, and as you might expect, interface extensions provides the possibility to include a default implementation for interface methods. This opens the door to powerful design patterns and flexible code, and also to use our "protocol oriented programming" techniques in Kotlin.

Interfaces

Kotlin interfaces are the equivalent of Swift protocols, and they also include extensions.

Interfaces
interface Person {
    (1)
    var name: String
        get set

    (2)
    fun greet() = "Hello! I am $this"

    (3)
    fun showMoreInformation()
}
1 Interfaces can specify properties and methods; in this case, a read-write property, to be implemented by subclasses.
2 This function provides a default implementation; classes implementing this interface can override this behaviour if needed.
3 This is a standard interface method, and just like in Java, classes must provide an implementation for it or otherwise the code will not compile.
Class Constructors

One of the most puzzling things about Kotlin for a Swift developer is the slightly uncommon syntax for constructors. The parameters of the class constructors are placed directly after the class declaration, including their visibility modifiers; they are not only the parameters of the constructor, but also the class fields, all in one definition.

Kotlin class constructor
class Manager(private var backingName: String = "",
              private var staff: MutableList<Person> = mutableListOf<Person>(),
              var state: State = State.Off) : Person {

To initialize class fields not part of the constructor parameters, or to perform some code at construction, you can provide an init method.

Init method
private val isActive: Boolean

init {
    isActive = true
}

In this example we use the init body to initialize a constant at construction time.

Data Classes

Kotlin also provides data classes which are the basis for POJOs; these classes provide automatically an implementation of hashCode() and toString().

Data class
data class Employee(private var backingName: String = "",
                    var age: Int = 30) : Person {

For all practical purposes, one can consider data classes as the Kotlin equivalent of Swift structs.

Class Instantiation

Once defined, you can use classes in your code using exactly the same syntax as you would in Swift:

Instantiating and using classes
val person1 = Employee("Olivia", 45) (1)

val person2 = Employee().apply { (2)
    name = "Thomas"
    age = 56
}
println("People: ${person1.greet()} and ${person2.showMoreInformation()}")

manager.addPerson(person1)
manager.addPerson(person2)
manager.state = State.On
1 The classic constructor call syntax in Kotlin is pretty much the same as in Swift.
2 This syntax, using the apply() function, will sound familiar to C# developers; it allows to customize a new instance, explicitly passing the values of the different parameters, resulting in very clear and explicit code.
Class Extensions

Kotlin allows you to extend any type with new properties and methods; this can be very powerful to create terse, more compact code and to place helper methods where they belong.

Defining class extensions
val Double.fahrenheit: Double get() = (this * 9 / 5) + 32
val Double.celsius: Double get() = (this - 32) * 5 / 9
Using class extensions
val temperature: Double = 32.0
val fahrenheit = temperature.fahrenheit
val celsius = fahrenheit.celsius
println("$temperature degrees Celsius is $fahrenheit degrees Fahrenheit")
Simple Objects and Singletons

Another nice feature of Kotlin is that you do not need to define a class to instantiate just an object; you can use the keyword object and define any static object that you need in your application, like singletons.

Data class
object Constants {
    val PI = 3.14
    val ANSWER = 42

    fun name() = "Math contstants"
}

This approach is commonly used in Android to define broadcast listeners or event handlers, for example.

Companion Objects

Kotlin classes do not have static members. If you need to provide static methods and constants to your classes, you must do so using a feature called companion object.

Companion object
companion object OptionalName {
    val MAXIMUM_EMPLOYEE_COUNT = 10

    fun managerFactory() = Manager("Maria Hill")
}

All variables, constants and functions inside of the companion object appear automatically as static members for the calling code. The companion object can have an optional name; in that case you can use it to access its members.

Operator Overloading

Kotlin makes it very easy to overload operators, since they are simply methods with specific names; we can, for example overload the + operator for any class by adding an operator fun plus() method with the required types.

Operator overloading
operator fun plus(person: Person): Team {
    return Team(this, person)
}

The table below gives an idea of the various operators that can be overloaded in Kotlin

Table 2. Overloading operators
Operator Method to override

+

unaryPlus()

-

unaryMinus()

!

not()

++

inc()

--

dec()

+

plus()

-

minus()

*

times()

/

div()

%

rem()

..

rangeTo()

in

contains()

[]

get() and set()

()

invoke()

+=

plusAssign()

-=

minusAssign()

*=

timesAssign()

/=

divAssign()

==

equals()

> < >=

compareTo()

Using overloaded operators is as simple as you might expect.

Using overloaded operators
val team = manager + person

As is the case with any language that provides operator overloading (such as C++ or Swift), make sure that using this feature actually increases the readability of the code.

Infix Methods

Kotlin allows methods taking just one parameter to be defined as infix.

Infix method
infix fun addPerson(person: Person) {
    if (staff.count() < MAXIMUM_EMPLOYEE_COUNT) {
        staff.add(person)
    }
    else {
        throw Exception("Cannot add more staff members")
    }
}

Using an infix method is like using an operator, without the need of a dot and parentheses, which may increase the readability of the code.

Using infix methods
manager addPerson person

Enumerations

If your application requires types that have only a few discrete values, you can simply define an enumeration to hold them all together.

Enumerations
enum class State {
    On,
    Off
}

Enumerations are fully-fledged classes, and can contain properties, functions and more.

Comparing Kotlin to Objective-C and Swift

The following table provides a comparison between these three languages.

Table 3. Comparison of Kotlin 1.2, Objective-C 2.0 and Swift 4
Kotlin 1.2 Objective-C 2.0 Swift 4

Inheritance

Simple, with interfaces and extensions

Simple, with protocols

Simple, with protocols and protocol extensions

Semicolons

Optional

Mandatory

Optional

Class definition

class

@interface & @implementation

class

Interfaces

implements interface

conforms to @protocol

conforms to protocol

Including code

import (symbols)

#import (files)

import (symbols)

Class extensions

Extensions

Categories

Extensions

Dynamic typing

Any

id

Any

Private field suffix

Not used

_ (underscore)

_ (underscore)

Memory management

Garbage collection

Manual or Automatic Reference Counting

Automatic Reference Counting

Generics

yes (type erasure)

yes (type erasure)

yes

Method pointers

no

@selector

#selector

Callbacks

Lambdas

Delegate objects and blocks

blocks

Pointers

no

yes

Via library classes

Root class

Object

NSObject / NSProxy / …

NSObject / NSProxy / …

Visibility

public / internal / protected / private

@public / @protected / @private (only fields)

open / public / internal / fileprivate / private

Exception handling

try / catch / finally + Exception

@try / @catch / @finally + NSException

do / try / catch + Error

Namespaces

Packages

Through class prefixes

Implicit, via modules

Formal grammar

kotlinlang.org

developer.apple.com

Converting Java Projects to Kotlin

To convert a Java project to Kotlin is very easy:

  1. Select the Tools  Kotlin  Configure Kotlin in Project menu entry. This will modify the Gradle files of the project to automatically include the Kotlin plugin for the modules.

  2. Once this is done, select the Java files you would like to convert in the Android pane on the left side of the Android Studio window, and select the Code  Convert Java File to Kotlin File menu entry.

The conversion from Java to Kotlin is usually quite fast and provides relatively good quality Kotlin code. You might need, depending on your project, to rewrite some Java idioms into Kotlin ones, particularly in what pertains to optionals.

Java and Kotlin in the same project

Remember that Android Studio projects can contain any number of both Java and Kotlin files, and they can coexist peacefully one next to the other. The Toolchain/Kotlin projects shows how a Kotlin class can use a Java class and viceversa.

The Android Runtime

Another important fact about Android is that, by design, the Java applications compiled for the Android operating system are not compatible with a standard Java Virtual Machine – JVM – such as the ones available for Windows, macOS or Linux. This simple fact is often overlooked but it is very important to remember.

Android and the JVM

Compiled Android Java applications are not compatibles with the standard Java Virtual Machine by Oracle.

Android applications are compiled as DEX binaries (which stands for "Dalvik Executables") and run in a special virtual machine, optimized for mobile devices, formerly known as "Dalvik" and now most commonly referred to as the Android Runtime (ART.) Binaries targeting the ART have the following characteristics:

  1. Developers can include Java code bundled in binary form, such as JARs (Java Archives) in their applications; they can also include the source files in their projects, but all of this will be compiled as Android DEX binaries, which has a different binary structure.

  2. Not all valid Java APIs for a standard JVM exist under ART; in particular most of the javax. packages are unavailable in Android.

  3. DEX files are smaller than their equivalent JARs.

  4. ART uses a register-based architecture, instead of the standard JVM stack-based architecture, in order to increase performance.

  5. ART uses non-JVM standard bytecode instructions, and a different inter-process protocol.

  6. ART can run several Android applications in the same process if required.

From Android 2.2 "Froyo" to Android 5 "Lollipop", a just-in-time compiler (JIT) had been added to the Dalvik virtual, helping it increase the performance of the final code. ART, on the other hand, single handedly compiles all downloaded apps to native code upon installation, and provides much better garbage collection and debugging facilities than Dalvik.

Compilation

The next diagram shows how close "APK" files are to the equivalent "IPA" files distributed by the Apple App Store. In both cases it consists of a compressed archive containing both the binary of the executable and all of its bundled resources, following a very particular folder structure.

diagram compilation
Figure 2. Android Application Compilation

Android Release History

The following table shows the history of Android releases, borrowed from Wikipedia, combined with information from the Android developer dashboard. This information is valid as of August 2017.

Table 4. Android Version History And Market Share
Code Name Version Number Release Date API Level Support status %

Alpha

1.0

September 23, 2008

1

Discontinued

Beta

1.1

February 9, 2009

2

Discontinued

Cupcake

1.5

April 27, 2009

3

Discontinued

Donut

1.6

September 15, 2009

4

Discontinued

Eclair

2.0 - 2.1

October 26, 2009

5 - 7

Discontinued

Froyo

2.2 - 2.2.3

May 20, 2010

8

Discontinued

Gingerbread

2.3 - 2.3.7

December 6, 2010

9 - 10

Discontinued

0.4%

Honeycomb

3.0 - 3.2.6

February 22, 2011

11 - 13

Discontinued

Ice Cream Sandwich

4.0 - 4.0.4

October 18, 2011

14 - 15

Discontinued

0.5%

Jelly Bean

4.1 - 4.3.1

July 9, 2012

16 - 18

Discontinued

5.9%

KitKat

4.4 - 4.4.4

October 31, 2013

19 - 20

Discontinued

13.4%

Lollipop

5.0 - 5.1.1

November 12, 2014

21 - 22

Supported

26.3%

Marshmallow

6.0 - 6.0.1

October 5, 2015

23

Supported

29.7%

Nougat

7.0 - 7.1.2

August 22, 2016

24 - 25

Supported

23.3%

Oreo

8.0 - 8.1

August 21, 2017

26 - 27

Supported

0.5%

As mentioned in the preface, the code samples that are bundled with this book support Lollipop (API 21) as their minimum requirement. This is because at the time of this writing, 80% of all Android devices in circulation run a version of Android equal or bigger to Lollipop.

1.3. Android Application Startup

When a user taps on the icon of an Android application a whole series of events happen in the device. Many of these events are very similar to those in iOS, and it turns out that, quite unsurprisingly, both operating systems use a very similar architecture, but with quite different class structures backing them.

Let us create a small project in Android Studio. In that project, add a subclass of the android.app.Application class, and register that class as the main application class in your AndroidManifest.xml file. Add two breakpoints in the source code, one in the Application.onCreate() method, and another in the MainActivity.onCreate() method.

The stack traces when hitting both breakpoints is shown below:

Application.onCreate()
training.akosma.startup.StartupApplication.onCreate(StartupApplication.java:8)
com.android.tools.fd.runtime.BootstrapApplication.onCreate(BootstrapApplication.java:370)
android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1012)
android.app.ActivityThread.handleBindApplication(ActivityThread.java:4553)
android.app.ActivityThread.access$1500(ActivityThread.java:151)
android.app.ActivityThread$H.handleMessage(ActivityThread.java:1364)
android.os.Handler.dispatchMessage(Handler.java:102)
android.os.Looper.loop(Looper.java:135)
android.app.ActivityThread.main(ActivityThread.java:5254)
java.lang.reflect.Method.invoke(Method.java:-1)
java.lang.reflect.Method.invoke(Method.java:372)
com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
MainActivity.onCreate()
training.akosma.startup.MainActivity.onCreate(MainActivity.java:10)
android.app.Activity.performCreate(Activity.java:5990)
android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1106)
android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2278)
android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2387)
android.app.ActivityThread.access$800(ActivityThread.java:151)
android.app.ActivityThread$H.handleMessage(ActivityThread.java:1303)
android.os.Handler.dispatchMessage(Handler.java:102)
android.os.Looper.loop(Looper.java:135)
android.app.ActivityThread.main(ActivityThread.java:5254)
java.lang.reflect.Method.invoke(Method.java:-1)
java.lang.reflect.Method.invoke(Method.java:372)
com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)

There are several interesting bits of information in the stack traces above. First of all, the android.os.Looper class, which as the name suggests provides the main run loop of the application. Most GUI toolkits include a similar construction, created at application runtime, holding an event queue and routing events from the operating system to the different activities and components of the application.

Looper == NSRunLoop

For all practical purposes, iOS developers will recognize that the android.os.Looper class is the equivalent of the NSRunLoop class in Cocoa. If you want to learn more about Looper, please check the official Android documentation by Google.

If you click on the name of a class in Android Studio while holding down the key, the IDE will open the corresponding class file; if you do not have the Android source code available in your local workstation the IDE will simply decompile the code from the local SDK and show a stub implementation of the corresponding class, with most of its methods.

By doing this repeatedly, from both the MainActivity and the Application subclass you created previously, you are going to arrive to the android.content.Context class, which is arguably the most important class in the Android SDK. The Context class includes many different methods, ranging from file management to database creation to inter-process communication, and it also holds a reference to the underlying Looper class.

Equivalent in iOS

There is no similar equivalent of android.content.Context in iOS, however one could argue that NSResponder fulfills a similar role, since many different classes such as UIViewController, UIView and even UIApplication are all subclasses of NSResponder. However, Context is a very different beast from NSResponder!

The next diagram shows a simplified class hierarchy of the Activity and Application classes and its relationship with the Looper class.

diagram context class
Figure 3. The android.content.Context class

1.4. Zygote

One of the biggest problems with any Java Virtual Machine (JVM) is the long startup time. In the case of Android, this problem is even bigger, given the limited resources of many devices in the market. Moreover, since every application has to run its own copy of the JVM to ensure privacy and security through sandboxing, the price to pay would be too steep.

Zygote is the name of a process that Android runs as soon as the operating system stops booting. Its objective is to launch a copy of the Android Runtime (ART), which is Android’s version of the JVM, so that it is ready to use by every application in the system.

As soon as the user taps on the icon of an application to launch it, Android will ask Zygote to "fork" a copy of the JVM process, so that each application runs in its own sandbox, without conflicting with other processes. Zygote also preloads a certain number of libraries and resources that are usually required by most Android applications to run properly. All of this substantially speeds up the launching time of applications.

More about Zygote

If you want to know more about Zygote, this answer in Stack Overflow provides an excellent summary.

1.5. Android Studio

Android Studio is a free IDE provided by Google to develop Android applications. It replaced the venerable Eclipse Android Developer Tools, historically the first official IDE for Android software development for many years. It was announced for the first time in May 2013 at the Google I/O conference. The first stable release was in December 2014. It is available for Windows, macOS and Linux, and is now considered the official IDE for Android development.

End-of-life of the Eclipse Android Developer Tools

Google has announced in November 2nd, 2016 the official end of support and development of the Eclipse Android Developer Tools, which are completely superceded by Android Studio 2.2.

Android Studio is powered by IntelliJ IDEA, a popular IDE for Java development for the past 15 years. It has a solid reputation, and is particularly appreciated by its advanced support for refactoring, code generation and project navigation features.

Android Studio is available from the Android Studio website. The current version at the time of this writing is 3.0.1. Android Studio is, by far, the most important piece in the daily workflow of an Android Developer, and includes many different features targeted to simplify the development of Android apps, which like all software development can be quite a complex endeavour sometimes.

Android Studio Splashscreen
Figure 4. Android Studio Splashscreen

Once downloaded and launched, Android Studio will launch a configuration wizard. Most developers will choose the standard settings. Finally, Android Studio will automatically download all the elements required for it to work properly.

Android Studio Migrating Preferences
Figure 5. Android Studio Migrating Preferences
Android Studio Setup Wizard – Step 1
Figure 6. Android Studio Setup Wizard – Step 1
Android Studio Setup Wizard – Step 2
Figure 7. Android Studio Setup Wizard – Step 2
Android Studio Setup Wizard – Step 3
Figure 8. Android Studio Setup Wizard – Step 3
Android Studio Setup Wizard – Step 4
Figure 9. Android Studio Setup Wizard – Step 4
Android Studio Setup Wizard – Step 5
Figure 10. Android Studio Setup Wizard – Step 5

Once Android Studio is ready to go, it will display some tips and tricks every day – something you can easily dismiss if you want.

Android Studio Tips
Figure 11. Android Studio Tips
Android SDK Environment

Once Android Studio is installed, it is strongly recommended to configure the environment of your system to point to the folder where the Android SDK resides. In my system, I have added an ANDROID_HOME environment variable in my .zshrc file, as follows:

# Path for the Android SDK
export PATH=~/Library/Android/sdk/platform-tools:~/Library/Android/sdk/tools:"${PATH}"
# For Android stuff
export ANDROID_HOME=~/Library/Android/sdk

Creating a New Project

To create a new project in Android Studio , just select the File  New  New Project menu item, and follow the instructions as shown in the following screenshots.

New Project Wizard – Step 1
Figure 12. New Project Wizard – Step 1
New Project Wizard – Step 2
Figure 13. New Project Wizard – Step 2
New Project Wizard – Step 3
Figure 14. New Project Wizard – Step 3
New Project Wizard – Step 4
Figure 15. New Project Wizard – Step 4

After running the project wizard, Android Studio should show you a windows similar to the one featured in the image below.

New project in Android Studio
Figure 16. New project in Android Studio
Invoking Android Studio Actions

Instead of clicking your way around in menus, you might want to learn the handy +Shift+A shortcut; this command allows you to invoke any operation available in the IDE without leaving your hands from the keyboard.

Command Line Tool

Android Studio allows developers to install a command line utility, useful to open projects directly from a terminal session. Select the Tools  Create Command Line Launcher… menu entry and select the output folder for the script.

Creating a launcher script for Android Studio
Figure 17. Creating a launcher script for Android Studio

Once installed, just type studio . at the root of a folder containing an Android Studio project, and a new windows with the current project will appear on your screen.

Many of the external tools required to build Android applications are available directly from the Tools  Android menu in Android Studio.

Tools/Android Menu in Android Studio
Figure 18. Tools/Android Menu in Android Studio

We are now going to learn more about each of these pieces individually.

1.6. SDK Manager

As the name implies, the SDK Manager allows the developer to install, manage and uninstall different versions of the Android Software Development Kit (SDK) in the local workstation. The next figure shows the default state after launching Android Studio and installing the latest available version of Android at the time of this writing, 7.1.1 (also known as Nougat.)

Android SDK Manager
Figure 19. Android SDK Manager

1.7. AVD Manager

The Android Virtual Device or AVD manager allows you to create emulators for your debugging sessions.

Android AVD Manager Wizard – Step 1
Figure 20. Android AVD Manager Wizard – Step 1
Android AVD Manager Wizard – Step 2
Figure 21. Android AVD Manager Wizard – Step 2
Android AVD Manager Wizard – Step 3
Figure 22. Android AVD Manager Wizard – Step 3
Android AVD Manager Wizard – Step 4
Figure 23. Android AVD Manager Wizard – Step 4
Android AVD Manager Wizard – Step 5
Figure 24. Android AVD Manager Wizard – Step 5

Running apps in the Emulator

Once you have created an Android Virtual Device using the AVD Manager, you can assign it to be used for debugging. To do that, you have to first create a "Run/Debug configuration." You can do that directly from Android Studio using the Run  Edit Configurations… menu.

Creating Run and Debug Configurations in Android Studio
Figure 25. Creating Run and Debug Configurations in Android Studio
Emulator incompatibility with Docker

At the time of this writing, the following message might appear on the Run console of Android Studio when starting the built-in emulator:

emulator: ERROR: Unfortunately, there's an incompatibility between HAXM hypervisor and VirtualBox 4.3.30+ which doesn't allow multiple hypervisors to co-exist.  It is being actively worked on; you can find out more about the issue at http://b.android.com/197915 (Android) and https://www.virtualbox.org/ticket/14294 (VirtualBox)

If this happens to you, make sure you are not running Docker for macOS at the same time, and if this is the case, quit Docker.

To run your application, just click the Play button on the toolbar. You are going to be asked to select a device (virtual or physical) to run your device into.

Types of emulator binaries

The AVD manager offers both "x86" and "ARM" Android images; it is recommended to install only "x86" images, which run faster in Mac workstations. It is much better to test your app directly on a device, if you need to test the code in an ARM environment.

Standard Android Emulator
Figure 26. Standard Android Emulator

Genymotion

As convenient as the built-in Android emulator is, it suffers from several drawbacks:

  • It can take a while to start, up to several minutes, depending on the memory available in your machine and the speed of the CPU.

  • It does not work in full-screen mode on macOS.

  • It conflicts with Docker for macOS.

Coping with a slow emulator

Please keep in mind that the default Android emulator can be slow at times, so it is strongly recommended to launch an instance of it and to leave it running while you work on your code. Thankfully, Android Studio includes a new feature which allows your code to be deployed much faster to the device or the emulator, and this will help you have shorter code-test cycles.

Many professional Android developers use the Genymotion emulator instead, which is much faster than the official emulator, offers full-screen mode compatibility to macOS users, and does not conflict with Docker. To use it, you must install VirtualBox from Oracle first.

Free for personal use

Genymotion offers a free download for personal use and evaluation, but it is a commercial developer tool and it is non-free for professional use. Please refer to the Genymotion website for information about pricing and how to buy.

Genymotion Emulator
Figure 27. Genymotion Emulator
Genymotion SDK path

If you use the Genymotion Emulator, make sure to open the Settings screen and select the Use custom Android SDK tools option, and browse to the ~/Library/Android/sdk folder. This will allow the standard Android Debug Bridge included with Android Studio to properly communicate with the Genymotion Emulator.

Genymotion Emulator Settings
Figure 28. Genymotion Emulator Settings

Android Emulator vs. iOS Simulator

At this point, my dear iOS developer reader must be remembering fondly the snappiness and convenience of use of the iOS Simulator. It is important to remember that the words "Emulator" and "Simulator" are not synonyms!

Table 5. iOS Simulator vs. Android Emulator
iOS Simulator Android Emulator

Type of Code

x86

ARM & x86

Hardware support

Limited: orientation, memory warnings, UI

Extended: camera, accelerometer, telephony…

In short, the iOS simulator allows Xcode to run applications compiled for the x86 architecture to run in a small window that has the shape and size of an iOS device. These iOS applications have access to the whole RAM, disk and operating system features of macOS, but they just happen to run in a window with a strange shape.

The Android Emulator, on the other hand, allows applications compiled for the ARM or Intel architectures to run in a well-defined sandbox, with strict memory, disk and networking capabilities, while providing a translation layer for the instructions targeting the ARM architecture to be executed by an x86 CPU.

Without regard for these differences, it is strongly recommended (for both iOS and Android developers) to run and debug their applications in real devices; this will give them a better idea of the performance and the characteristics of their code in the real environment.

1.8. Gradle

It is safe to assert that Android Studio is, under the hood, just a visual environment built around Gradle, an open source build tool created with the Groovy programming language. You can think of Gradle as a tool similar to Maven or Make, but specifically taylored for the task of building and deploying Android applications.

Every Android Studio project includes three default Gradle build files:

  1. The build.gradle file at the root of the Android Studio project, providing configuration options for all subprojects and modules.

  2. The settings.gradle file, at the root of the project, which specifies the Gradle files to include (by default, only the app/build.gradle file.)

  3. The app/build.gradle file, which contains specific instructions and settings for the Android application that will be built by the project.

The latter one specifies compilation parameters, build types, dependencies and many other parameters. The next listing shows a typical Gradle build file.

The app/build.gradle file
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'

android {
    compileSdkVersion 27
    defaultConfig {
        applicationId "training.akosma.introduction"
        minSdkVersion 21
        targetSdkVersion 27
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:27.0.2'
    implementation 'com.android.support.constraint:constraint-layout:1.0.2'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.1'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}
repositories {
    mavenCentral()
}

Gradle can be invoked from the command line, using the following command:

$ chmod +x ./gradlew
$ ./gradlew

The output of running Gradle on the command line looks like this:

To honour the JVM settings for this build a new JVM will be forked. Please consider using the daemon: https://docs.gradle.org/2.14.1/userguide/gradle_daemon.html.
Incremental java compilation is an incubating feature.
:help

Welcome to Gradle 2.14.1.

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

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

BUILD SUCCESSFUL

Total time: 9.259 secs

To discover the various predefined tasks available in Gradle, just run

$ ./gradlew tasks

This will yield a long lists of tasks.

Learning Gradle

Virtually anything that can be done from the Android Studio IDE can be done from the command line using Gradle. It is recommended to become familiar with this tool, since this knowledge will be helpful to configure build scripts, continuous integration systems, and more.

1.9. Other Tools

The Android developer life is filled with various tools, each with a specific task. This section will give an overview of the most important of them.

Android Debug Bridge

The Android Debug Bridge (or ADB for short) provides the capability to debug your application from Android Studio in an emulator or in a device connected to the developer workstation. ADB can be used in the command line, to install applications in a device or an emulator, and to launch debugging processes.

ADB is a client-server system that consists of three items:

  • A client, running on your developer machine, used to trigger commands.

  • A daemon, running on the device or the emulator, receiving and responding to commands.

  • A server, running as well in your developer machine, coordinating the communication between client and daemon.

Enable developer options in your device

To enable ADB in your device, you have to enable the USB debugging option in your device, which is part of the Developer options. On Android 4.2 and later, you can enable the (otherwise hidden) developer options by tapping seven times on the Build number item in the Settings > About phone screen.

Developers can debug applications both via USB and via Wifi. Below is a list of useful ADB commands:

  • adb devices shows the list of available devices.

  • adb start and adb stop help to start and stop emulator instances.

  • adb connect starts a debugging session on a device connected through a wifi network.

  • adb pull remote local copies the remote file to the local file.

  • adb push local remote copies the local file to remote on the device.

  • adb shell starts a shell on the remote device or emulator.

  • adb shell screencap /sdcard/screen.png takes a screenshot of the current display of the device.

  • adb shell screenrecord /sdcard/demo.mp4 records the current activity of the device in an MP4 movie.

logcat

logcat is the logger library used in Android apps. It basically replaces the use of NSLog() (or print() in Swift) to output data in the console while debugging or running Android apps.

Any Android application can log messages to the console by using the following code:

Log.i("application", "This is a message for logcat")

The Log class is available after importing it:

import android.util.Log

Hierarchy Viewer

The Hierarchy Viewer is very similar to the View debugger in Xcode. It allows developers to inspect and understand the view tree with all the widgets displayed in the user interface. In Android Studio, select the Tools  Android  Android Device Monitor menu and select the Window  Open Perspective    Hierarchy View.

The tree view on the left of the Android Device Monitor window allows you to select the activity you want to inspect (which can be any running application on a device or an emulator.) Select the one that corresponds to your application, and you will be able to see the full view hierarchy on the center of the window.

ProGuard

ProGuard is used to analyze the contents of APK files, in order to reduce their download size, and to make sure that the limit of 65'536 methods is not overridden.

Maximum size for Dalvik executables

Dalvix dex files are limited to a maximum number of 65'536 methods; this is by design, as surprising as it may sound. This number of methods includes the methods imported from other libraries in the application. It is very important then to use ProGuard to remove unused code! If you need to support more, please check the corresponding article in the official documentation.

KDoc

Android Studio can extract the KDoc documentation from your APIs, which is very similar to Javadoc documentation. Select the Tools  Generate JavaDoc… menu and the dialog shown in the image below will guide you.

Javadoc Generation Dialog
Figure 29. Javadoc Generation Dialog

You can add KDoc comments very easily, on top of classes, methods, fields and any other Java element:

/**
 * Short description.
 *
 * @param  savedInstanceState Description
 * @return Description
 */
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
}
Dokka Documentation Tool

Although the KDoc and Javadoc formats are very similar, the official code documentation tool for Kotlin is Dokka.

1.10. Summary

Google provides a solid set of tools for Android development, including most if not all the tools required to get the job done. Some commercial tools exist as well, and they provide a certain added value to the equation.

Developers spend most of their time in Android Studio, editing and debugging code, both in emulators and on a device. Devices must have "developer mode" enabled in order to enable debugging via USB.

The Android SDK Manager is in charge of the installation and removal of different versions of the Android SDK in the workstation. The Android Virtual Device Manager is used to create emulators with different versions of Android. The Genymotion emulator is a commercial option to the standard Android emulator, offering several functionalities and better performance.

With the knowledge gained during this chapter, we are going to start writing some Android applications, in order to learn how to tie all these tools together.

2. Debugging

Writing software is a difficult activity, and in Android the chances for things to go wrong are multiplied by the astronomical number of devices available in the market. This chapter will show some useful techniques to create, debug and troubleshoot apps in different environments and with different tools.

2.1. TL;DR

For those of you in a hurry, the table below summarizes the most important pieces of information in this chapter.

Table 6. Debugging Android Apps
Android iOS

Debugger

JDB

LLDB

Log output

logcat

Xcode console

Remote debugging

yes

yes

Log viewers

PID Cat & LogCat

libimobiledevice & deviceconsole

Network logger

NSLogger

NSLogger

2.2. Enabling Exception Breakpoints

The first thing that I recommend you to do is to enable exception breakpoints, either caught or uncaught. This will enable Android Studio to stop the execution of the application in case an Exception occurs, and given the wide range of possibilities for errors, this can be a handy measure before any debugging session starts.

To do that, just open the Run  View Breakpoints… (or hit the Shift++F8 keyboard shortcut) and check the "Java Exception Breakpoint" and the "Any Exception" checkboxes.

Breakpoints Window in Android Studio
Figure 30. Breakpoints Window in Android Studio

2.3. Enabling USB Debugging

If you have reached this point in the book one could imagine very well that you have been able to successfully run code in your device; but for those readers who have jumped directly to this section, here is a quick recap of the steps required to debug applications in your device.

First, you must enable "Developer mode" in your device. Open the Settings application and scroll to the "About phone" section. In that screen you should see a "Build number" entry, which you must tap seven times. If you try to do it again, you should see a "toast" message just like the one shown below.

Developer mode already active
Figure 31. Developer mode already active

After you have done that, the Settings application will display a new "Developer" item. Select it, scroll down and you will see a toggle switch to enable USB debugging in the device.

USB Debugging switch
Figure 32. USB Debugging switch

2.4. Enabling WiFi Debugging

The Android debugger can also be used via a standard WiFi connection, although this can be a security problem and you should remember to disable this capability when you are done.

First connect your device with the USB cable and make sure that ADB is running in a specific port:

$ adb tcpip 5555

To enable WiFi debugging, then issue the following command, to retrieve the IP address of the device:

$ adb -s e99b50ed shell ifconfig

The common output of the ifconfig command returns information for each of the interfaces available in the device; normally the one labeled wlan0 is the one you are looking for. Write down the IP address, disconnect the USB cable and then run the next command:

$ adb connect 192.168.1.xxx:5555

The adb devices command should show something similar to the following now:

$ adb devices -l
List of devices attached
192.168.1.xxx:5555     device product:OnePlus3 model:ONEPLUS_A3003 device:OnePlus3

If you see the above, you can now use the device from Android Studio or any other IDE, and you will be able to debug your application just as if it were running tethered through the standard USB cable.

At the end of your debugging session, make sure to type the command to disable wifi debugging in your device.

$ adb -s 192.168.1.xxxx:5555 usb

This is a security measure, to disable any attempts in any network to debug and inspect applications in your device. After running this command, any attempt to connect to your device should print the following output:

$ adb connect
unable to connect to 192.168.1.xxx:5555: Connection refused.
Some devices offer a menu entry in the Developer settings called "ADB over network" which serves the same purpose, allowing you to enable and disable the setting visually, as shown in the picture below. (Source: stackoverflow.com/a/10236938/133764)
ADB over network option (Source: http://stackoverflow.com/a/10236938/133764)
Figure 33. ADB over network option

2.5. Working on the Command Line

I am a bit of a command line junkie, and I like being able to perform as many tasks as possible using my preferred macOS terminal tools: iTerm 2, zsh and tmux. In this section we are going to see how easy it is to leave Android Studio aside for a while, and use command line tools to manage Android applications.

First, open your preferred terminal and make sure that you have all the required tools on your system:

$ env | grep ANDROID
ANDROID_HOME=/Users/adrian/Library/Android/sdk
$ which adb
/Users/adrian/Library/Android/sdk/platform-tools/adb

Then cd into the folder of any of the applications provided with this book and run the following command to build the application directly from the command line:

$ ./gradlew build

Let us install the application in the device. First, connect your Android device to your Mac and run the following command to verify that ADB is connected to it:

   $ adb devices -l
List of devices attached
e99b50ed               device usb:337772544X product:OnePlus3 model:ONEPLUS_A3003 device:OnePlus3
If your device does not appear, just plug and unplug the USB cable of the device, and make sure that you have enabled USB debugging in the device.

Of course, in your case the output will be different; this is what I see when I connect my own OnePlus 3 device with the USB cable.

To install the debug build in my device, I just have to run the following command:

$ ./gradlew installDebug

This will install the application in all the devices currently connected to the adb daemon. Alternatively, you can also install your application in just one device using the adb command:

$ adb -s e99b50ed install app/build/outputs/apk/debug/app-debug.apk
Taking screenshots using adb

As mentioned previously in this book, you can easily take screenshots using ADB with the following commands:

$ adb -s e99b50ed shell /system/bin/screencap -p /sdcard/screenshot.png
$ adb -s e99b50ed pull /sdcard/screenshot.png screenshot.png

Once the application is installed, how about debugging it? The Android toolkit allows you to debug applications entirely through the command line. The ADB process can bridge debugger commands to the Android Runtime, by the means of port forwarding. Let us see how to do that.

Launch the application in your device and then retrieve the process ID in your device:

$ adb -s e99b50ed jdwp
27496

JDWP stands for Java Debug Wire Protocol, a standard defined for Java debuggers, and which the Android platform implements, albeit in limited form. The technique we are going to use basically uses ADB to connect a local instance of jdb (the standard Java debugger distributed with the Java SDK) to the process running in the device.

The last command we ran returns the number of the process in the Android Runtime of the device. We are going to use that number to connect all the pieces together.

First, let us set a debug bridge between jdb and the device:

$ adb forward tcp:7777 jdwp:27496

Then, launch the Java debugger and start a debugging session:

$ jdb -sourcepath src -attach localhost:7777

A short debugger session then looks more or less like this:

Set uncaught java.lang.Throwable
Set deferred uncaught java.lang.Throwable
Initializing jdb ...
> stop in training.akosma.commandline.MainActivity.OnCreate
Unable to set breakpoint training.akosma.commandline.MainActivity.OnCreate : No method OnCreate in training.akosma.commandline.MainActivity
> stop in training.akosma.commandline.MainActivity.onCreate
Set breakpoint training.akosma.commandline.MainActivity.onCreate
>
Breakpoint hit: "thread=main", training.akosma.commandline.MainActivity.onCreate(), line=12 bci=0
12            super.onCreate(savedInstanceState);

main[1] list
8        /** Called when the activity is first created. */
9        @Override
10        public void onCreate(Bundle savedInstanceState)
11        {
12 =>         super.onCreate(savedInstanceState);
13            setContentView(R.layout.main);
14        }
15    }
main[1] next
>
Step completed: "thread=main", training.akosma.commandline.MainActivity.onCreate(), line=13 bci=3
13            setContentView(R.layout.main);

main[1] list
9        @Override
10        public void onCreate(Bundle savedInstanceState)
11        {
12            super.onCreate(savedInstanceState);
13 =>         setContentView(R.layout.main);
14        }
15    }
main[1] help
** command list **
<SNIP>
main[1] cont
> quit

The previous listing shows several debugger commands:

  • stop in to create a breakpoint.

  • list to show the current status of the instruction pointer upon hitting a breakpoint.

  • next to "step over" to the next instruction.

  • cont to continue the execution.

  • help to learn more about other commands.

Quit Android Studio

If you are not able to perform the command-line debugger steps delineated above, make sure to quit Android Studio, as it has it uses its own adb daemon and this might interfere with the operations.

2.6. Logcat and pidcat

If you use the command line frequently, you will start missing the logcat output displayed by Android Studio at the bottom of the IDE. If that is the case, and you are using (as you should) logcat commands in your application, you should install then pidcat, a logging tool that provides color output in the terminal, and which can be restrained to only display the logs for the application you are interested in.

pidcat fulfills the same role as libimobiledevice or deviceconsole for iOS.

You can install it very easily using Homebrew: brew install pidcat. Once installed, just call it using this command:

$ pidcat training.akosma.pidcatexample

The console should display something similar to the following:

Example output with pidcat
Figure 34. Example output with pidcat

The pidcat help text shows that the tool allows to filter entries by verbosity level or by device or emulator, among other options.

$ pidcat --help
usage: pidcat [-h] [-w N] [-l {V,D,I,W,E,F,v,d,i,w,e,f}] [--color-gc]
              [--always-display-tags] [--current] [-s DEVICE_SERIAL] [-d] [-e]
              [-c] [-t TAG] [-i IGNORED_TAG] [-v] [-a]
              [package [package ...]]

Filter logcat by package name

positional arguments:
  package               Application package name(s)

optional arguments:
  -h, --help            show this help message and exit
  -w N, --tag-width N   Width of log tag
  -l {V,D,I,W,E,F,v,d,i,w,e,f}, --min-level {V,D,I,W,E,F,v,d,i,w,e,f}
                        Minimum level to be displayed
  --color-gc            Color garbage collection
  --always-display-tags
                        Always display the tag name
  --current             Filter logcat by current running app
  -s DEVICE_SERIAL, --serial DEVICE_SERIAL
                        Device serial number (adb -s option)
  -d, --device          Use first device for log input (adb -d option)
  -e, --emulator        Use first emulator for log input (adb -e option)
  -c, --clear           Clear the entire log before running
  -t TAG, --tag TAG     Filter output by specified tag(s)
  -i IGNORED_TAG, --ignore-tag IGNORED_TAG
                        Filter output by ignoring specified tag(s)
  -v, --version         Print the version number and exit
  -a, --all             Print all log messages

This is an invaluable tool to be able to quickly scan for specific values in the logger output of your applications. A similar tool, but this time with a graphical user interface for macOS is LogCat.

2.7. NSLogger

Although PID Cat is useful, sometimes it is not possible or desirable to debug application instances individually using the USB cable. For more complex debugging scenarios, NSLogger is a useful option.

NSLogger allows developers to gather logging information from running applications in the local network. To do that, developers must include the NSLogger NSLogger Android client in their applications. When the application runs, the code automatically tries to send all of its logging information to an application running on a Mac on the same local network as the devices. Developers can then watch live the updates of their applications and thus to troubleshoot problems in specific devices.

To install NSLogger in your own app, at the time of this writing there is no simple mechanism (like, for example, using Gradle.) The code of NSLogger must be included in the application, but since the linkage is done through static methods and properties, the Kotlin compiler can strip the code out of the application if not used (for example, in Release builds.)

Download the zip from the main Github repository of NSLogger and copy the folder inside of the Client Logger/Android/client-code/src into the app/src/main/java folder of your own application. Android Studio should detect the new code automatically.

Copy the files in the Client Logger/Android/example/com/example/ folder using Finder and paste them in Android Studio as part of your application.

Add the following permissions to your application in the AndroidManifest.xml file:

Permissions for NSLogger
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
    <uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />

Include your logging code, either in your custom subclass of the Application class in your project, or in your activity, to activate logging.

Sending NSLogger calls
        if (Debug.D) {
            Debug.enableDebug(application, true)
            // change to your mac's IP address, set a fixed TCP port in the Prefs in desktop NSLogger
            Debug.L?.setRemoteHost("192.168.1.115", 50007, true)
            Debug.L?.LOG_MARK("MainActivity startup")
        }

        button.setOnClickListener { Debug.L?.LOG_UI(0, "Button clicked") }

        seekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
            override fun onProgressChanged(seekBar: SeekBar, i: Int, b: Boolean) {
                Debug.L?.LOG_APP(0, "SeekBar changed")
            }

            override fun onStartTrackingTouch(seekBar: SeekBar) {
                Debug.L?.LOG_NETWORK(0, "SeekBar onStartTrackingTouch")
            }

            override fun onStopTrackingTouch(seekBar: SeekBar) {
                Debug.L?.LOG_SERVICE(0, "SeekBar onStopTrackingTouch")
            }
        })
Change the IP address to match the one where the client NSLogger application is running.

Before launching your application in Android Studio, make sure to download and launch the NSLogger desktop viewer application in your Mac. You should configure so that the port used by the client code is the same as the one used in the desktop viewer.

NSLogger desktop viewer configuration
Figure 35. NSLogger desktop viewer configuration

Having done this, and after adding some logging calls in your code, launching and using the application should automatically open a new NSLogger window in your Mac, displaying something similar to the contents of the following screenshot.

NSLogger application running
Figure 36. NSLogger application running
The contents of the NSLogger macOS application window can be saved into a file with the .nsloggerdata extension. The source code of this book includes a sample NSLogger file in the Debugging/NSLogger folder.

2.8. Stetho

Stetho is a tool created by Facebook to debug Android applications using the Google Chrome debugging tools. It is very simple to use and can be helpful to inspect and edit the user interface of applications, either on the device or on an emulator.

Follow along

The code of this section is located in the Debugging/StethoSample folder.

To use it, just add the required dependencies in the module Gradle file:

Stetho dependencies in the module Gradle file
implementation 'com.facebook.stetho:stetho:1.5.0'
implementation 'com.facebook.stetho:stetho-js-rhino:1.5.0'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"

After adding the dependencies, create a subclass of android.app.Application in your project:

Application subclass
import android.app.Application
import com.facebook.stetho.Stetho

class StethoSampleApplication : Application() {

    override fun onCreate() {
        super.onCreate()
        Stetho.initializeWithDefaults(this)
    }
}

Of course, you need to declare this new subclass in your AndroidManifest.xml file:

AndroidManifest.xml file with the new Application subclass
<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:supportsRtl="true"
    android:theme="@style/AppTheme"
    android:name=".StethoSampleApplication">

Launch the application, either on the device or the simulator, and open Google Chrome. Navigate to chrome://inspect/#devices and you should see something similar to the following image:

Google Chrome showing available devices for debugging
Figure 37. Google Chrome showing available devices for debugging

Clicking on the "inspect" link will open a developer tool window, which will be extremely familiar to those using Google Chrome for frontend web development. This editor can be used to inspect the UI of the application, drilling down until finding the right component, and can be used to modify the state of the interface as shown in the screenshot below:

Editing the UI using Stetho
Figure 38. Editing the UI using Stetho

Stetho can be used to inspect the state of SQLite databases, but there is a very useful module available to inspect the state of Realm databases.

2.9. Summary

Android applications can not only be debugged on the Android Studio IDE, but also on the command line; remember that Android Studio can be seen as a huge user interface built on top of Gradle and ADB. Other tools, such as NSLogger, Stetho and pidcat, provide additional services for inspecting the behavior of applications in different devices.

Part 2: User Interfaces

Getting and Reacting to User Input

Good looks are fundamental for any successful application, and this is true for both iOS and Android. The good news is that both system share a lot of commonalities, including drawing APIs that looks incredibly similar. This part will explain the Android graphics subsystem, including the view hierarchies, the APIs and other topics.

3. User Interface

Android uses the same basic input interface as iOS; a touchscreen. Through this interface, users are able to manipulate and interact with widgets such as buttons, scrollbars, panes and menus, with a sense of physicality very much like the one offered by UIKit and its related frameworks.

In this chapter we are going to learn how to build and organize user interfaces in our applications, concentrating our attention in the major building blocks of Android apps: Activities, Intents and Fragments.

3.1. TL;DR

For those of you in a hurry, the table below summarizes the most important pieces of information in this chapter.

Table 7. User Interface in Android
Android iOS

UI design

Layout files

NIB/XIB/Storyboard

Controllers

Activity

UIViewController

Callbacks

Anonymous Classes

IBAction

Views

android.view.View

UIView

Connecting views

findViewById(R.id.xxxxx)

IBOutlet

Text fields

EditText

UITextField

Buttons

Button

UIButton

Text labels

TextView

UILabel

Translatable strings

strings.xml

Localizable.strings

Navigation between controllers

Intent

Storyboard Segue

UI decomposition

Fragment

Children UIViewController

Serialization

Parcelable

NSPropertyListSerialization

Dialog boxes

AlertDialog

UIAlertController

3.2. UI Design Guidelines

Material Design is the current visual language that Google has created to unify the visuals and interactions throughout their complete suite of products, on the web, on the desktop and of course on mobile devices.

This book is definitely not a book about visual design (and if you can tell through my UML diagrams, I can say that design in general is not one of my strenghts!) but it is important to understand the underlying principlies behind Material Design.

Google created Material Design with the following principles in mind:

Material is the metaphor

A material metaphor is the unifying theory of a rationalized space and a system of motion.

Bold, graphic, intentional

The foundational elements of print-based design – typography, grids, space, scale, color, and use of imagery – guide visual treatments.

Motion provides meaning

Motion respects and reinforces the user as the prime mover.

Of course, this should come as no surprise to any seasoned iOS developer; Apple itself is well known for having created visual guidelines for their own operating systems (starting with macOS and following with iOS, watchOS and tvOS) for a long time.

These guidelines, as the name suggest, provide designers and developers with a common language, enabling teams to discuss and elaborate visual architectures for their applications.

I strongly suggest the reader of these lines to spend some time browsing the Google Material Design website in order to understand the paradigms and the ideas behind the different visual elements that make up an Android application.

3.3. Android Support Library

Historically, the characteristic of Android that frightens most iOS developers is the sheer diversity of devices and versions of Android available in the wild. The technical press usually refers to this issue as the "fragmentation" of the Android world… but as with many things in the press these days, it is safe to say that these claims (and the fears generated as a consequence) are overrated.

Early in the development of Android, Google realized that application developers should be able to support lots of different devices, with different screen sizes (such as tablets and smartphones of all sizes) and resolutions. This situation led to two very important additions to the Android toolkit back in 2011:

  1. Fragments.

  2. The Support Library.

The Support Library, initially known as the Android Compatibility Package, allows applications running in older versions of Android to enjoy the UI paradigms and the features brought to the system in new versions of Android.

This library is available to Android developers through the Android SDK, and is distributed to users through the Play Store; this means that Android devices that include the standard Google Play Store will always have the Support Library installed, and this enables all applications to run seamlessly in all devices, starting in Android 2.3 (API level 9) and higher.

Throughout this book, we are going to use this library extensively. The package is named android.support and all of our applications will inherit from android.support.v7.app.AppCompatActivity instead of the standard Activity class provided by Android.

3.4. Activities

By far, the most important building block of Android applications are activities. They fulfill a similar role to that of UIViewController instances in iOS, that is, to manage the display of a screenful of data at any single time.

Android Studio allows developers to build applications around the Activity paradigm, creating and removing activities for each of the specific tasks that are required by the application. Activities are a powerful architectural mechanism for organizing and encapsulating your code.

Activities must be self-contained and have as few dependencies from other activities as possible. This will allow you to reuse them in the same or in other applications easily.

The Activity Stack

In every Android application, there is a default activity that is launched and displayed by default when the application starts. This is similar to the "entry point" defined in iOS Storyboards. The same way you can move the arrow that represents the entry point of the Main storyboard in an iOS application, you can specify the default activity for any Android application as a simple entry in the AndroidManifest.xml file:

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="training.akosma.basic">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
    </application>

</manifest>

However, Android being different from iOS, it also means that the way activities are connected to each other is also different. In Android, all activities are included by default in a default navigation stack , and any activity can be launched independently of the other activities bundled in an application.

For iOS developers, it is better to think that the operating system holds a system-wide UINavigationController instance that spans accross all applications; every time the user launches an application, the activity is "pushed" to this global navigation stack. When the user presses the Back button (which is usually available in most Android devices by default as a hardware button) the current activity is "popped" from the stack, and the device returns to the previous state of operation, whichever that is.

This means that if your application launches an activity from the calendar application, and this one in turn launches another activity, say for example a contact from the address book, then when the user presses the back button twice, the current activity will be again your own activity, the one that started this chain of operations.

A Basic Application

In the source code bundled with this book, please open the Android Studio project located in the UI/Basic folder. This is a very simple Android application with a single activity, that shows how to use an activity as a simple controller for the UI of your application.

To recreate the application by yourself, follow these steps:

  1. Create a new Android Studio application. Select the default options, including the "Empty Activity" template.

  2. When Android Studio is ready, you open the activity_main.xml layout file and delete the label that appears on top of the screen.

  3. Using your mouse, drag three components to the UI, in the following order: a plain text EditText, a Button, and a TextView widget. You should see them in the palette at the left side of the editor panel, a familiar sight for those used to Interface Builder.

  4. Select the EditText component in the designer, and on the right side of the editor you should see a properties panel. Change the ID to nameEditText, change the "hint" property to Enter your name and touch "Greet" and remove the value of the "text" property.

  5. Select the Button component in the designer, and on the properties panel change the properties as follows: set the ID to greetButton, set the text to Greet, and set the onClick event to button_onClick.

  6. Select the TextView component in the designer, and set the properties as follows: ID to greetingTextView and remove any value in the "text" property.

As you can see, the Android UI designer properties panel works in a very similar way as that of Interface Builder; it organizes the properties following the inheritance chain; the EditText properties appearing on top of those defined in the TextView class.
  1. Launch your application on the emulator or, better yet, connect your device to your Mac and use it to launch the application. You should see the widgets displayed according to the layout created in the designer, but for the moment you can not do anything on the user interface. Clicking the Greet button yields nothing. It is time to enter some code.

  2. Double click on the MainActivity.kt file in the project browser on the left of the Android Studio window (if you have not done that already.) We are going to add a bit of code to that file, until it looks like the source code below.

MainActivity.kt
class MainActivity : AppCompatActivity() {
    (1)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    (2)
    fun button_onClick(view: View) {
        name = nameEditText.text.toString()
        greetWithHtml()
    }

    private fun greetWithHtml() {
        val text = "Hello <span style='color: blue;'><b>$name</b></span>"
        var html: Spanned
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            html = Html.fromHtml(text, Html.FROM_HTML_MODE_LEGACY) (3)
        } else {
            @Suppress("DEPRECATION")
            html = Html.fromHtml(text)
        }
        greetingTextView.text = html
    }
}
1 Just like UIViewController instances, Activities have a well-defined lifecycle . They have several methods that are called at specific moments in their lifetime. This method, onCreate() is roughly equivalent to viewDidLoad in iOS, as it is also called when the instance is created in memory.
2 In addition to using the XML layout to assign event handlers, you can also add them using simple Kotlin lambdas assigned via the setOnClickListener method. Moreover, the kotlin-android-extensions plugin allows to create automatically the connections between variables in the code and widgets in the layout.
3 You can easily display HTML code directly in your TextView instances. Use the android.text.Html.fromHtml() method and assign the result (a Spanned value) to the text property.
The R class is autogenerated, and can sometimes get out of sync with the resources of the project. If that happens, a quick solution is to use the Build  Clean Project menu item in Android Studio.

Activity States

As you have just seen, Activities have a lifecycle very similar to that of the UIViewController class. The following diagram shows in detail the various states and methods called in each state change, as the Activity instance is created, modified and disposed.

diagram activity states
Figure 39. Activity State Diagram

Inspecting the UI

When inspecting the user interface using the Hierarchy Viewer bundled in the Android Device Monitor, the screen shown in the image below appears.

Inspecting the user interface of the Basic application
Figure 40. Inspecting the user interface of the Basic application

Android Widgets

In the Basic application built above, we have used several different widgets for the user interface.

Sometimes Android UI widget classes have strange sounding names for those of us coming from the iOS world. The naming conventions are different, and as such it is important to learn them.

In the "Basic" application above we have used the TextView, EditText and Button classes, which are all related through inheritance.

diagram edittext classes
Figure 41. EditText Hierarchy Diagram

The TextView class is a subclass of android.view.View, the base class of all visible things in Android. Just like in iOS, a View is a representation of a rectangle on the screen, including everything that is drawn inside.

Instances of the android.view.View class, unlike UIView instances, cannot have children widgets; this is only possible at the level of the android.view.ViewGroup class, itself a subclass of android.view.View. Remember this when you will want to create your own complex view systems.

Many of the most important subclasses of android.view.View are shown below.

diagram view classes
Figure 42. View Hierarchy Diagram

Finally, when a developer wants to set or get the text of a TextView, it turns out that the setter and getter do not take String instances, but rather reference the CharSequence interface. In the case of the EditText, the getText() method returns an object implementing the Editable interface.

As a general design decision, using interfaces in your method signatures it is always a good idea. It makes your APIs more flexible and extensible.
diagram string classes
Figure 43. String Hierarchy Diagram

Optimizing the Project with Lint

Let us enhance now this "Basic" application a little bit. First of all, we are going to select the Analyze  Inspect Code… menu item. The dialog shown in the screenshot below will appear.

Android Studio Code Inspection Dialog
Figure 44. Android Studio Code Inspection Dialog

Android Lint is a tool roughly equivalent to the Clang Static Analyzer, available in Xcode in the Product  Analyze… menu item. We will run it with the default options, and among the many improvements, there are a few "low hanging fruits" which we can fix right now.

The output of the tool appears at the bottom of the screen.

Android Studio Lint Results
Figure 45. Android Studio Lint Results

The "Inspection" pane at the bottom of the Android Studio window contains the list of problems found in the project, and when selecting any of these items, an explanation is shown on the left side.

String Files

Now let us fix some of these problems.

The first one is actually highlighted in the previous image, and has to do with the fact that we have hardcoded strings in the layout file when we created our "Basic" application. This is hardly a good idea; first of all, we might want to make our application available to users in other languages in the future, and this is where the strings.xml file comes in handy.

The strings.xml resource file is more or less equivalent to the Localizable.strings file used in Cocoa to provide international versions of all the strings included in the application. There is, however, a nice difference; the strings referenced in strings.xml are immediately parsed by Android Studio, and they are available through the autogenerated R class.

To solve the problem in the Lint Inspection pane, select it with your mouse and hit the + keyboard shortcut. This will open the file where the problem resides and will scroll automatically to the required line of code. Replace the value Enter your name and touch "Greet" with the text @string/edit_hint. At first Android Studio will complain that the key is non existent, but we are going to fix that immediately.

Open now the strings.xml file; for that, let us use another handy keyboard shortcut: Shift++O. This opens a "Quick Open" dialog that allows you to open any file on the project. Add the required key on the file, and then do the same with the text on the Greet button.

Your strings file should now look like shown below.

A simple strings.xml file
<resources>
    <string name="app_name">Basic</string>
    <string name="edit_hint">Enter your name and touch "Greet"</string>
    <string name="greet_button">Greet</string>
</resources>

One of the nice things of concentrating strings inside of the resources file is that Android Studio proposes to "autocomplete" most string placeholders with values taken out of it. This simplifies the handling of these values greatly, and makes it easy to translate all the strings in a project in one operation, usually before publication.

One final note about string files: to avoid (mis)handling the XML code in the file, Android Studio provides a nice interface that can be used to edit strings instead. This can be accessed by clicking on the "Open editor" link that appears on the top right of the strings.xml file tab in Android Studio.

Android Studio Translation Editor
Figure 46. Android Studio Translation Editor

Handling Orientation Changes

Whether you are using now a physical device or an emulator, try now the following: first, enter your name in the EditText field, hit the Greet button and then change the orientation of the device.

See what happened? The value in the TextView suddenly disappeared. This is something that can be very puzzling for iOS developers. In iOS, the state of a UIViewController is kept between orientation changes, and the controller just receives a few callbacks to be notified of the change.

In the case of Android, the behavior is radically different; when the user rotates the device, the activity is destroyed and disposed, and a new one is created and displayed.

The only possible solution for an Android developer, given these constraints, is to save the current state of the application, and to reload it accordingly if required. Let us add some code to solve this problem.

MainActivity keeping state
    (2)
    if (savedInstanceState != null) {
        name = savedInstanceState.getString(KEY)
        greetWithHtml()
    }
(3)
override fun onSaveInstanceState(outState: Bundle?) {
    super.onSaveInstanceState(outState)
    outState?.putString(KEY, name)
}
companion object {

    (1)
    private val KEY = "name"
}
1 This is the key used to store the current value of the TextView instance when the activity is about to be destroyed.
2 When Android creates the activity, it verifies whether there was already a stored value; if this is the case, then use it to reset the UI to the previous state.
3 This method is called right before the Activity is destroyed; we use the KEY string to store the current value of the TextView widget before being called upon oblivion.

3.5. Intents

The basic communication mechanism between activities is the android.content.Intent class. Whenever an activity wants to start another activity, or to communicate with another process in the device, it will always use an instance of the Intent class.

This architecture has no equal in the world of iOS applications, where the communication between view controllers is usually strongly coupled; this has the advantage of a simpler programming model, relatively easier to understand for newcomers, but it also leads to tangled architectures, where it is almost impossible to reuse controllers in different contexts.

Thanks to intents, Android activities can truly be independent from each other at every time, which helps in the creation of decoupled architectures, with high degrees of testability and reuse.

Follow along

The code of this section is located in the UI/Age folder.

There are three use cases for intents in Android:

  • Explicit intents specify the class of the activity to launch, and are commonly used inside of a single application to navigate from screen to screen.

  • Implicit intents are used to open system-wide services, such as asking the built-in browser to open a web page or to search for a contact in the contacts database.

  • Return values from an activity to the "previous" one in the activity stack are also Intent instances, holding on to the data that must be passed back.

Let us learn now how to use Intent instances to open other activities.

Implicit Intents

Implicit intents are the simplest. Just specify the action you would like to launch (in this case, android.content.Intent.ACTION_VIEW) and the parameter (in this case, a URL.) This implicit intent has the net result of opening the default web browser in the device.

Using implicit intents
webButton.setOnClickListener {
    val intent = Intent(ACTION_VIEW, Uri.parse("https://akosma.training"))
    startActivity(intent)
}

The startActivity() method of the Activity class takes an Intent instance as a parameter, and asks the operating system to do the rest.

Explicit Intents

The simplest use case consists in navigating from one activity to another, that is, pushing a new activity on the device navigation stack.

Using explicit intents
ageButton.setOnClickListener {
    val i = Intent(this, AgeActivity::class.java)
    i.putExtra("age", age)
    startActivityForResult(i, 0)
}

In this case we use the startActivityForResult() method of the Activity class, because we are expecting the AgeActivity class to return a simple value.

Returning Values

The AgeActivity class in our example contains a SeekBar that the user can slide from left to right to choose a suitable age. When the user presses the "Back" button (or, alternatively hits the "Finish" button) the current activity is popped off the current stack and the setResult() method is called. This method takes an Intent as a parameter, one that contains the data to be passed to the previous activity.

Returning data with an Intent
private fun notifyAge() {
    val data = Intent()
    data.putExtra("age", age)
    setResult(Activity.RESULT_OK, data)
}

When an activity calls setResult() the one that has requested it through the startActivityForResult() will be notified of this, and the onActivityResult() callback will be activated.

Reading a result value
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
    super.onActivityResult(requestCode, resultCode, data)
    if (resultCode != Activity.RESULT_OK) {
        return
    }
    age = data.getIntExtra("age", DEFAULT_AGE)
    displayAge()
}

Thanks to this simple architecture, data can flow from one activity to another freely and simply. The structure of the data is, of course, part of an implicit contract that should be documented and specified – and tested, if at all possible.

Another example of requesting data from the operating system is shown below, where we ask the user to select a contact from its device and we display the name on our application.

Requesting and showing a contact
contactsButton.setOnClickListener {
    val intent = Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI)
    startActivityForResult(intent, PICK_REQUEST)
}
public override fun onActivityResult(request: Int, code: Int, data: Intent) {
    super.onActivityResult(request, code, data)

    if (request == PICK_REQUEST && code == Activity.RESULT_OK) {
        val contactData = data.data
        val resolver = contentResolver
        var c: Cursor? = null
        if (contactData != null) {
            c = resolver.query(contactData, null, null, null, null)
        }
        if (c != null && c.moveToFirst()) {
            val column = ContactsContract.Contacts.DISPLAY_NAME
            val index = c.getColumnIndex(column)
            val name = c.getString(index)
            textView.text = name
            c.close()
        }
    }
}

companion object {
    private val PICK_REQUEST = 0
}
Data Providers

The contacts example we saw in the previous section is just one example among a large collection of generic data providers. We are going to learn more about Android data providers in chapter "Storage".

3.6. Fragments

Remember iOS "Universal Applications"? These are, according to Apple, iOS applications that can run both in the "form factor" of the iPhone or on that of the iPad. They are usually quite easy to create on iOS; just create different storyboards for each device, wire the scenes in your storyboards accordingly, and iOS takes care of the rest. If the application is running on an iPhone, then the iPhone storyboard will be displayed; if it is on an iPad, then the iPad storyboard will be taken into account.

Android works in pretty much the same way; the difference is that Android applications can have different layout files for different screen sizes, and even for different resolutions, orientations, and many other factors!

However, no matter how many layout files you have in your projects, Activities still take all the available screen space, every single time. Android does not allow many activities to share the current screen. Hence, a different solution was required. And this solution is called fragments.

Fragments are one of the most fundamental visual building blocks in Android. They allow developers to create flexible user interfaces that work differently in different devices, yet they are distributed as the same application in the Play Store.

Fragments are, in a sense, like small UIViewController instances that are children of a bigger, "container" UIViewController. You can compose complex user interfaces in iOS by nesting controllers inside of other controllers. Each contains its own view logic, and talks to other controllers using well-defined interfaces (like notifications, delegate protocols or other mechanisms.)

In Android, Fragments always exist inside an Activity. Activities can have one or many fragments, each containing its own view logic.

Follow along

The code of this section is located in the UI/Fragments folder.

We are going to create now a small application that displays the same data in different ways depending on whether it is running on a tablet or on a smartphone; we are actually going to create something similar to a UISplitViewController!

The first step consists in creating the individual fragments for our application. We need a fragment that displays a list of items, and another fragment that displays just one of the items; the classical "master & detail" user interface paradigm.

We are also going to need two activities; one is the MainActivity, the root activity of the application, whether it is running on a smartphone or a tablet. If the application is running on a tablet, it will display both fragments at the same time. If it is running on a smartphone, that means that the "detail" fragment will not be visible, and then the DetailActivity, itself containing the DetailFragment will be called using an intent.

These are the layouts we need for both activities; as you can see, there are two layouts for the MainActivity, which has two different "look & feels":

MainActivity layout file for smartphones
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
             android:layout_width="match_parent"
             android:layout_height="match_parent">

    <fragment
        android:id="@+id/listFragment"
        class="training.akosma.fragments.ListFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</FrameLayout>
MainActivity layout file for tablets
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="horizontal"
              android:layout_width="match_parent"
              android:layout_height="match_parent">

    <fragment
        android:id="@+id/listFragment"
        class="training.akosma.fragments.ListFragment"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1" />

    <fragment
        android:id="@+id/itemFragment"
        class="training.akosma.fragments.DetailFragment"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="2" />
</LinearLayout>

The DetailActivity class only needs one layout:

DetailActivity layout file
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_detail"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="training.akosma.fragments.DetailActivity">

    <fragment
        android:id="@+id/itemFragment"
        class="training.akosma.fragments.DetailFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</RelativeLayout>

In terms of code, the most complex class in the project is the ListFragment class, which actually uses a android.support.v7.widget.RecyclerView instance to display a list of strings. The RecyclerView class is the closest thing in Android to an iOS UICollectionView or UITableView, and we are going to learn more about it in the next few chapters. For the moment you only need to know that the Adapter class inside of it is used as a data source, just as you would do it in iOS.

The ListFragment class also defines a "callback protocol" so that users of that fragment are notified of events on the RecyclerView. The root activity of the project is responsible for the coordination of the work between the fragments, and we can see that at work in the code below.

The MainActivity class coordinating the fragments
override fun onItemSelected(value: String?) {
    Toast.makeText(this, value, Toast.LENGTH_SHORT).show()

    val detailFragment = supportFragmentManager.findFragmentById(R.id.itemFragment) as DetailFragment?
    if (detailFragment == null || !detailFragment.isInLayout) {
        val intent = Intent(this, DetailActivity::class.java)
        intent.putExtra(DetailFragment.PARAMETER, value)
        startActivity(intent)
    } else {
        detailFragment.update(value ?: "")
    }
}

As you can see, here we check for the existence of the detail fragment in the layout of the activity; if we are running the app in a smartphone, then the layout will not include that fragment, and the check will yield a falsy value; hence, we just create an Intent and ask the operating system for a new activity, in order to display the value that the user selected on the list.

On the other hand, if the app is running on a tablet, then the layout file that has been loaded by the operating system already includes that fragment, and thus the only thing we need to do is to simply update its value.

Coordination via LocalBroadcastManager

In the previous example we used a direct communication pattern between the list fragment and its host activity. You can use the android.support.v4.content.LocalBroadcastManager class for a more disconnected system of interaction, one that is suspiciously similar to the one provided in Cocoa by the NSNotificationCenter and NSNotification classes.

Local broadcast are available through the support library, and they guarantee privacy since no app can accept local broadcasts from any other application, and no other application can listen to the local broadcast of other apps.

Follow along

The code of this section is located in the UI/Notifications folder.

The first thing we need to do is to make the MainActivity class, the one that coordinates the communication between fragments, to register itself as a listener of a local broadcast.

Create an android.content.BroadcastReceiver object and override its onReceive() method, as shown in the following listing.

A local broadcast receiver
private val receiver = object : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        receive(intent)
    }
}

private fun receive(intent: Intent) {
    val value = intent.getStringExtra(Constants.DATA_KEY)
    Toast.makeText(this, value, Toast.LENGTH_SHORT).show()

    val detailFragment = supportFragmentManager
            .findFragmentById(R.id.itemFragment) as DetailFragment?
    if (detailFragment == null || !detailFragment.isInLayout) {
        val showDetailIntent = Intent(this, DetailActivity::class.java)
        showDetailIntent.putExtra(DetailFragment.PARAMETER, value)
        startActivity(showDetailIntent)
    } else {
        detailFragment.update(value)
    }
}

Then register this receiver object in onCreate().

Registering a local broadcast receiver
val filter = IntentFilter(Constants.NOTIFICATION_NAME)
val manager = LocalBroadcastManager.getInstance(this)
manager.registerReceiver(receiver, filter)

Needless to say, you should deregister it on onDestroy().

Deregistering a local broadcast receiver
override fun onDestroy() {
    val manager = LocalBroadcastManager.getInstance(this)
    manager.unregisterReceiver(receiver)
    super.onDestroy()
}

Finally, let us modify the ListFragment class so that local broadcasts are sent every time that the user taps on an item.

Sending a local broadcast
override fun onClick(view: View) {
    val intent = Intent(Constants.NOTIFICATION_NAME)
    intent.putExtra(Constants.DATA_KEY, item)
    val activity = activity
    val manager: LocalBroadcastManager
    manager = LocalBroadcastManager.getInstance(activity!!)
    manager.sendBroadcast(intent)
}

3.7. Layouts

Android is an incredibly popular operating system, running in billions of devices all over the planet. These devices can have dramatically different sizes, ranging from small smartphones to large tablets.

When designing Android applications, it is important to never hardcode the positions of the widgets on the screen, but rather to place them in relative positions, and then to test the application in as many devices as possible.

Android applications use XML files to define the user interface shown in activities and fragments. These XML files use different "layouts" to place the widgets in similar ways across all devices. There are several kinds of layouts available, and this chapter will describe the most common ones.

Layouts available in Android Studio
Figure 47. Layouts available in Android Studio
Follow along

The code of this section is located in the UI/Layouts folder.

What is a Layout?

Android layouts are views which have the characteristic of being able to have children. By default, Android views cannot have children, and the first view that presents this characteristic in the class hierarchy is the ViewGroup class. All layouts inherit from the ViewGroup class, as shown in the diagram below. Also, some common widgets, such as the RadioGroup and the DatePicker class inherit from layouts, helping their complex structures.

diagram layout classes
Figure 48. Layout Hierarchy Diagram

Since layouts are ViewGroup subclasses, they can contain other layouts as well. This means that complex user interfaces can be built by composition, using different layouts where they make sense, one inside the other.

Constraint Layout

By default, new Android application projects will use the Constraint Layout for their user interface. This is the latest and the most powerful layout system available in Android, and it is very similar to Auto Layout in iOS.

Cassowary Algorithm

Both iOS' Auto Layout and Android Constraint Layout derive from the same mathematical foundation, the Cassowary linear arithmetic constraint solving algorithm.

The idea behind the Constraint Layout is to describe the positions of the widgets on the screen using equations that are solved at runtime; all of the widgets are positioned in such a way that these equations are solved and correct. For example, we might want a text label with a title to be centered on the screen, but always placed 42 points below the title bar; then we would want a button to have a flexible width but a fixed height, and to be placed always 30 points below the previous text label; and so on. Android will solve this equations at runtime for you.

To make your life easier, Android provides a visual layout editor.

Constraint Layout Editor
Figure 49. Constraint Layout Editor

In the XML file you can see the results of your manipulation.

Constraint Layout XML
<Button
    android:id="@+id/linearButton"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginEnd="64dp"
    android:layout_marginStart="8dp"
    android:layout_marginTop="80dp"
    android:text="Linear"
    app:layout_constraintEnd_toStartOf="@+id/relativeButton"
    app:layout_constraintHorizontal_bias="0.794"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/frameButton" />

Linear Layout

The Linear Layout is the simplest of all layouts available. It just places the widgets on the screen in the order in which they are defined in the XML file, either vertically or horizontally.

Linear Layout XML
<LinearLayout android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    xmlns:android="http://schemas.android.com/apk/res/android">

    <Button
        android:id="@+id/button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Button" />

    <CheckBox
        android:id="@+id/checkBox"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="CheckBox" />
Linear Layout Editor
Figure 50. Linear Layout Editor

Frame Layout

The Frame Layout blocks a part of the screen so that only one component can be drawn. The idea of the Frame Layout is to position a single child element, although it is technically possible to use the android:layout_gravity parameter of child views to place an item on a corner, at the center or at the middle of the layout. The example below shows how to do this.

Frame Layout XML
<Button
    android:id="@+id/button2"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Button"
    android:layout_gravity="center"
    />

<CheckBox
    android:id="@+id/checkBox2"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="CheckBox"
    android:layout_gravity="top|right"
    />
Frame Layout Editor
Figure 51. Frame Layout Editor

Grid Layout

As the name explains, the Grid Layout is used to place child views in a grid structure. This can be useful in many situations, like for example the buttons of a calculator; the example below shows how to create a simple grid layout resembling the keypad of a pocket calculator in a few steps.

Grid Layout XML
<Button
    android:id="@+id/button08"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_column="1"
    android:layout_row="0"
    android:text="8" />

<Button
    android:id="@+id/button09"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_column="2"
    android:layout_row="0"
    android:text="9" />
Grid Layout Editor
Figure 52. Grid Layout Editor

Relative Layout

The Relative Layout is used to place widgets relative to one another; it is a bit simpler to understand than the Constraint Layout, but it is also less powerful.

Relative Layout XML
<ProgressBar
    android:id="@+id/progressBar2"
    style="?android:attr/progressBarStyle"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_below="@+id/checkBox3"
    android:layout_marginEnd="14dp"
    android:layout_marginTop="25dp"
    android:layout_toStartOf="@+id/checkBox3" />

<Switch
    android:id="@+id/switch3"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignEnd="@+id/checkBox3"
    android:layout_below="@+id/progressBar2"
    android:layout_marginTop="27dp"
    android:text="Switch" />
Relative Layout Editor
Figure 53. Relative Layout Editor

3.8. Summary

Activities are the basic building block of Android applications. Every activity manages a screenful of information, just like UIViewController instances would on iOS. The UI of activities can be designed visually, just like with Interface Builder in Xcode. Attach event handlers to UI widgets directly using Kotlin lambdas, instead of using Java anonymous classes, which makes code easier to write, read and maintain.

Intents are objects used to launch other activities, either in this application or in any other application in the system. They provide the glue that allow applications to talk to each other.

Applications should be built around Fragments, to ensure their adaptation to other device sizes, such as tablets. In that sense, Activities should be seen as containers for fragments, allowing them to collaborate and to share information at runtime.

Finally, the whole operating system works like a giant UINavigationController instance, onto which Activity instances are constantly pushed and popped, and this explains the importance of the back button in Android devices.

4. Graphics

Android applications, just like their iOS counterparts, rely heavily in strong, bold, beautiful graphics to convey their meaning and to help users perform their tasks. In this chapter we are going to learn the basics of Android graphics – which, as we will see, are very similar to those of iOS – as well as a few techniques to gather complex events and drawing intricate graphics in the easiest possible way.

4.1. TL;DR

As usual, for those readers in a hurry, the table below summarizes the most important pieces of information in this chapter.

Table 8. Android Graphics
Android iOS

Framework

android.graphics

UIKit

Views

View

UIView

Coordinate system

Origin at top left

Origin at top left

Location on screen

LayoutParams

CGRect

Images

ImageView

UIImageView

Colors

Color (manipulates int!)

UIColor

Bezier curves

Path

UIBezierPath

Drawing method

onDraw()

draw()

Drawing context

Canvas

CGContext

Mark as "dirty"

invalidate()

setNeedsDisplay()

Gestures

GestureDetector

UIGestureRecognizer

Pinch gesture

ScaleGestureDetector

UIPinchGestureRecognizer

Affine Transformations

Matrix

CGAffineTransform

Simple animations

View.animate()

animate(withDuration:animations:)

Complex animations

android.animation.Animator

CAAnimation

Application-level memory warnings

Application.onLowMemory()

applicationDidReceiveMemoryWarning()

Activity-level memory warnings

Activity.onTrimMemory()

didReceiveMemoryWarning()

4.2. Graphics on Android

Let us begin our discussion about drawing on Android devices with a little bit of theory. This section will explain the coordinate system used in Android graphics, the units, the variety of screen densities, and will provide some background about how to solve some common memory problems that arise when handling large amounts of data.

Coordinate system

To draw, we not only need to know what to draw, but where to draw it. In iOS, the coordinate system has its origin – the (0, 0) point – at the top left of the screen. In Android, it is exactly the same.

Follow along

The code of this section is located in the Graphics/ScreenSize folder.

To learn more about the Android coordinate system we are going to create a small project with a main activity taking the whole screen; no status bar, no action bar, nothing. Just the activity.

To do that, no need to add any code; the XML resource files are enough. First we are going to modify the base application theme in res/values/styles.xml so that it looks like shown below:

Fullscreen application style
<resources>

    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="windowNoTitle">true</item>
        <item name="windowActionBar">false</item>
        <item name="android:windowFullscreen">true</item>
        <item name="android:windowContentOverlay">@null</item>
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

</resources>

We are going to take the basic style called Theme.AppCompat.Light.NoActionBar provided by Android and we are going to personalize it, removing the title bar and the action bar as required.

We are also going to remove all padding from the activity, modifying the res/values/dimens.xml and /res/values-w820dp/dimens.xml files:

Dimension specifications
<resources>
    <dimen name="activity_horizontal_margin">0dp</dimen>
    <dimen name="activity_vertical_margin">0dp</dimen>
</resources>

Our activity will contain a single TextView centered in the middle of the screen, and we are going to programmatically add other TextView instances on the screen, using something that all iOS developers know too well: absolute positioning.

Absolute positioning of views
val width = 300
val height = 300

val display = windowManager.defaultDisplay
val metrics = DisplayMetrics()
display.getRealMetrics(metrics)
val maxX = metrics.widthPixels
val maxY = metrics.heightPixels
val density = metrics.densityDpi

sizeLabel.text = """
    Screen: $maxX x $maxY px
    Density: $density dpi
    """.trimIndent()

val topLeftTextView = TextView(this)
topLeftTextView.text = "Top Left"
val topLeft = RelativeLayout.LayoutParams(width, height)
topLeftTextView.setBackgroundColor(Color.BLUE)
topLeftTextView.setTextColor(Color.WHITE)
topLeftTextView.setTypeface(null, Typeface.BOLD_ITALIC)
topLeftTextView.textSize = 20f
relativeLayout.addView(topLeftTextView, topLeft)

The final result of this operation is visible in the following figures, both real screenshots taken in devices running the application.

Screen dimensions in a smartphone
Figure 54. Screen dimensions in a smartphone
Screen dimensions in a tablet
Figure 55. Screen dimensions in a tablet
Do not use absolute positioning

The code above only serves the purpose of showing the sizes and position of elements on the screen, but views on Android screens should always be positioned relatively or using some kind of flow layout, like LinearLayout. This will allow your user interfaces to adapt gracefully to new screen sizes.

Units

The previous exercise shows values on the screen, in pixels and in dots per inch. Just like in iOS, the drawing system in Android is device independent, and as such it can accomodate and work in devices of all kinds. The following list provides the complete reference of units supported in Android.[1]

px

Pixels - corresponds to actual pixels on the screen.

in

Inches - based on the physical size of the screen. 1 Inch = 2.54 centimeters

mm

Millimeters - based on the physical size of the screen.

pt

Points - 1/72 of an inch based on the physical size of the screen.

dp or dip

Density-independent Pixels - an abstract unit that is based on the physical density of the screen. These units are relative to a 160 dpi screen, so one dp is one pixel on a 160 dpi screen. The ratio of dp-to-pixel will change with the screen density, but not necessarily in direct proportion. Note: The compiler accepts both "dip" and "dp", though "dp" is more consistent with "sp".

sp

Scale-independent Pixels - this is like the dp unit, but it is also scaled by the user’s font size preference. It is recommend you use this unit when specifying font sizes, so they will be adjusted for both the screen density and user’s preference.

The following table shows the relationship and major characteristics of these units.

Table 9. Android Graphical Units
Unit Description Units Per Physical Inch Density Independent Same Physical Size On Every Screen

px

Pixels

Varies

No

No

in

Inches

1

Yes

Yes

mm

Millimeters

25.4

Yes

Yes

pt

Points

72

Yes

Yes

dp

Density Independent Pixels

~160

Yes

No

sp

Scale Independent Pixels

~160

Yes

No

Use dp whenever possible

As a rule of thumb, always privilege the use of density-independent pixels (dip) whenever possible, just like you would use points instead of pixels in iOS.

Screen Densities

The following tables show the "classical" Android screen densities; please keep in mind that most modern Android smartphones and tablets fall into the xhdpi and xxhdpi classes, offering incredibly high levels of contrast and display. You should ideally test your app in the largest possible number of devices to ensure that your designs scale well into new size classes.

Table 10. Android Graphical Densities
Density Bucket Screen Density Physical Size Pixel Size

ldpi

120 dpi

0.5 x 0.5 in

0.5 in * 120 dpi = 60x60 px

mdpi

160 dpi

0.5 x 0.5 in

0.5 in * 160 dpi = 80x80 px

hdpi

240 dpi

0.5 x 0.5 in

0.5 in * 240 dpi = 120x120 px

xhdpi

320 dpi

0.5 x 0.5 in

0.5 in * 320 dpi = 160x160 px

xxhdpi

480 dpi

0.5 x 0.5 in

0.5 in * 480 dpi = 240x240 px

xxxhdpi

640 dpi

0.5 x 0.5 in

0.5 in * 640 dpi = 320x320 px

About color warmth and other issues

Designing beautiful graphics is a subject that falls outside of the scope of this book, but please keep in mind that different devices feature different screen technologies, and as such the warmth and gamut of the colors could vary sensibly from the drawing application to the device. Again, the only solution here is to test in a wide range of devices, from different manufacturers.

Out of Memory when Using Graphics

One of the most common problems when working with graphics in Android is the dreaded, never welcome, drastically horrid OutOfMemoryError, also referred to as "OOME" in the Java literature. Given the tight conditions in which Android applications run, it is entirely possible – actually, extremely likely – that you will encounter one of these errors in your life as a graphical Android developer. Allocate a huge android.graphics.Bitmap instance in memory, and you could end the life of your application right away.

The first and simplest solution could be to… well, load a smaller version of the same image. This advice seems silly but remember that most Android devices do not have big screens, and loading that 20 MB image all of a sudden could not be the best idea, particularly if a 50 KB version of the same image offers the same quality to the end user.

If you still have to load large images in memory, there are a couple of tricks you can use.

  1. You can add the android:largeHeap="true" property to your AndroidManifest.xml file. This will tell the operating system to pay attention to the fact that your application might need more memory than usual. However, doing this can slow down your application in some devices, because more memory means longer (and potentially more frequent) garbage collection cycles, which impact performance directly.

  2. Use java.lang.ref.SoftReference<> objects to hold your Bitmaps. SoftReference objects are guaranteed to be removed from memory before the system throws the dreaded OOME.

  3. Resample the image using BitmapFactory.decodeResource() (and, of course, save that image afterwards in the local storage of your device for it to be reused later.)

  4. Implement the onTrimMemory() method in your Activity subclasses; this method is the equivalent of the didReceiveMemoryWarning() method of the UIViewController class in UIKit.

  5. Add a subclass of the android.app.Application to your project, register it in your AndroidManifest.xml file and implement the onLowMemory() method, which is the equivalent of the applicationDidReceiveMemoryWarning(_:) method of the UIApplicationDelegate protocol in UIKit.

Follow along

The code of this section, showing how to use BitmapFactory.decodeResource() is located in the Graphics/LargeBitmap folder.

Checking for the current memory status of your device is quite straightforward.

Checking the current memory status of your device
val memoryInfo = availableMemory
val mem = humanReadableByteCount(memoryInfo.availMem, false)
var message = String.format("Available memory: %s", mem)

if (memoryInfo.lowMemory) {
    message += "\nLow memory!"
}

val builder = AlertDialog.Builder(this)
builder.setTitle("Memory status").setMessage(message)
val dialog = builder.create()
dialog.show()

The code below shows what kind of action to take when an activity receives low-memory warnings.

Receiving and reacting to a memory warning
override fun onTrimMemory(level: Int) {
    super.onTrimMemory(level)

    when (level) {
        // Called when the app went to the background
        ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN -> {  }

        // These happen at runtime, even if your app is
        // on the foreground. If CRITICAL, the operating
        // system will begin to kill processes.
        ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE,
        ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW,
        ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL -> {  }

        // This is even worse; this process is in the
        // list to be terminated as soon as possible.
        // This might be your last chance to survive.
        ComponentCallbacks2.TRIM_MEMORY_BACKGROUND,
        ComponentCallbacks2.TRIM_MEMORY_MODERATE,
        ComponentCallbacks2.TRIM_MEMORY_COMPLETE -> {  }

        // This is a generic low-memory level message.
        // Do your job. Release memory. Now.
        else -> {  }
    }
}

The code below shows some "last resort" measures, asking the operating system to trigger the JVM garbage collection. Not precisely a good idea in itself, but useful for the curious among you.

Reacting to a memory warning on the Application subclass
class LargeBitmapApp : Application() {
    override fun onLowMemory() {
        super.onLowMemory()
        System.runFinalization()
        Runtime.getRuntime().gc()
        System.gc()
    }
}

Remember to declare your custom Application subclass in the AndroidManifest.xml file! Otherwise it will not be taken into account.

Declaring a custom Application class and extending the JVM heap size
<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:supportsRtl="true"
    android:theme="@style/AppTheme"

    android:largeHeap="true"
    android:name=".LargeBitmapApp">

4.3. Custom Views

This is one of my preferred sections in this book: we are going to learn how to create a simple drawing application. This application will have three very simple features:

  1. Users can choose a color and a brush size

  2. They can draw freely on the screen.

  3. They can erase and start from scratch.

The most important point in this whole application is that the final code will be very small – no more than 100 lines of code for the View class where the drawing takes place!

Follow along

The code of this section is located in the Graphics/Draw folder.

You can see a screeshot of the application in action in the following picture.

Drawing application on a tablet
Figure 56. Drawing application on a tablet

The application consists of a single Activity which does not do anything else than wiring events to the different components in the UI; we have a Button, a SeekBar to select sizes, and a couple of RadioButton instances to select the color of the brush.

The core of the application is undoubtedly the DrawableCanvas class, itself a subclass of android.view.View.

The declaration of the DrawableCanvas class
class DrawableCanvas(context: Context, attrs: AttributeSet) : View(context, attrs) {

    private var strokeColor = Color.BLACK
    private var strokeWidth = 3.0f
    private var currentLine: Line? = null
    private var lines: ArrayList<Line> = ArrayList()

DrawableCanvas is a subclass of android.view.View, the most important class in the drawing system of Android. Every single component you can see on most Android apps (with the exception of those apps drawing their own widgets, like OpenGL games for example) is an instance of a subclass of View.

And the good news for iOS developers is that View is very similar to UIView in many ways. The biggest similarity is the fact that both include an overridable method that can be used to perform custom drawing on the screen; in the case of iOS it’s the UIView.draw() method; in the case of Android is the View.onDraw() method. Even the names are similar!

The snippet below shows how to draw lines on the screen, using the android.graphics.Path, which is in many ways the same thing as a UIBezierPath on iOS.

The DrawableCanvas.onDraw() method
override fun onDraw(canvas: Canvas) {
    super.onDraw(canvas)

    for (line in lines) {
        val path = line.path
        val paint = line.paint
        canvas.drawPath(path, paint)
    }
}

As you can see, the onDraw() method shares many points in common with its iOS counterpart:

  1. This method is never called directly by the user; the application Looper decides when and how to refresh the screen. The task of the developer is merely to include the drawing code, and nothing else.

  2. The method receives an instance of android.graphics.Canvas as a parameter, which looks suspiciously similar to a CGContext parameter received by UIView.draw(). Both represent an in-memory structure where visual changes can be made, and the Android operating system will take those changes and pass them to the GPU (if the current device has one) or the graphics subsystem, in order to refresh the screen. In this case we are calling canvas.drawPath() which, as the name implies, draws a particular path object on the screen, using a android.graphics.Paint object, with information about color, stroke width and other details.

Of course, the lines must be drawn by the user, and the DrawableCanvas class should react to the touch events generated by the user. We are going to override yet another method, in this case View.onTouchEvent() which reminds us of similar methods in the UIResponder class.

The DrawableCanvas.onTouch() method
override fun onTouchEvent(event: MotionEvent): Boolean {
    val result = super.onTouchEvent(event)
    if (!result) {
        when (event.action) {
            MotionEvent.ACTION_DOWN -> {
                val newLine = Line(strokeColor, strokeWidth)
                lines.add(newLine)
                newLine.moveTo(event.x, event.y)
                currentLine = newLine
                invalidate()
                return true
            }

            MotionEvent.ACTION_MOVE -> {
                val x = event.x
                val y = event.y
                currentLine?.lineTo(x, y)
                invalidate()
                return true
            }

            MotionEvent.ACTION_UP -> {
                currentLine = null
                invalidate()
                return true
            }
        }
    }
    return result
}

The code above is quite straightforward, but there is one very interesting method called inside of that switch statement: the View.invalidate() method is the exact equivalent of setNeedsDisplay() in iOS. It tells the operating system that the current instance is "dirty" and that it should be redrawn as soon as possible, usually at the end of the current Looper iteration.

4.4. Persisting the State of Views

In the chapter "User Interface", more exactly in section "Handling Orientation Changes" we talked about how Android activities must save their state when the orientation of the screen changes; and that this is actually a very good idea for dealing with low memory situations, in which your application might be killed to free memory for other processes.

It turns out that this requirement applies not only to activities, but also to all the views contained in that activity. The View.onSaveInstanceState() and View.onRestoreInstanceState() are called automatically in all views, so that their state can be safely restored in the case of an orientation change or an application restart.

The code below shows how our DrawableCanvas class is able to save and restore its own state whenever the hosting activity suffers from some kind of destruction event.

Saving the state of views
override fun onSaveInstanceState(): Parcelable? {
    val bundle = Bundle()
    bundle.putParcelable("superState", super.onSaveInstanceState())
    bundle.putParcelableArrayList("lines", lines)
    return bundle
}

public override fun onRestoreInstanceState(state: Parcelable) {
    if (state is Bundle) {
        lines = state.getParcelableArrayList("lines")
        super.onRestoreInstanceState(state.getParcelable("superState"))
    }
}

The most important concept of saving application state revolves around the android.os.Parcelable interface. Parcelable specifies the methods to implement to make any object subject to serialization and deserialization in the case of a destruction event.

The most common class implementing Parcelable is the android.os.Bundle class, which is simply a bag of string keys and Parcelable values.

In our drawing application we have implemented the Parcelable interface in our Line and Point classes; these are used by the DrawableCanvas class to store the information about the lines created by the user as the finger moved around on the screen.

Architecture of the Draw application

In the "Draw" application, the Line class holds an array of Point instances, representing the locations on the screen "visited" by the finger of the user. A Line also contains a Paint object with information about color, stroke width and other parameters, and can generate on demand a Path object providing its visual representation.

The listing below shows how the Line class implements Parcelable, which involves overriding two methods and adding one public static final field called CREATOR (this might be the most puzzling fact about the Parcelable interface, by the way.)

Implementing the Parcelable interface
override fun describeContents() = 0

override fun writeToParcel(parcel: Parcel, i: Int) {
    val points = arrayOfNulls<PointF>(pointsList.size)
    pointsList.toTypedArray()
    parcel.writeInt(mColor)
    parcel.writeFloat(mStrokeWidth)
    parcel.writeParcelable(initialPoint, 0)
    parcel.writeParcelableArray<PointF>(points, 0)
}

companion object CREATOR : Parcelable.Creator<Line> {
    override fun createFromParcel(parcel: Parcel): Line {
        val loader = PointF::class.java.classLoader
        val line = Line(parcel.readInt(), parcel.readFloat())
        val initialPoint = parcel.readParcelable<Parcelable>(loader) as PointF
        line.moveTo(initialPoint.x, initialPoint.y)
        val points = parcel.readParcelableArray(loader) as Array<PointF>
        for (i in points.indices) {
            val point = points[i]
            line.lineTo(point.x, point.y)
        }
        return line
    }

    override fun newArray(i: Int): Array<Line?> = arrayOfNulls(i)
}

Thanks to this system, the MainActivity can be destroyed at wish, and if this happens the state of the DrawingCanvas will be restored without problem.

Kotlin @Parcelize Annotation

To simplify the job of implementers of the Parcelable interface, there are two solutions:

  1. Android Studio includes plugins that automatically generate the code required by the Parcelable interface. You can install them in the settings of the application, under the section "Plugins".

  2. The kotlin-android-extensions plugin contains Parcelable experimental support through the @Parcelize annotation, which dramatically simplifies the implementation of this interface in any class.

To show this second option, we are going to enable the experimental support for the Kotlin Android extensions plugin:

Enabling the experimental Kotlin Android extensions
androidExtensions {
    experimental = true
}

Then, on the Line class, we comment out the complete Parcelable implementation shown earlier and we just add the @Parcelize annotation to our class. And that is it!

Using Kotlin @Parcelize annotation
@Parcelize
class Line(private val mColor: Int,
           private val mStrokeWidth: Float,
           private var pointsList: ArrayList<PointF> = ArrayList(),
           private var initialPoint: PointF = PointF(0f, 0f)) : Parcelable {
This Kotlin Android extension requires all serializable properties to be defined in the constructor of the class, not just as fields in the body of the class.

4.5. Gestures

Let us be very clear from the beginning; Android does not include anything remotely similar to the beloved UIGestureRecognizer family. This means that all interactions on the screen must be managed manually, a situation somewhat similar to the world of iPhone development before iPhone OS 3.2 (released together with the first iPad, and which included gesture recognizers for the first time.)

Follow along

The code of this section is located in the Graphics/Gestures folder.

Having to deal with multiple touch patterns in code is not simple, but the code below shows one possible way to do this. The activity must keep track at all times of the state of the touches, and using this information it builds an instance of android.graphics.Matrix, which in many ways is the Android equivalent of CGAffineTransform matrices.

Multi-touch in Android
override fun onTouch(v: View, motionEvent: MotionEvent): Boolean {
    val view = v as ImageView
    when (motionEvent.action and MotionEvent.ACTION_MASK) {
        MotionEvent.ACTION_DOWN -> {
            savedMatrix.set(matrix)
            originalPoint.set(motionEvent.x, motionEvent.y)
            state = DRAG
            lastEvent = null
        }

        MotionEvent.ACTION_POINTER_DOWN -> {
            oldDistance = spacing(motionEvent)
            if (oldDistance > 10f) {
                savedMatrix.set(matrix)
                midPoint(midPoint, motionEvent)
                state = ZOOM
            }
            val event = FloatArray(4)
            event[0] = motionEvent.getX(0)
            event[1] = motionEvent.getX(1)
            event[2] = motionEvent.getY(0)
            event[3] = motionEvent.getY(1)
            lastEvent = event
            distance = rotation(motionEvent)
        }

        MotionEvent.ACTION_UP, MotionEvent.ACTION_POINTER_UP -> {
            state = NONE
            lastEvent = null
        }

        MotionEvent.ACTION_MOVE -> if (state == DRAG) {
            matrix.set(savedMatrix)
            val dx = motionEvent.x - originalPoint.x
            val dy = motionEvent.y - originalPoint.y
            matrix.postTranslate(dx, dy)
        } else if (state == ZOOM) {
            val newDist = spacing(motionEvent)
            if (newDist > 10f) {
                matrix.set(savedMatrix)
                val scale = newDist / oldDistance
                matrix.postScale(scale, scale, midPoint.x, midPoint.y)
            }
            if (lastEvent != null && motionEvent.pointerCount == 2) {
                val newRot = rotation(motionEvent)
                val r = newRot - distance
                val values = FloatArray(9)
                matrix.getValues(values)
                val tx = values[2]
                val ty = values[5]
                val sx = values[0]
                val xc = view.width / 2 * sx
                val yc = view.height / 2 * sx
                matrix.postRotate(r, tx + xc, ty + yc)
            }
        }
    }

    view.imageMatrix = matrix
    return true
}
Gesture recognizers on Android

At least one project in Github is trying to bring an implementation of gesture recognizers to the world of Android development, but as far as the author of these lines is concerned, this is probably the only effort of this kind at the time of this writing.

4.6. Animations

Another thing that Android makes as easy to use as in iOS are animations. All classes inheriting from View have a set of "animatable properties" that can be… well, animated! We are going to learn how to do this in this section.

Follow along

The code of this section is located in the Graphics/Animations folder.

There are basically two main APIs that allow you to animate views on the screen, and they happen to be extremely similar to their counterparts in UIKit.

The first API is the View.animate() method, which is analogue to the animate(withDuration:animations:) method of UIView. Using this method you just specify the duration and the transitions that you require, and you can attach a callback object (implementing the Animator.AnimatorListener interface) to be notified of different events in the life of the animation.

This shows how to animate a simple TextView instance:

Simple animations using View.animate()
var transparency = 1.0f
if (textViewVisible) {
    transparency = 0.0f
}
textViewVisible = !textViewVisible
textView.animate()
        .alpha(transparency)
        .setDuration(duration.toLong())
        .setListener(this)

The second API is the family of android.animation.Animator classes, which is extremely similar to the CAAnimation family of classes in the Core Animation framework of iOS; for example, ObjectAnimator is similar to CAPropertyAnimation; AnimatorSet is similar to CAAnimationGroup and so on.

The diagram below explains the class hierarchy of the Animator family of classes.

diagram animator classes
Figure 57. Animator Hierarchy Diagram

The following code shows how to use the Animator classes in your code.

More complex animations using Animator
val rotate = ObjectAnimator.ofFloat(textView,
        "rotation", 0f, 360f)
val moveH = ObjectAnimator.ofFloat(textView,
        "translationX", 0f, 100f)
val moveV = ObjectAnimator.ofFloat(textView,
        "translationY", 0f, 100f)
val backH = ObjectAnimator.ofFloat(textView,
        "translationX", 100f, 0f)
val backV = ObjectAnimator.ofFloat(textView,
        "translationY", 100f, 0f)
val set = AnimatorSet()
set.setDuration(duration.toLong())
        .play(rotate)
        .before(backH).before(backV)
        .after(moveH).after(moveV)
set.addListener(this)
set.start()

But, what is animatable in a View? Well, just like with iOS, there is a defined set of "animatable properties" that can be… well, animated! These are the properties:

translationX and translationY

Location of the view in its container.

rotation, rotationX, and rotationY

Rotation of the view around its pivot point.

scaleX and scaleY

Scaling of the view around its pivot point.

pivotX and pivotY

Location of the pivot point (by default it is at the geographical center of the view.)

x and y

Coordinates of the view in the reference frame of its parent view.

alpha

Transparency of the view, ranging from 0 (transparent) to 1 (fully opaque.)

Finally, you can be notified of the end of an animation, whether it is a simple or a complex one, by passing an object implementing the Animator.AnimatorListener interface and providing a suitable implementation of its methods.

Notification after the end of an animation
override fun onAnimationEnd(animator: Animator) {
    if (textViewVisible) {
        menuItem?.title = "Disappear"
    } else {
        menuItem?.title = "Appear"
    }
    Toast.makeText(this,
            "Animation finished!",
            Toast.LENGTH_SHORT)
            .show()
}

4.7. Using PaintCode

To close this chapter, I wanted to provide all mobile developers out there with a closer look at an amazing commercial application called PaintCode. Originally targeting only iOS developers, version 3 (released during the writing of this book) included the possibility to generate Java code, helping designers and developers to work closer when creating cross-platform user interfaces.

PaintCode generates code for Android (only Java at the time of this writing), iOS (both in Swift and in Objective-C), macOS (in Swift and in Objective-C) the web (generating JavaScript Canvas, CSS and SVG code) and even in C# for the Xamarin application programming environment. It can generate "stylesheets" in the shape of classes, including all the required resources such as colors, gradients, shadows and images, so that your application shares common design elements throughout platforms.

Kotlin support

At the time of this writing, it is uncertain whether PaintCode will support Kotlin in the future. However, given that Kotlin and Java code can safely coexist in the same project, this is not really a problem.

The next two figures both show how an admittedly bad design is translated faithfully from the designer application to the device.

The PaintCode application in action
Figure 58. The PaintCode application in action
The design deployed on a device
Figure 59. The design deployed on a device

I can only recommend downloading the trial and giving it a shot; you might be surprised of the possibilities. But please make sure to have a designer in the team, or at least do not hire me to do your designs!

4.8. Summary

Android provides a fully fledged set of APIs, ready to bring you craziest user interfaces to life. The variety of devices and resolutions available in the market, however, require you to pay attention and to make sure that your designs will scale gracefully in all kinds of hardware.

Drawing custom views in Android is very similar to using Core Graphics in iOS, including the existence of a "context" object onto which all drawing is performed. Similarly, developers must not call the onDraw() method themselves, as this is the job of the operating system.

Views can save their state automatically whenever their container activity is being destroyed. They should do this to ensure that the user experience they provide stays untouched by device orientation changes, memory warnings or other situations.

Finally, Android has full support for animations, and once again this subsystem is incredibly similar to that of iOS. Developers can use applications such as PaintCode to help them create their designs with greater fidelity accross platforms.

Part 3: Managing Data

Retrieving, Storing, Displaying and Consuming Data

Arguably, getting and manipulating data are the most important tasks of any application in any operating system. In this part we are going to learn how to connect to data sources through the network, how to store that data locally and how to display it on the screen of our devices.

5. Networking

In this chapter we are going to learn how to use some very common networking technologies used by Android applications to communicate with servers and with other devices on the Internet. Not only that, but we are also going to learn how to display the data from the network in a list similar to a UITableView.

5.1. TL;DR

For those of you in a hurry, the table below summarizes the most important pieces of information in this chapter.

Table 11. Networking in Android
Android iOS

Native networking library

HttpURLConnection

NSURLConnection

Background mechanism

android.os.AsyncTask

NSOperation

JSON Parser

org.json.JSONObject

NSJSONSerialization

JSON (de)serialization

Gson

NSPropertyListSerialization

XML SAX

org.xmlpull.v1.XmlPullParser

NSXMLParser

XML DOM

org.w3c.dom.Document

KissXML

Array

List<> & ArrayList<>

NSArray & NSMutableArray

Table view

RecyclerView

UITableView

Table view data

RecyclerView.Adapter

UITableViewDataSource

Table view cell

RecyclerView.ViewHolder

UITableViewCell

REST Client

Retrofit

RESTKit

Popular networking library

OkHttp

AFNetworking

Web view

android.webkit.WebView

WKWebView

Zeroconf Service

android.net.nsd.NsdServiceInfo

NetService

Zeroconf Browser

android.net.nsd.NsdManager

NetServiceBrowser

5.2. Consuming REST Web Services

We are going to start this chapter by creating a very simple application that performs a network request to a free API provided by the GeoNames geographical database. The data of this database is freely available, and it is licensed under a Creative Commons Attribution 3.0 License.

In particular, we are going to use a very nice API they offer, called "findNearbyWikipedia" which returns items of interest located in a geographical region. This API returns data in both JSON and XML formats, which we will use to show how to parse data in these two different formats.

First we are going to use the default HTTP libraries offered by Android, and later in this chapter we are going to use Retrofit, a third party open source library created by the team of Square.

Built-in HTTP Libraries

We are going to start our exploration of Networking in Android by performing a very simple GET request to one of the endpoints in the GeoNames APIs.

Follow along

The code of this section is located in the Networking/HTTPRequest folder.

Create a new project in Android Studio, using all the default parameters. Add a new Kotlin class to this project and name it APIConnector. The code of the APIConnector class is available in the following code snippet.

Class performing a GET HTTP request
internal class APIConnector {
    @Throws(IOException::class)
    fun getStringData(urlString: String): String {
        val url = URL(urlString)
        val conn = url.openConnection() as HttpURLConnection
        conn.useCaches = true
        conn.requestMethod = "GET" // default value
        conn.connectTimeout = 30000

        try {
            val inputStream = conn.inputStream
            if (conn.responseCode != HttpURLConnection.HTTP_OK) {
                throw IOException(conn.responseMessage + " (" + urlString + ")")
            }
            return inputStream.bufferedReader().readText()
        } finally {
            conn.disconnect()
        }
    }
}

The HttpURLConnection class in the Android SDK fulfills a similar role to that of the NSURLConnection class in Cocoa. You can specify various parameters, including the HTTP verb to be used (by default, as one might expect, this value is GET) and other parameters, such as headers, cookies, authentication, etc.

Use the built-in cache

Remember how caching was one of the most complex problems in computer science? Do not create your own local cache for downloaded images! Just use the one provided by the HttpURLConnection class: conn.setUseCaches(true); and you are ready to go.

We are now going to use this simple wrapper around the HttpURLConnection class in our MainActivity. We could simply do the following at this stage:

Blocking the Main Thread
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    String data = new APIConnector().getStringData(API_URL);
}

But this approach has a major flaw; it runs the network connection on the main thread of the application. And this is a bad, bad thing; just like it is in iOS. No surprise here.

Android applications, like most GUI toolkits out there, use an "event loop" in the main thread. This event loop, represented by the Looper class, and very well known to any iOS developer used to see the NSRunLoop class out there in the wild, performs pretty much the same tasks in Android as it does in iOS. It consumes events from the operating system, and executes the code associated to these events as fast as possible. If we use the APIConnector class in our main loop, it will block its execution until the network call has completed (or failed for any reason, including a timeout.) We say that the network call is executed synchronously.

What we need, in this case, is a mechanism to execute our network call in a background thread, or asynchronously, and for this reason we are going to use the standard android.os.AsyncTask class.

A much better version of the MainActivity class looks like this:

Performing Network Operations in the Background
class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        ConnectToAPITask().execute()
    }

    private inner class ConnectToAPITask : AsyncTask<Void, Void, String>() {
        override fun doInBackground(vararg voids: Void): String? {
            try {
                val url = API_URL.replace("USERNAME", "steve")
                val data = APIConnector().getStringData(url)
                Log.i("MainActivity", "Fetched data: " + data)
                return data
            } catch (e: IOException) {
                Log.e("MainActivity", "Failed to fetch URL: ", e)
            }

            return null
        }

        override fun onPostExecute(s: String) {
            textView.text = s
        }
    }

    companion object {
        private val API_URL = "http://api.geonames.org/findNearbyWikipediaJSON?formatted=true&lat=47&lng=9&username=USERNAME&style=full"
    }
}

The result of running the code of this application should appear in the logcat viewer of your Android Studio installation, and it should look like this:

JSON API Output
I/MainActivity: Fetched data: {"geonames": [
                  {
                    "summary": "The Glärnisch is a mountain of the North-Eastern Swiss Alps, overlooking the valley of the Linth in the Swiss canton of Glarus. It consists of several summits of which the highest is 2,915 metres above sea level (...)",
                    "elevation": 2880,
                    "geoNameId": 2660595,
                    "feature": "mountain",
                    "lng": 8.998611,
                    "distance": "0.1869",
                    "countryCode": "CH",
                    "rank": 91,
                    "lang": "en",
                    "title": "Glärnisch",
                    "lat": 46.998611,
                    "wikipediaUrl": "en.wikipedia.org/wiki/Gl%C3%A4rnisch"
                  },

The benefit of using an AsyncTask is that our main thread is now completely free to keep receiving input and events, and our user will be able to scroll, navigate and perform any other task while the network call returns – or not.

The JSON result contains information about the points of interest in a canton of Switzerland called Glaris – a beautiful place you should definitely visit one day! If you do, do not forget to let the author know and hopefully we could meet in person and talk about Android watching the Swiss Alps.

Permissions

If you have followed the instructions above, most probably the code did not work, and that is OK; we have forgotten to add the required permissions to our application.

By default, and for security reasons, Android applications are not allowed to perform many operations, such as connecting to the internet, accessing the list of contacts on your device or using the camera. You have to manually give permission to perform each of these operations, and this is done in the AndroidManifest.xml file.

Internet Permission in the AndroidManifest.xml file
<uses-permission android:name="android.permission.INTERNET"/>

OkHttp

To finish this overview of simple HTTP connectivity, we are going to perform the same operation as previously, but instead of using HttpURLConnection we are going to use an open source library called OkHttp.

OkHttp is a wildly popular Android networking library. It is referenced by many other libraries in the open source world around Android, and in many ways it can be considered the equivalent of AFNetworking.

Follow along

The code of this section is located in the Networking/OkHttp folder.

Using it is extremely simple, and we are going to reuse the GeoNames API we have used previously. First you must add the dependency in Gradle, as shown below.

Adding OkHttp as a dependency
implementation 'com.squareup.okhttp3:okhttp:3.9.1'

Next we are going to rewrite the APIConnector class we created before, so that it uses OkHttp instead. And the final code could not be simpler, as shown as follows.

Using OkHttp
internal class APIConnector {
    @Throws(IOException::class)
    fun getStringData(urlString: String): String? {
        val url = URL(urlString)
        val client = OkHttpClient()

        val request = Request.Builder()
                .url(url)
                .build()

        val response = client.newCall(request).execute()
        return response.body()?.string()
    }
}

OkHttp offers the full spectrum of services expected of a networking library, and it is very simple to use.

5.3. Parsing JSON Data

The result of our call is a long string containing a certain amount of information codified in the venerable JSON format. This format has become a de facto standard for web APIs, and as such we are going to see how to convert this JSON code into native data structures that we can manipulate with Kotlin. This process is called parsing and deserializing the JSON string.

Follow along

The code of this section is located in the Networking/JSONParsing folder.

We are going to create a new project now, using the default parameters as usual, in we are going to reuse our APIConnector class from our previous project.

This time we are going to create a POJO (also known as "Plain Old Java Object") that we will call PointOfInterest – it will hold the information returned by the JSON returned from the API call.

A POJO Representing a Point of Interest
class PointOfInterest @Throws(JSONException::class)
internal constructor(obj: JSONObject) {
    var summary: String? = null
    var elevation: Int = 0
    var geoNameId: Int = 0
    var feature: String? = null
    var lng: Double = 0.toDouble()
    var distance: String? = null
    var countryCode: String? = null
    var rank: Int = 0
    var lang: String? = null
    var title: String? = null
    var lat: Double = 0.toDouble()
    var wikipediaUrl: String? = null

    init {
        summary = obj.getString("summary")
        elevation = obj.getInt("elevation")
        if (obj.has("geoNameId")) {
            geoNameId = obj.getInt("geoNameId")
        }
        if (obj.has("feature")) {
            feature = obj.getString("feature")
        }
        lng = obj.getDouble("lng")
        distance = obj.getString("distance")
        countryCode = obj.getString("countryCode")
        rank = obj.getInt("rank")
        lang = obj.getString("lang")
        title = obj.getString("title")
        lat = obj.getDouble("lat")
        wikipediaUrl = obj.getString("wikipediaUrl")
    }

    override fun toString(): String {

// ...

Parsing JSON with Gson

We all agree that the code in the previous section is quite verbose, and if we have to do the same for every POJO in our application we are going to end up with a substantial amount of boilerplate code scattered throughout our application.

We can do better, because Google thankfully provides Gson, an open source library that removes the need for adding this code manually for every deserialized object in our application. Gson helps developers to serialize and deserialize JSON structures into native Kotlin objects fast, easily and efficiently. It can handle large amounts of data and is very fast.

Equivalent of Gson in iOS

In the world of iOS apps, Mantle plays more or less the same role than Gson. You can also think of Gson as an equivalent of Cocoa’s NSPropertyListSerialization class, but instead of working with Cocoa’s native "property list formats" (Binary and XML), Gson works with JSON strings and streams.

First we need to add the dependency to our application-level Gradle file, as shown here:

Follow along

The code of this section is located in the Networking/Gson folder.

Adding Gson as a dependency in the build.gradle file
implementation 'com.google.code.gson:gson:2.8.2'

Once this is done, we can modify the APIConnector class.

APIConnector class using Gson
internal val pointsOfInterest: List<PointOfInterest>?
    @Throws(IOException::class)
    get() {
        val parser = JsonParser()
        val root = parser.parse(data).asJsonObject
        val geonames = root.get("geonames").asJsonArray
        val collectionType = object : TypeToken<List<PointOfInterest>>() {}.type
        val gson = Gson()
        return gson.fromJson<List<PointOfInterest>>(geonames, collectionType)
    }

And that is all. Because our PointOfInterest class has exactly the same field names as the JSON returned by the API, we can use Gson here to perform a very simple 1-to-1 mapping of fields and keys.

5.4. Parsing XML Data

The same web service we have used so far can return XML data; the only difference is that we have to remove the "JSON" word from the URL, and just by using the same parameters, we will have a simple XML output.

Follow along

The code of this section is located in the Networking/XMLParsing folder.

In this section we are going to learn how to use the standard "SAX-style" XML parser functionality built-in into Android.

About XML Parsers

There are two different kinds of XML parsers:

  1. SAX-style

  2. DOM-style

A SAX-style XML parser is event-based, and consume a stream of data; it is usually very fast, requires very little RAM, and is perfectly adapted for the low-power, battery-fed world of smartphones. On the other hand, it is usually hard to manipulate and code, particularly if the XML stream is complex.

On the other hand, a DOM-style XML parser loads a whole XML file in memory at once, and offers a tree-based approach with nodes and children nodes. This is usually a much simpler programming model, but it has increased memory requirements, which makes it suitable only for small amounts of XML data.

Using The Android SAX XML Parser

The XML data returned from the web service looks like the output below.

XML data returned by the web service
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<geonames>
<entry>
<lang>en</lang>
<title>Glärnisch</title>
<summary>The Glärnisch is a mountain… </summary>
<feature>mountain</feature>
<countryCode>CH</countryCode>
<elevation>2880</elevation>
<lat>46.9986</lat>
<lng>8.9986</lng>
<wikipediaUrl>http://en.wikipedia.org/wiki/Gl%C3%A4rnisch</wikipediaUrl>
<thumbnailImg/>
<rank>91</rank>
<distance>0.1869</distance>
</entry>
<!-- ... -->
</geonames>

The code required to parse this XML stream is located in the training.akosma.xmlparsing.XmlPOIParser class in the sample project. The most important method of that class is reproduced below, and shows how the parser advances through the XML stream until there is no more data to process.

XML SAX parser
@Throws(XmlPullParserException::class, IOException::class)
private fun readFeed(parser: XmlPullParser): List<PointOfInterest> {
    val points = ArrayList<PointOfInterest>()

    parser.require(XmlPullParser.START_TAG, ns, "geonames")
    while (parser.next() != XmlPullParser.END_TAG) {
        if (parser.eventType != XmlPullParser.START_TAG) {
            continue
        }
        val name = parser.name
        // Starts by looking for the entry tag
        if (name == "entry") {
            points.add(readPOI(parser))
        } else {
            skip(parser)
        }
    }
    return points
}

The code above references the readPOI() method, which itself watches the stream for specific tags, and builds the required PointOfInterest instance accordingly.

XML SAX parser
@Throws(XmlPullParserException::class, IOException::class)
private fun readPOI(parser: XmlPullParser): PointOfInterest {
    parser.require(XmlPullParser.START_TAG, ns, "entry")
    val poi = PointOfInterest()
    while (parser.next() != XmlPullParser.END_TAG) {
        if (parser.eventType != XmlPullParser.START_TAG) {
            continue
        }
        val name = parser.name
        when (name) {
            "lang" -> poi.lang = readString(parser, "lang")
            "title" -> poi.title = readString(parser, "title")
            "summary" -> poi.summary = readString(parser, "summary")
            "feature" -> poi.feature = readString(parser, "feature")
            "countryCode" -> poi.countryCode = readString(parser, "countryCode")
            "wikipediaUrl" -> poi.wikipediaUrl = readString(parser, "wikipediaUrl")
            "distance" -> poi.distance = readString(parser, "distance")
            "elevation" -> poi.elevation = readInt(parser, "elevation")
            "geoNameId" -> poi.geoNameId = readInt(parser, "geoNameId")
            "lat" -> poi.lat = readDouble(parser, "lat")
            "lng" -> poi.lng = readDouble(parser, "lng")
            "rank" -> poi.rank = readInt(parser, "rank")
            else -> skip(parser)
        }
    }
    return poi
}

The whole process becomes relatively simple thanks to the structure of the XML data, which has no major complexities. Keeping track of the different tags in the XML stream can be problematic using this method. For example, if the same XML tag appear at different levels in the stream, then the developer is forced to keep track of the current "depth" level of the tree and the current tag name, in order to parse the data correctly. This can quickly become complex.

5.5. Displaying Data in Lists

One of the most common tasks that iOS and Android developers perform every day consists in loading data from some backend network service and display it in a list. This is so common that we are going to dedicate a complete section to it, and we are going to discover just how similar it is to use a RecyclerView than to use a UITableView instance.

Follow along

The code of this section is located in the Networking/PointOfInterest folder.

This sample code consists in a RecyclerView instance contained within a PointOfInterestListFragment object, itself contained within the MainActivity of our class. However, instead of loading the activity through the XML layout – just like we did in chapter "User Interface", this time we are going to load the fragment programmatically using the FragmentManager system.

This is a complementary mechanism to that of XML layouts, and allows for greater flexibility; fragments loaded using the Fragment Manager can be replaced, removed and changed at runtime, which is something that XML-based fragments cannot do.

To use the fragment manager is very simple.

Loading fragments using a FragmentManager
class MainActivity : FragmentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val fm = supportFragmentManager
        var fragment: Fragment? = fm.findFragmentById(R.id.fragment)

        if (fragment == null) {
            fragment = PointOfInterestListFragment()
            fm.beginTransaction()
                    .add(R.id.fragment, fragment)
                    .commit()
        }
    }
}

The fragment itself contains an instance of the android.support.v7.widget.RecyclerView class, which as the package name implies is part of the Support Libraries of Android. It has been recently added to the platform, and it has quickly become the de-facto mechanism to display lists in Android. It is very simple to use and its API looks very similar to that of the UITableView class in iOS.

The RecyclerView class is not available by default in Android projects; the following images show how to add the required dependencies in the project when selecting File  Project Structure or using the + (arrow down) keyboard shortcut, and then selecting the "Dependencies" tab.

Project dependencies
Figure 60. Project dependencies
Add the RecyclerView dependency in the project
Figure 61. Add the RecyclerView dependency in the project

We need to give the RecyclerView the data to be displayed; for that, we have to create an Adapter which is an object that extends the RecyclerView.Adapter class. An adapter is nothing else than the "data source" of the recycler view; its role is to return a "Holder" view (what we could simply call a "cell" in iOS) for each item in the list.

This is the adapter for our RecyclerView.

Adapter for the RecyclerView
private inner class PointOfInterestAdapter internal constructor(private val data: List<PointOfInterest>)
    : RecyclerView.Adapter<PointOfInterestHolder>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PointOfInterestHolder {
        val inflater = LayoutInflater.from(activity)
        val view = inflater.inflate(R.layout.list_item_poi, parent, false)
        return PointOfInterestHolder(view)
    }

    override fun onBindViewHolder(holder: PointOfInterestHolder, position: Int) {
        val poi = data[position]
        holder.bind(poi)
    }

    override fun getItemCount(): Int {
        return data.size
    }
}

The onCreateViewHolder() method of the adapter is the local equivalent of the tableView:cellForRowAtIndexPath: method in the UITableViewDataSource protocol in Cocoa. This method returns a subclass of RecyclerView.ViewHolder which is in many ways equivalent to the UITableViewCell class.

Generic Class

The RecyclerView.Adapter class is generic, and takes as a parameter the class name of the "cell" to be returned for each item in the list.

The local subclass of RecyclerView.ViewHolder is shown below.

ViewHolder subclass
private inner class PointOfInterestHolder internal constructor(v: View)
    : RecyclerView.ViewHolder(v), View.OnClickListener {

    private var poi: PointOfInterest? = null
    private val titleTextView: TextView
    private val latitudeTextView: TextView
    private val longitudeTextView: TextView
    private val summaryTextView: TextView

    init {
        v.setOnClickListener(this)
        titleTextView = v.findViewById(R.id.name_text_view)
        latitudeTextView = v.findViewById(R.id.latitude_text_view)
        longitudeTextView = v.findViewById(R.id.longitude_text_view)
        summaryTextView = v.findViewById(R.id.summary_text_view)
    }

    internal fun bind(poi: PointOfInterest) {
        this.poi = poi
        titleTextView.text = poi.title
        latitudeTextView.text = poi.lat.toString()
        longitudeTextView.text = poi.lng.toString()
        summaryTextView.text = poi.summary
    }

    override fun onClick(view: View) {
        Toast.makeText(activity, poi?.summary, Toast.LENGTH_LONG).show()
    }
}

Once the application runs, the list appears on screen as shown in the next figure.

List implemented with the RecyclerView class
Figure 62. List implemented with the RecyclerView class

Fast-Scrolling RecyclerView

Since version 26 of the Android Support Library, the RecyclerView class can support fast scrolling, that is, a bar on the rightmost edge on the screen of the list which allows it to scroll very quickly to the bottom. To add this feature, only the XML resource files need to be changed; just add the tags shown below.

Fast-scrolling RecyclerView
<android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/listView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    app:fastScrollEnabled="true"

    app:fastScrollHorizontalThumbDrawable="@drawable/thumb_drawable"
    app:fastScrollHorizontalTrackDrawable="@drawable/line_drawable"
    app:fastScrollVerticalThumbDrawable="@drawable/thumb_drawable"
    app:fastScrollVerticalTrackDrawable="@drawable/line_drawable"
    app:layout_behavior="@string/appbar_scrolling_view_behavior" />

You will need to add the required drawables in the res/drawable folder of your application, for example a line and a thumb marker.

Line used to draw the fast scrolling feature
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">

    <solid android:color="@android:color/darker_gray" />

    <padding
        android:top="10dp"
        android:left="10dp"
        android:right="10dp"
        android:bottom="10dp"/>
</shape>
Thumb marker for fast scrolling
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">

    <corners
        android:topLeftRadius="44dp"
        android:topRightRadius="44dp"
        android:bottomLeftRadius="44dp" />

    <padding
        android:paddingLeft="22dp"
        android:paddingRight="22dp" />

    <solid android:color="@color/colorPrimaryDark" />

</shape>
For a nicer example, with a much longer list, check the example in the UI/Fragments folder.

5.6. Retrofit

Retrofit is a high-level, strongly typed REST API wrapper library for Kotlin and Android. It is built on top of OkHttp and provides a complete abstraction over the entities being served over the API. It is quite simple to use it but it requires a bit of infrastructure to setup.

Follow along

The code of this section is located in the Networking/RESTClient folder.

The application talks to a server application built with Node.js available in the Networking/restapi folder. This application can be launched locally using the node app.js command, and then you should modify line 11 of the ServiceGenerator class in the project to point to the correct URL.

Using ngrok as reverse proxy

For testing purposes the author of this lines was using ngrok to tunnel the local Node.js server so that it could be reached from any testing device.

First you need to add the dependency in the app/build.gradle file.

Adding Retrofit as a dependency
implementation 'com.squareup.retrofit2:retrofit:2.3.0'
implementation 'com.squareup.retrofit2:converter-gson:2.3.0'

Then you need to create the infrastructure required to map the REST endpoints to your Android application:

  1. The ServiceGenerator class wraps the creation of the different client objects used to connect to the backend.

  2. The UsersClient interface maps local Kotlin methods with remote REST API methods, including their HTTP verbs and other contextual information.

  3. A local model class User represents the data being manipulated through the REST interface.

Retrofit ServiceGenerator class
internal object ServiceGenerator {

    // Do not use "localhost" or "127.0.0.1" here!
    // Those point to the emulator, not to the local machine!
    private val API_BASE_URL = "https://da9621ea.eu.ngrok.io/"

    private val httpClient = OkHttpClient.Builder()

    private val builder = Retrofit.Builder()
            .baseUrl(API_BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())

    fun <S> createService(serviceClass: Class<S>): S {
        val retrofit = builder.client(httpClient.build()).build()
        return retrofit.create(serviceClass)
    }
}
UsersClient interface
interface UsersClient {
    @get:GET("/users")
    val users: Call<List<User>>

    @GET("/user/{id}")
    fun getUser(@Path("id") userId: Int): Call<User>

    @POST("/users")
    fun createUser(@Body user: User): Call<User>

    @PUT("/user/{id}")
    fun updateUser(@Path("id") userId: Int, @Body user: User): Call<Void>

    @DELETE("/user/{id}")
    fun deleteUser(@Path("id") userId: Int): Call<Void>
}
User class
class User {

// ...

Once all of these elements are in place, we can start creating, editing and deleting users using a relatively high-level, strongly typed interface.

Using the Retrofit infrastructure in your code
val client = ServiceGenerator.createService(UsersClient::class.java)

val newUser = User()
newUser.age = 10
newUser.country = "Latveria"
newUser.name = "Doctor Doom"

val getUsersCall = client.users
getUsersCall.enqueue(object : Callback<List<User>> {
    override fun onResponse(call: Call<List<User>>,
                            response: Response<List<User>>) {
        if (response.isSuccessful) {
            val users = response.body()
            if (users != null) {
                Log.i("MainActivity", "User: " + users.toString())
                updateList(users)
            }
        } else {
            Log.e("MainActivity", "Error calling the API")
        }
    }

    override fun onFailure(call: Call<List<User>>, t: Throwable) {
        Log.d("Error", t.message)
    }
})

The code speaks by itself; if your network API follows closely the design guidelines of the REST specification, you could benefit greatly from using such a library. One of the biggest benefits it is that it removes all boilerplate code and it makes your application logic stand out at first sight.

5.7. WebView

Android applications can display web content very easily, just like iOS applications can use WKWebView instances to do the same. The android.webkit.WebView class provides such services, and in this section we will create a simple web browser application.

Follow along

The code of this section is located in the Networking/Browser folder.

The first thing we need to do is to create a RelativeLayout with three subviews:

  1. An EditText to enter URLs.

  2. A button to launch requests.

  3. A web view to display the requested page.

Then, on the main activity, we can wire all of these components together:

Wiring the components of a web browser application
val webSettings = webView.settings
webSettings.builtInZoomControls = true
webView.webViewClient = Client()

goButton.setOnClickListener { go() }

urlField.setImeActionLabel(getString(R.string.go), KeyEvent.KEYCODE_ENTER)
urlField.setOnEditorActionListener { textView, i, keyEvent ->
    if (keyEvent != null && keyEvent.keyCode == 66) {
        go()
    }
    false
}

To be able to receive events from the web browser, we need to create an instance of android.webkit.WebViewClient and override some methods:

WebViewClient object to receive events from the WebView
private inner class Client : WebViewClient() {
    override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean {
        return false
    }

    override fun onPageFinished(view: WebView, url: String) {
        urlField.setText(webView.url)
        webView.requestFocus()
    }
}

The go() method is very simple:

Method triggering the loading of URLs
private fun go() {
    webView.requestFocus()
    val input = urlField.text.toString()
    webView.loadUrl(input)
}

Finally, in order to provide keyboard and back button support, we can override the onKeyDown() method:

Handling keyboard and back button events
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
    if (event.action == KeyEvent.ACTION_DOWN) {
        when (keyCode) {
            KeyEvent.KEYCODE_BACK -> {
                if (webView.canGoBack()) {
                    webView.goBack()
                } else {
                    finish()
                }
                return true
            }
        }
    }
    return super.onKeyDown(keyCode, event)
}

The image below shows the result of using the web browser.

WebView browser app in action
Figure 63. WebView browser app in action

5.8. Embedding a Web Server in an Application

Many applications feature embedded web servers, which can be useful to share files with other users in the local network or to provide advanced functionality. To do this, we are going to use a popular embeddable web server called NanoHTTPD.

Follow along

The code of this section is located in the Networking/HTTPServer folder.

As usual, the first step consists in adding the required dependencies in the module Gradle file:

Embedding NanoHTTPD in an application
implementation 'org.nanohttpd:nanohttpd-webserver:2.3.1'

Then we need to add the required permissions so that we can connect to our device from the network:

AndroidManifest.xml permissions
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>

Then we need to create a subclass of fi.iki.elonen.NanoHTTPD, which will be used to return responses to HTTP requests.

An HTTP server for our application
class Server(port: Int = 8080) : NanoHTTPD(port) {
    override fun serve(session: IHTTPSession?): Response {
        var msg = header(session)

        val parameters = session?.parameters
        if (parameters != null) {
            val users = parameters["username"]
            msg += if (users != null && users.count() > 0) {
                greet(users[0])
            } else {
                form()
            }
        }
        return newFixedLengthResponse(msg + footer());
    }

    private fun footer() = "</body></html>\n"

    private fun header(session: IHTTPSession?): String {
        var msg = """
                <html>
                <head>
                <title>Android Server</title>
                </head>

                <body><h1>Android Server</h1>
                <p>Called with `${session?.uri}`</p>
                """.trimIndent()  (1)
        return msg
    }

And the only remaining thing to do now is to actually use this server in our main activity.

Using our HTTP server from the main activity
val port = 8080
val ip = getCurrentIPAddress()
textField.text = "http://$ip:$port"

val server = Server(port)
server.start()

If you run your application now, it will display the URL offered by the HTTP server, for example 192.168.1.107:8080/. Open any browser now and navigate to this page, and you will be able to interact with your Android device in a different way.

Port 80 is not available

For security reasons, port 80 is reserved to the root user in all Linux systems, and as such it is not available when using NanoHTTPD.

5.9. Zeroconf

Zeroconf or Zero-Configuration Networking is defined by Wikipedia as follows:

…a set of technologies that automatically creates a usable computer network based on the Internet Protocol Suite (TCP/IP) when computers or network peripherals are interconnected. It does not require manual operator intervention or special configuration servers.

— Wikipedia

Among its various uses, Zeroconf is particularly popular among printer manufacturers, allowing users in a local network to automatically discover and setup printers in their devices.

Android applications, just like their iOS counterparts, are able to use Zeroconf natively since Android 4.1 (Jelly Bean, API 16.) In this section we are going to learn how to use Zeroconf in Android using the native libraries, as well as using a common substitute, the JmDNS library also available in Github.

Follow along

The code of this section is located in the Networking/Zeroconf folder.

Basic Zeroconf Concepts

Zeroconf is a large standard, but most of what people do with it can be summarized in two items:

  • Either advertise a service so that other users in the network can connect to it;

  • Or browse for services to connect to in the local network.

"Services" can be anything you can imagine: a printer, a chat server, a media streaming device, an IoT (Internet of Things) device in your home, a game controller, anything. Applications interested in a particular type of service must know the "service identifier" in the form of a string with this shape:

_zeroconfserv._tcp.local.

Knowing this simple string, usually provided by the manufacturer of the service or by a Bonjour browser application, you can create applications that connect to a variety of devices.

Android devices, just like iOS ones, can be used to advertise services in the network, and they can also browse for services. Since our application has to have access to the local network, we need to add the proper permissions:

AndroidManifest.xml permissions for Zeroconf
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />

Creating a Zeroconf Service

To create a Zeroconf service in Android, use the android.net.nsd.NsdServiceInfo class. Once the service is created, one must advertise it, and this requires using the android.net.nsd.NsdManager class:

Zeroconf server in Android
val serviceInfo = NsdServiceInfo()
serviceInfo.serviceName = Constants.DEFAULT_SERVICE_NAME
serviceInfo.serviceType = Constants.SERVICE_TYPE
serviceInfo.port = port
nsdManager.registerService(serviceInfo, NsdManager.PROTOCOL_DNS_SD, registrationListener)

There is an obvious risk of collision of service names in the same network, since many devices could be running this same code at the same time. In that case, the registration process of the service assigns a unique service name at the end of the registration process:

Registration finished for a Zeroconf server
override fun onServiceRegistered(serviceInfo: NsdServiceInfo) {
    Log.i(TAG, "Service registered")
    backingServiceName = serviceInfo.serviceName
    val intent = Intent(Constants.BROADCAST_NAME)
    val manager = LocalBroadcastManager.getInstance(context)
    manager.sendBroadcast(intent)
}

In the code above, we notify the application of the name that our service has been assigned by the operating system after the registration.

Browsing for Zeroconf Services

Devices interested in consuming Zeroconf services can browse for them using another instance of android.net.nsd.NsdManager:

Starting to browse for Zeroconf services
nsdManager?.discoverServices(Constants.SERVICE_TYPE,
        NsdManager.PROTOCOL_DNS_SD,
        discoveryListener)

When a service is found, our code must check that it is the one we are looking for; if it is, then we will ask our manager to resolve it, that is, to find all the information about the service, usually the IP and the port.

Found Zeroconf services and ask for resolution
override fun onServiceFound(service: NsdServiceInfo) {
    Log.i(TAG, "Service found: " + service)
    if (service.serviceType != Constants.SERVICE_TYPE) {
        Log.d(TAG, "Unknown Service Type: " + service.serviceType)
    } else if (service.serviceName == Constants.DEFAULT_SERVICE_NAME) {
        nsdManager?.resolveService(service, resolveListener)
    }
}

Finally, once the service is resolved, we notify the application about it.

Resolved Zeroconf service ready to be consumed
override fun onServiceResolved(serviceInfo: NsdServiceInfo) {
    Log.i(TAG, "Service resolved: " + serviceInfo)

    backingService = serviceInfo
    listener?.onServiceResolutionSucceeded()
}

Browsing for Zeroconf Services using JmDNS

Using JmDNS is very similar, but it requires to download and add a set of JARs from the internet into the app/libs folder of the project, and then to right-click on them so that they are added properly to the module Gradle file:

Gradle file adding JmDNS to the project
implementation files('libs/jmdns-3.5.1.jar')
implementation files('libs/slf4j-android-1.7.25.jar')
implementation files('libs/slf4j-api-1.7.25.jar')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"

Once this is done, we must use the javax.jmdns.JmDNS class to browse the network for services:

Starting to browse for Zeroconf services with JmDNS
val wifi = context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
val deviceIpAddress = getDeviceIpAddress(wifi)
lock = wifi.createMulticastLock(javaClass.name)
lock?.setReferenceCounted(true)
lock?.acquire()
val hostname = "localhost"
jmdns = JmDNS.create(deviceIpAddress, hostname)
jmdns?.addServiceListener(Constants.SERVICE_TYPE + "local.", this)

Similarly, when a service is found, we ask JmDNS to resolve it:

Found Zeroconf services and ask for resolution to JmDNS
override fun serviceAdded(serviceEvent: ServiceEvent) {
    Log.i(TAG, "Service added: " + serviceEvent)
    jmdns?.requestServiceInfo(serviceEvent.type,
            serviceEvent.name, 1)
}

And of course, after the service is resolved, we notify the application about it.

JmDNS resolved a Zeroconf service
override fun serviceResolved(serviceEvent: ServiceEvent) {
    Log.i(TAG, "Service resolved: " + serviceEvent)
    val host = serviceEvent.info.hostAddresses[0]
    val port = serviceEvent.info.port
    service = String.format(Locale.ENGLISH, "%s:%d", host, port)
    if (listener != null) {
        listener?.onServiceResolutionSucceeded()
    }
}

Browsing and Resolving a macOS Service

To show the interoperability between the Apple and the Android implementations of the Zeroconf protocol suite (also referred to as "Bonjour" in the world of Apple) there is a Swift program in the Networking/Zeroconf folder containing a simple network service, which your Android devices will be connecting to.

Zeroconf service written in Swift
let domain = "local."
let type = "_zeroconfserv._tcp."
let name = "ZeroconfServer"
let port : CInt = 9876
service = NetService(domain: domain, type: type, name: name, port: port)
service?.publish()

This server can be launched from the command line using the following commands:

$ swift zeroconf.swift

If you want, you can even launch it directly:

$ chmod +x zeroconf.swift
$ ./zeroconf.swift
Mac application exporting a Zeroconf service
Figure 64. Mac application exporting a Zeroconf service

Once launched on your Mac, launch the Android application in your device, and make sure that your device and the Mac are in the same local network (wired or wireless.) Hit the "Native Client" or "JmDNS Client" buttons on your Android device and after a few seconds you should see something like the screenshot below.

Title
Figure 65. Android device found Zeroconf service

Hitting the "Server" button in the Android application will advertise a Zeroconf service on the local network, using the same identifier as the Mac version.

Issues resolving services in some devices

During the preparation of this book, the Samsung Tablet was able to successfully advertise the service to the network, but was totally unable to browse, discover or resolve services. Be aware that there are substantial differences among devices, and that you might encounter some problems using Bonjour in Android.

5.10. Summary

The most important thing to keep in mind when writing networking code in Android is, just as with iOS, to keep long running tasks off the main thread. There are many mechanisms to do this, starting with the AsyncTask class, which provides a handy solution for short-lived requests. Most networking libraries automatically take their work to a background thread for you.

Android includes a JSON parsing library, but the open source library Gson by Google is wildly popular too; it provides automatic serialization and deserialization of objects to and from any class (or collection thereof.)

Speaking about open source libraries, OkHttp and Retrofit are also popular choices to connect Android applications to backend services. The former can be thought of the Android equivalent of AFNetworking, while Retrofit is more similar to RestKit, at least in spirit.

Finally, consuming web content from Android applications is as simple as using the android.webkit.WebView class, which has a very similar API to that of WKWebView.

6. Storage

In this chapter we are going to learn several different mechanisms used to store and retrieve information in Android applications. These include reading application resources, writing and reading files to the sandbox, and storing information in SQLite databases.

6.1. TL;DR

For those of you in a hurry, the table below summarizes the most important pieces of information in this chapter.

Table 12. Storage in Android
Android iOS

Local documents

Context.getFilesDir()

NSSearchPathForDirectoriesInDomains()

External storage

Environment.getExternalStorageDirectory()

n/a

Bundled resource

getResources()

NSBundle

Downloading files

DownloadManager

UIBackgroundTaskIdentifier

Notifications

BroadcastReceiver

NSNotificationCenter

Periodic tasks

android.os.Handler & Runnable

NSTask

Preferences

PreferenceManager & SharedPreferences

NSUserDefaults

Sqlite wrapper

SQLiteOpenHelper

FMDB

Object Relational Mapper

Room

Core Data

Realm

Realm

Realm

6.2. Bundled Resources

The first and simplest way to store files in an Android application is by adding files to the application bundle. You can add any kind of file to your application in Android Studio, and these files will be added to the final APK file, and distributed with the binary.

This is very similar to what happens in iOS; in Xcode, developers can add any kind of file to the application bundle, and it is precisely the NSBundle class that is used to retrieve those resources at runtime.

Follow along

The code of this section is located in the Storage/Resources folder.

In the Resources application, we have added an XML file, downloaded from the "findNearbyWikipedia" GeoNames web service. This is exactly the data we were using in chapter "Networking" when connecting to the internet. The first lines of the file appear below.

Embedded data resource file
<geonames>
<entry>
<lang>en</lang>
<title>Glärnisch</title>
<summary>The Glärnisch is a mountain of the Glarus Alps, overlooking the valley of the Linth in the Swiss canton of Glarus. It consists of several summits of which the highest (2,914 metres) is the Bächistock, followed by the Vrenelisgärtli--"Verena's Little Garden"—at 2,904 metres) and the Ruchen (2,901 (...)</summary>
<feature>mountain</feature>
<countryCode>CH</countryCode>
<elevation>2880</elevation>
<lat>46.9986</lat>
<lng>8.9986</lng>
<wikipediaUrl>http://en.wikipedia.org/wiki/Gl%C3%A4rnisch</wikipediaUrl>
<thumbnailImg/>
<rank>85</rank>
<distance>0.1869</distance>
</entry>
The res/raw folder does not exist by default in Android Studio projects. You have to right-click on the res folder and select the option to create a new folder. After a few seconds, the folder and its contents will be available through the R class.
To add a file to the newly created res/raw folder (or to any other folder in an Android Studio project, for that matter) select the file in the Finder and hit the +C keystroke. Select the target folder in Android Studio and hit +V keystroke. That is right! It is just a matter of copying and pasting the file where you need it.

To load this information at runtime, just use the getResources().openRawResource(R.raw.data) method call, which returns an InputStream object.

Programmatically loading a resource file
val factory = DocumentBuilderFactory.newInstance()
val builder = factory.newDocumentBuilder()

val inputStream = resources.openRawResource(R.raw.data)

val doc = builder.parse(inputStream)
val root = doc.documentElement
val entries = root.childNodes

By the way, as a complementary option, instead of using the org.xmlpull.v1.XmlPullParser class to perform a SAX-style parsing of the XML, in this example we use the org.w3c.dom.Document class, which implements a DOM-style XML parser. The main difference between both SAX and DOM parsers is that the DOM requires to load the contents of the whole XML file in memory at once, which might only be useful for small XML payloads.

6.3. Downloading Files

If your application requires to download very large files, you can do that in the background with a little bit of infrastructure. The downloads themselves will be handled completely by the operating system, which actually includes a very handy application (quite aptly named "Downloads") which shows the various file download tasks launched from different applications.

Follow along

The code of this section is located in the Storage/Downloads folder.

This is a similar system to that offered by background download tasks in iOS, with the added benefit of an ad-hoc application that allows you to manage and work with the downloaded file.

Background tasks in iOS
- (void)applicationDidEnterBackground:(UIApplication *)application
{
    UIApplication  *app = [UIApplication sharedApplication];
    UIBackgroundTaskIdentifier task;
    task = [app beginBackgroundTaskWithExpirationHandler:^{
        [app endBackgroundTask:task];
    }];
}

In Android, you can trigger a file download in the background using the code shown below, where we are downloading an image from Wikipedia.

Triggering a file download in the background
val manager = getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
val path = Environment.getExternalStorageDirectory().toString()
val folder = File(path + "/AndroidTutorial")
if (!folder.exists()) {
    folder.mkdirs()
}

val url = "https://upload.wikimedia.org/wikipedia/commons/b/b4/LocationItaly.png"
val uri = Uri.parse(url)
val request = DownloadManager.Request(uri)

request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI
        or DownloadManager.Request.NETWORK_MOBILE)
        .setAllowedOverRoaming(false)
        .setTitle("AndroidTutorial")
        .setDescription("Wikipedia image")
        .setDestinationInExternalPublicDir("/AndroidTutorial",
                "photo.jpg")
queueID = manager.enqueue(request)

Of course, you might as well be interested in being notified when the file is ready to be used; in this case you have to create an instance of BroadcastReceiver and verify that you have received the correct notification, in this case ACTION_DOWNLOAD_COMPLETE.

Receiving a notification when the download is finished
val receiver = object : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        val action = intent.action
        if (DownloadManager.ACTION_DOWNLOAD_COMPLETE == action) {
            intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, 0)
            val query = DownloadManager.Query()
            query.setFilterById(queueID)
            val c: Cursor? = manager.query(query)
            if (c != null && c.moveToFirst()) {
                val columnIndex = c.getColumnIndex(DownloadManager.COLUMN_STATUS)
                if (DownloadManager.STATUS_SUCCESSFUL == c.getInt(columnIndex)) {
                    val index = c.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI)
                    val uriString = c.getString(index)
                    imageView.setImageURI(Uri.parse(uriString))
                }
                c.close()
            }
        }
    }
}
registerReceiver(receiver, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE))
You can think of a BroadcastReceiver as an object subscribing to notifications sent by the NSNotificationCenter in Cocoa.

For this code to work, remember to add the proper permissions.

Permissions required for file downloading
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
The official Android documentation recommends asking for the READ_EXTERNAL_STORAGE permission as well, as this will be required in the future. Be sure to add it, to ensure your applications will work as soon as this change is made.

6.4. Saving and Reading Files Locally

Sometimes the best option for your application is just to persist the data created by the user in a file stored locally. Android offers two different solutions for this:

  1. Internal Storage, available to all applications in all Android devices, and sandboxed from other applications in the same device.

  2. External Storage, usually in the form of SD cards, not available in all Android devices, and not sandboxed – which means that any application can read the data stored by any other application.

In this section we are going to see how to use Internal Storage, with a very simple application that stores and reloads a text written on an EditText. A very simple application to keep just one note at all times! The source code is very simple and is shown next.

Follow along

The code of this section is located in the Storage/InternalStorage folder.

Writing and reading a text file
private fun writeFile() {
    try {
        val file = File(filesDir, filename)
        val os = FileOutputStream(file)
        val text = editor.text.toString()
        os.write(text.toByteArray())
        os.close()
        Log.i("MainActivity", "File saved")
    } catch (e: Exception) {
        Log.e("MainActivity", "Problem writing file", e)
    }

}

private fun readFile() {
    try {
        val file = File(filesDir, filename)
        if (file.exists()) {
            val inputStream = FileInputStream(file)
            val text = inputStream.bufferedReader().readText()
            editor.setText(text)
        }
    } catch (e: Exception) {
        Log.e("MainActivity", "Problem reading file", e)
    }

}

Readers with previous experience with Java will instantly recognize that the APIs used by Android are strictly the same provided in the standard JDK.

We also want to call the writeFile() and readFile() methods when the application goes and returns from the background, and not only that, but we would like to automatically save our text every 5 seconds when the application is active. Let us see how we can do that in the snippet below.

Automatic saving and loading
private val handler = Handler()

internal var periodicWriter: Runnable = object : Runnable {
    override fun run() {
        writeFile()
        val interval = 5000
        handler.postDelayed(this, interval.toLong())
    }
}

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
}

override fun onPause() {
    super.onPause()
    writeFile()
    handler.removeCallbacks(periodicWriter)
}

override fun onResume() {
    super.onResume()
    readFile()
    periodicWriter.run()
}

As shown above, the android.os.Handler class can be used, coupled to a Runnable instance, to trigger the execution of code periodically, similarly to how the NSTask class is used in Cocoa.

6.5. Storing User Preferences

Another common requirement of applications is to store small bits of information, usually booleans, numbers and strings, as preferences for the current user. These preferences can be used to customize the behavior and the look and feel of the application, to accomodate the… preferences of the current user.

Follow along

The code of this section is located in the Storage/Preferences folder.

As shown below, the android.preference.PreferenceManager class is used to access the SharedPreferences object holding the preferences of the user. This class is very similar to the NSUserDefaults class in iOS, and provides pretty much the same interface, allowing you to store atomic elements of information that can be used to customize the application at runtime.

Loading user preferences
val pm = PreferenceManager.getDefaultSharedPreferences(this)
val value = pm.getString("value", "default value")
val decision = pm.getBoolean("decision", true)
val age = pm.getInt("age", 30)

Android Studio also proposes to create activities to allow the user to express those preferences from a graphical user interface. The SettingsActivity class in the Storage/Preferences project shows how to do that.

6.6. SQLite

Android contains a very simple wrapper class that allows developers to manipulate SQLite databases. These are actually the same databases that iOS and Core Data use, but the major difference is that Android does not include an object-relational mapper in its toolkit. In this sense, Android offers something more similar to FMDB, a popular open source SQLite wrapper for Objective-C and Swift applications.

In the next section we are going to see how to use a lightweight ORM in Android applications, but for the moment we are going to use SQLite as it is natively available.

Follow along

The code of this section is located in the Storage/SQLite folder.

The API of this wrapper contains two major classes: android.database.sqlite.SQLiteDatabase SQLiteDatabase represents the SQLite file being accessed, and android.database.sqlite.SQLiteOpenHelper SQLiteOpenHelper is the helper class that manipulates instances of the former class. Applications must subclass SQLiteOpenHelper and provide the commands required to open, drop and modify tables, indexes and other database components.

The listing below shows how to subclass SQLiteOpenHelper in order to create the required tables.

Subclassing SQLiteOpenHelper
class DatabaseHelper internal constructor(context: Context) : SQLiteOpenHelper(context, FILE_NAME, null, VERSION) {

    override fun onCreate(database: SQLiteDatabase) {
        val sb = StringBuilder("CREATE TABLE ")
        sb.append(PEOPLE_TABLE)
                .append(" (")
                .append(ID_FIELD)
                .append(" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL")
                .append(", ")
                .append(NAME_FIELD)
                .append(" TEXT")
                .append(", ")
                .append(AGE_FIELD)
                .append(" INT")
                .append(");")
        database.execSQL(sb.toString())
    }

    override fun onUpgrade(database: SQLiteDatabase, i: Int, i1: Int) {

    }

    companion object {
        private val FILE_NAME = "data.sqlite"
        private val VERSION = 1
        internal val PEOPLE_TABLE = "people"
        internal val ID_FIELD = "id"
        internal val NAME_FIELD = "name"
        internal val AGE_FIELD = "age"
    }
}

As you can see, you have to properly format your SQL statements, following the official SQL syntax supported by SQLite.

SQL in SQLite

Every database vendor implements their own "dialect" of SQL, and SQLite is not the exception. Please take a look at the official SQL documentation for SQLite, and test your queries against a local database, for example using the command line SQLite client application. Android Studio will not prompt any warnings about wrong SQL queries, and your application will definitely fail at runtime if you have errors in them.

Once you have your application-specific database helper, you just need to use it in your application. In this case we are creating a small application to store contact data, and we have created a small Person class to help us model the data.

The next snippet shows how to use the helper subclass in your own application, performing a SELECT statement.

Database Query
private fun queryValues(): List<Person> {
    val table = PEOPLE_TABLE
    val columns = arrayOf(ID_FIELD, NAME_FIELD, AGE_FIELD)
    val selection: String? = null
    val selectionArgs: Array<String>? = null
    val groupBy: String? = null
    val having: String? = null
    val orderBy = NAME_FIELD
    val peopleCursor = database?.query(table,
            columns,
            selection,
            selectionArgs,
            groupBy,
            having,
            orderBy)

    val people = ArrayList<Person>()
    if (peopleCursor != null) {
        var p: Person

        peopleCursor.moveToFirst()
        if (!peopleCursor.isAfterLast) {
            do {
                val id = peopleCursor.getLong(0)
                val name = peopleCursor.getString(1)
                val age = peopleCursor.getInt(2)
                p = Person()
                p.id = id
                p.name = name
                p.age = age
                people.add(p)
            } while (peopleCursor.moveToNext())
        }
        peopleCursor.close()

        Log.i("MainActivity", "Found people: " + people)
    }
    return people
}

Whenever you query your database using the helper, you will be served an instance of android.database.Cursor , a helper object which allows you to "move forward" in the list of records that match your criteria. The do … while statement in the middle of the queryValues() method does precisely that, and creates a Person instance at each iteration.

To insert values in the database, we are going to use another helper class, the android.content.ContentValues class. The Person class shows how to create instances of ContentValues, which are basically key-value maps.

Creating ContentValues instances
val contentValues: ContentValues
    get() {
        val values = ContentValues()
        values.put(NAME_FIELD, name)
        values.put(AGE_FIELD, age)
        return values
    }

override fun toString(): String {
    return String.format(Locale.ENGLISH, "%s(%d)", name, age)
}

The only remaining step consists in giving that ContentValues bag to the DatabaseHelper and let it do its job, inserting the data in the database.

Inserting data in the database
private fun insertValues() {
    val p1 = Person()
    p1.name = "John Smith"
    p1.age = 56

    val p2 = Person()
    p2.name = "Maria Rogers"
    p2.age = 33

    val values1 = p1.contentValues
    database?.insert(PEOPLE_TABLE, null, values1)

    val values2 = p2.contentValues
    database?.insert(PEOPLE_TABLE, null, values2)
}

The SQLiteOpenHelper class offers much more functionality, all of which is described in extensive detail in the Android documentation.

Testing SQLite code

If you need to unit test code using SQLite databases, create the database in memory; for that, remember that SQLiteOpenHelper() creates an in-memory database when the second parameter, name, is null.

Finding the SQLite File on the Device

After executing the code above, you should be able to find your SQLite file using Android Studio’s "Device File Explorer" tool window, available from the View  Tool Windows…  Device File Explorer menu. You should dive into the /data/data folder, and there you will see individual folders for each of the applications installed in the device. Select the training.akosma.sqlite folder, and inside the databases folder you will see a data.sqlite file. You can right-click on it to save it locally on your computer for debugging and inspection.

Device File Explorer Tool Window
Figure 66. Device File Explorer Tool Window

6.7. OrmLite

OrmLite offers a lightweight and strongly-typed alternative to the use of plain SQLite statements in your application. It is a simple ORM that targeted primarily the Java world, and was then adapted to the Android platform, where it works beautifully well.

In this section we are going to rewrite our contact management application, but this time using OrmLite to manage the storage of our entities.

Follow along

The code of this section is located in the Storage/OrmLite folder.

At the moment of this writing, developers must integrate OrmLite manually in their project by downloading the Jar files and adding them in Android Studio. It is not as convenient, but it is not very difficult to do either.

First make sure to download the latest binaries of OrmLite in the official download page; at the time of this writing, it is version 5.0.

Download OrmLite

When you download OrmLite, make sure to download both the ormlite-android-5.0.jar and the ormlite-core-5.0.jar; both are required.

Then create a new Android Studio project, and in the Project pane, select the "Project" perspective. Inside that view, you will see the tree of your project; open up the branches of the tree until you see the app/libs folder. It turns out that Android Studio (thanks to Gradle) automatically compiles anything that you drop in the app/libs folder, so you can just drag and drop your Jar files inside that folder. A couple of seconds later the symbols contained in those archives should be available to your code.

The only remaining thing then is to write the application. We are once again going to create a helper class, but this time we are going to subclass the com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper class. The code below shows how the final version of the file should look like.

OrmLite Database Helper
class DatabaseHelper(context: Context) : OrmLiteSqliteOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION, R.raw.ormlite_config) {

    private var personDao: Dao<Person, Int>? = null
    private var personRuntimeDao: RuntimeExceptionDao<Person, Int>? = null

    val dao: Dao<Person, Int>
        @Throws(SQLException::class)
        get() {
            if (personDao == null) {
                personDao = getDao(Person::class.java)
            }
            return personDao!!
        }

    override fun onCreate(sqLiteDatabase: SQLiteDatabase, connectionSource: ConnectionSource) {
        try {
            Log.i(DatabaseHelper::class.java.name, "onCreate")
            TableUtils.createTable(connectionSource, Person::class.java)
        } catch (e: SQLException) {
            Log.e(DatabaseHelper::class.java.name, "Can't create database", e)
            throw RuntimeException(e)
        }
    }

    override fun onUpgrade(sqLiteDatabase: SQLiteDatabase, connectionSource: ConnectionSource, i: Int, i1: Int) {
        try {
            Log.i(DatabaseHelper::class.java.name, "onUpgrade")
            TableUtils.dropTable<Person, Any>(connectionSource, Person::class.java, true)
            // after we drop the old databases, we create the new ones
            onCreate(sqLiteDatabase, connectionSource)
        } catch (e: SQLException) {
            Log.e(DatabaseHelper::class.java.name, "Can't drop databases", e)
            throw RuntimeException(e)
        }
    }

    fun getPersonDao(): RuntimeExceptionDao<Person, Int> {
        if (personRuntimeDao == null) {
            personRuntimeDao = getRuntimeExceptionDao(Person::class.java)
        }
        return personRuntimeDao!!
    }

    override fun close() {
        super.close()
        personDao = null
        personRuntimeDao = null
    }

    companion object {
        private val DATABASE_NAME = "helloAndroid.db"
        private val DATABASE_VERSION = 1
    }
}

OrmLite works by configuration, using standard Java attributes. We are going to "decorate" our Person class with those, so that OrmLite knows what to do with each piece of information. This is shown here:

Person class with attributes
@DatabaseTable(tableName = "people")
class Person {

    @DatabaseField(generatedId = true)
    var id: Long = 0

    @DatabaseField(index = true)
    var name: String? = null

    @DatabaseField
    var age: Int = 0

    override fun toString(): String {
        return String.format(Locale.ENGLISH, "%s (%d)", name, age)
    }
}

// ...

The final piece of the puzzle is a configuration file, which is expected to be located in the res/raw folder. This file contains more information about the database to be created by OrmLite.

OrmLite configuration file
#
# generated on 2012/06/06 05:55:57
#
# --table-start--
dataClass=training.akosma.ormlite.Person
tableName=people
# --table-fields-start--
# --field-start--
fieldName=id
generatedId=true
# --field-end--
# --field-start--
fieldName=name
indexName=person_string_idx
# --field-end--
# --field-start--
fieldName=age
# --field-end--
# --table-fields-end--
# --table-end--
#################################

Indeed, the syntax of this file is quite strange.

Once this is all done and ready, the only remaining piece of the puzzle is to actually use the Person instances in your code; the code below shows how to do that.

Using OrmLite in your application
dao = helper.getPersonDao()

val john = Person()
john.name = "John Smith"
john.age = 35
dao?.create(john)

val lucy = Person()
lucy.name = "Lucy Skies"
lucy.age = 42
dao?.create(lucy)

showAllPeople()

OrmLite provides annotations for many other parameters, all of which is specified in the documentation pages in the official website.

6.8. Realm

Realm is a relative newcomer in the storage arena, but it is the first solution of its kind to have been thought with Post-PC devices (smartphones and tablets) in mind from the very beginning.

It is extremely simple to use, it is fast, it has a ridiculously low memory footprint, it is compatible with iOS and Android, it has APIs for Swift, Objective-C, Kotlin, Java and C#, and needless to say, it is wildly popular among Android developers as well.

We are going to implement our contact management application now using Realm, and as you will see, this will be a very short section indeed.

Follow along

The code of this section is located in the Storage/Realm folder.

The first thing you need to do is, as expected, to add a line in the root build.gradle file. This will automatically download the required libraries and will make them available to your code.

Integrating Realm in your project
classpath "io.realm:realm-gradle-plugin:4.3.1"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
Use the root build.gradle file!

In the case of Realm, we must add the dependency directly at the level of the module, not of the application itself.

Once this is done, we must subclass our model classes from the io.realm.RealmObject class. This is the base class for all model objects that are stored at runtime in a Realm database.

Subclassing RealmObject
open class Person : RealmObject() { (1)
    var name: String? = null
    var age: Int = 0

    override fun toString(): String {
        return String.format(Locale.ENGLISH, "%s (%d)", name, age)
    }
}

Nothing mysterious here; just define the fields that you want, the getters and setters that you need, and you are good to go. Finally, to use Realm, just execute transactions and add, edit and remove your objects in those.

Manipulating Realm objects
Realm.init(this)
val realmConfiguration = RealmConfiguration.Builder().build()
Realm.setDefaultConfiguration(realmConfiguration)

realm = Realm.getDefaultInstance()

realm?.executeTransaction { realm ->
    val john = realm.createObject(Person::class.java)
    john.name = "John Smith"
    john.age = 40

    val mary = realm.createObject(Person::class.java)
    mary.name = "Mary Poppins"
    mary.age = 60
}
val person = realm?.where(Person::class.java)?.equalTo("age", 60L)?.findFirst()
Log.i("MainActivity", "Person found: " + person!!)
showPeople()

And that is it! Your data will be automatically saved in your device as you run your code. Realm even provides a free browser application for macOS (available through the Mac App Store) which allows developers to inspect the internal structure of their applications at any given time.

6.9. Room

Room is the latest ORM available for Android, and it was created by Google. It also uses SQLite as the underlying storage mechanism.

Follow along

The code of this section is located in the Storage/Room folder.

To use Room, you have to add the required entries in both build.gradle files:

Integrating Room in the project
maven { url 'https://maven.google.com' }
Integrating Room in the main module
implementation "android.arch.persistence.room:runtime:1.0.0"
annotationProcessor "android.arch.persistence.room:compiler:1.0.0"
kapt "android.arch.persistence.room:compiler:1.0.0"

Once this is done, you can define your entities, as usual:

Creating a Room entity
@Entity
class Person {

    @PrimaryKey(autoGenerate = true)
    var id: Long = 0

    @ColumnInfo(name = "name")
    var name: String? = null

    @ColumnInfo(name = "age")
    var age: Int = 0

    override fun toString(): String {
        return String.format(Locale.ENGLISH, "%s (%d)", name, age)
    }
}

You must also define classes for your database and your data access object (DAO):

Room database definition
@Database(entities = arrayOf(Person::class), version = 1)
abstract class AppDatabase : RoomDatabase() {
    abstract fun personDao(): PersonDao
}
Room DAO definition
@Dao
interface PersonDao {
    @get:Query("SELECT * FROM person")
    val all: List<Person>

    @Query("SELECT * FROM person WHERE id IN (:arg0)")
    fun loadAllByIds(userIds: IntArray): List<Person>

    @Query("SELECT * FROM person WHERE name LIKE :arg0 LIMIT 1")
    fun findByName(name: String): Person

    @Insert
    fun insertAll(vararg people: Person)

    @Delete
    fun delete(person: Person)

    @Query("DELETE FROM person")
    fun deleteAll()
}

Finally, you can use all of your infrastructure in your activities. Pay attention to the fact that Room will throw an exception if you try to access your database from the main thread; you should use an AsyncTask or any other mechanism to be able to execute your code in a background thread.

In this case, we will use a DispatchQueue – which of course does not exist in Kotlin, but we can make one wrapping HandlerThread instead.

Manipulating Room objects
queue.async {
    val personDao = database?.personDao()
    personDao?.insertAll(*people)
    val savedPeople = personDao?.all

    DispatchQueue.main.async {
        showPeople(savedPeople)
    }
}
DispatchQueue in Kotlin

If you are interested in knowing how to implement DispatchQueue in Kotlin, check the "HandlerThread" section of the "Multithreading" chapter later in this book.

6.10. Summary

Android offers a large array of options to store and retrieve information in a variety of persistent mediums. Whether as part of a bundle, as a downloaded file from the Internet, as a file stored in the internal or external storage, or as structured data in a database, there are quite a few ways to make sure your users will not forget anything.

The most common mechanism for Android apps to store structured data is SQLite, but this requires manually managing the schema of the information stored in the file. Android includes a SQLite wrapper which provides exactly this functionality.

If you need a bit more flexibility, you can opt for Room or OrmLite, both Object Relational Mappers that work perfectly well on Android devices. And of course there is Realm, a modern, fast option for structured on-device storage, offering compatibility with iOS devices as well, and a free viewer application for macOS.

Part 4: Sensors and Multimedia

Taking Advantage of Device Capabilities

Android devices feature an incredible range of sensors and hardware, and the operating system has very simple yet powerful APIs to consume their data. This part will dive into the interaction with many different sensors and how to take advantage of their multimedia capabilities.

7. Sensors

Android devices come bundled with a large array of useful sensors, just like iOS devices. Actually, some Android devices even carry more sensors than have ever been available on iPhones and iPads, like temperature or humidity sensors. This chapter will give you a quick overview of the most important sensors available, and how to access and consume the data they produce.

Run on Device!

The code samples of this chapter must be run on a real device! Although the Android Emulator is capable of emulating sensors as well, it is very important to test sensor-related code in real devices.

7.1. TL;DR

For those of you in a hurry, the table below summarizes the most important pieces of information in this chapter.

Table 13. Android Sensors
Android iOS

Framework

android.hardware

Core Motion & Core Location

Main class

SensorManager

CMMotionManager

Callback methods

SensorEventListener

Blocks

Sensor data

SensorEvent

CMGyroDataCMAccelerometerDataCMAttitude

Location

LocationManager

CLLocationManager

7.2. Getting a List of Sensors

Android devices are not all built equal. Some of them are bundled with a plethora of sensors, and some of them – usually the more affordable ones – include just a basic subset.

iOS developers are used to dealing with a much more stable hardware platform, and charts like James Dempsey’s iOS Device Summary or the iOS Support Matrix show how stable is the world we have to deal with.

In the case of Android, hardware diversity is the rule; in 2015 Google acknowledged more than 4000 different kinds of devices running Android, built by more than 400 different manufacturers and supported by 500 different mobile carriers all over the world.

This means that Android developers must absolutely test the existence of any sensors they intend to use in their devices, before actually using them.[2]

Follow along

The code of this section is located in the Sensors/SensorList folder.

To get a list of the available sensors in your own device, you can create a simple application with a ListView and populate it using the code shown below.

Obtaining a list of sensors in your Android device
val manager = getSystemService(Context.SENSOR_SERVICE) as SensorManager  (1)
var sensorList: List<Sensor>? = null
sensorList = manager.getSensorList(Sensor.TYPE_ALL)

val layout = android.R.layout.simple_list_item_1
var adapter: ListAdapter? = null
if (sensorList != null) {
    adapter = ArrayAdapter(this, layout, sensorList)
}
listView.adapter = adapter
1 The android.hardware.SensorManager class plays a similar role in Android to that of CMMotionManager in iOS.

Running this code in an actual Android device yields the screen shown in image below.[3]

Sensors available in a OnePlus 3 Android Device
Figure 67. Sensors available in a OnePlus 3 Android Device

The following table shows in detail the different sensors available in the Android operating system.

Table 14. Available Android Sensors
Name Sensor Identifier Type Unit Available in Emulator

Linear Acceleration

TYPE_LINEAR_ACCELERATION

Software or Hardware

m/s2

Accelerometer

TYPE_ACCELEROMETER

Hardware

m/s2

Yes

Air Pressure

TYPE_PRESSURE

Hardware

hPa or mbar

Yes

Gravity

TYPE_GRAVITY

Software or Hardware

m/s2

Gyroscope

TYPE_GYROSCOPE

Hardware

rad/s

Humidity

TYPE_RELATIVE_HUMIDITY

Hardware

%

Yes

Light

TYPE_LIGHT

Hardware

lx

Yes

Magnetometer

TYPE_MAGNETIC_FIELD

Hardware

μT

Yes

Orientation

TYPE_ORIENTATION

Software

rad

Proximity

TYPE_PROXIMITY

Hardware

cm

Yes

Rotation

TYPE_ROTATION_VECTOR

Software or Hardware

Temperature

TYPE_AMBIENT_TEMPERATURE

Hardware

°C (Celsius)

Yes

As you can see, some of them are totally unheard of in iOS! Please note that not all sensors are available in all models of Android devices; always check for the availability of a sensor before attempting to consume its data!

Taking Screenshots in Android

To take a screenshot from your Android device programmatically and then to transfer it to your computer, just run the following adb commands:

adb devices -l

This will show a list of available Android devices. You will need the ID of your target device if you have several devices connected or emulators running at the same time! Create the screenshot as follows:

adb -s xxxxxxxx shell /system/bin/screencap -p /sdcard/screenshot.png

The -s parameter requires the device ID you obtained in the previous step. Finally, copy the screenshot to your local computer:

adb -s xxxxxxxx pull /sdcard/screenshot.png screenshot.png

7.3. Using the Accelerometer

Longtime iOS developers will remember a time when we had to use the UIAccelerometer in the UIKit framework to access the data coming from the accelerometer… Since iOS 4, however, the functionality was moved into the Core Motion framework, where it has been available ever since.

The SensorManager Class

In Android we use the android.hardware.SensorManager class as the entry point for accessing data from the accelerometer. As you might expect, the usual workflow consists in creating a manager object, and setting another object as the "listener" (or "delegate" in iOS speak) of the manager object. Once the connection is done, the listener will periodically receive the relevant data, to manipulate it as needed.

Follow along

The code of this section is located in the Sensors/Accelerometer folder.

In the case of Android, listeners must implement the SensorEventListener interface, which defines the required methods expected by the sensor manager. Of these, the most important method is onSensorChanged() which is called periodically with a parameter of type SensorEvent. This object contains a .values array with a variable number of float values; from one (in the case of the light sensor) to three (in the case of movement sensors).

API Uniformity

No matter which sensor you use in Android, the delegate interface and the SensorEvent parameter are always the same. What changes from sensor to sensor is the meaning of the .values property in the sensor event. This is a different behavior than the one in iOS, where different data structures such as CMGyroData, CMAccelerometerData or CMAttitude are each used for different sensors.

The listing below shows how to set a simple activity as the listener for the accelerometer events.

Consuming accelerometer data
class MainActivity : AppCompatActivity(), SensorEventListener {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        (1)
        sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
        if (sensorManager != null) {
            accelerometer = sensorManager?.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
        }
    override fun onSensorChanged(sensorEvent: SensorEvent) {  (2)
        val values = sensorEvent.values.clone()

        // Uncomment this to filter the values with a low-pass filter
        // values = lowPass(values);

        val xValue = values[0]
        val yValue = values[1]
        val zValue = values[2]

        view_x.text = String.format(Locale.ENGLISH, "%1.1f", xValue)
        view_y.text = String.format(Locale.ENGLISH, "%1.1f", yValue)
        view_z.text = String.format(Locale.ENGLISH, "%1.1f", zValue)

        val vector = Math.sqrt((xValue * xValue +
                yValue * yValue +
                zValue * zValue).toDouble())
        view_vector.text = String.format(Locale.ENGLISH, "%1.1f", vector)
        if (vector < 3) {
            activity_main.setBackgroundColor(Color.RED)
        } else if (vector < 10) {
            activity_main.setBackgroundColor(Color.GREEN)
        } else if (vector < 40) {
            activity_main.setBackgroundColor(Color.YELLOW)
        } else {
            activity_main.setBackgroundColor(Color.RED)
        }
    }
    override fun onResume() {    (3)
        super.onResume()
        sensorManager?.registerListener(this,
                accelerometer,
                SensorManager.SENSOR_DELAY_NORMAL)
    }

    override fun onPause() {     (4)
        super.onPause()
        sensorManager?.unregisterListener(this)
    }

    protected fun lowPass(input: FloatArray): FloatArray {    (5)
        val acceleration = floatArrayOf(0f, 0f, 0f)
        val FACTOR = 0.1f

        for (i in 0..2) {
            acceleration[i] = input[i] * (1.0f - FACTOR)
        }
        return acceleration
    }
1 Here we request a SensorManager instance to the operating system, and then we use that object to obtain a reference to the accelerometer.
2 This method is called every time that the accelerometer has new data.
3 This activity registers itself as a listener when it becomes active…
4 …and of course, being a good citizen, it deregisters itself before going to the background.
5 A small filter method, to reduce the noise in the signal retrieved from the accelerometer.

The application is built in such a way that the screen becomes yellow when you shake your device a little bit, red when you shake it a lot (or when you let it fall freely!) and green when it stays quiet.

Disclaimer!

Pay attention not to break your device! The usual disclaimer applies here: I am not responsible of any destruction caused by this sample or its use.

The execution of this code is shown here:

Consuming accelerometer data
Figure 68. Consuming accelerometer data

7.4. Using the Compass

Accessing data from the compass (or, using a technically more correct term, the magnetometer requires using the exactly same API as we have seen in the previous section.

We are going to jump directly to the code required to make a nice compass out of that information, which actually also requires the use of the accelerometer information. The snippet below shows how to use the information to finally be able to set the rotation of the image that represents the compass needle.

Follow along

The code of this section is located in the Sensors/Compass folder.

Consuming information from the magnetometer
override fun onSensorChanged(sensorEvent: SensorEvent) {
    if (sensorEvent.sensor == accelerometer) {
        lastAccData = sensorEvent.values.clone()
        isAccDataReady = true
    } else if (sensorEvent.sensor == magnetometer) {
        lastMagnData = sensorEvent.values.clone()
        isMagnDataReady = true
    }

    if (isAccDataReady && isMagnDataReady) {
        val rotationMatrix = FloatArray(9)
        SensorManager.getRotationMatrix(rotationMatrix, null,
                lastAccData,
                lastMagnData)

        val orientation = FloatArray(3)
        SensorManager.getOrientation(rotationMatrix, orientation)

        val radians = orientation[0]
        val degrees = Math.toDegrees(radians.toDouble()).toFloat()
        val rotation = (degrees + 360) % 360
        view_compass.rotation = -rotation
    }
}

In the case of the compass, to be able to read properly the current inclination of the device compared to the magnetic axis of Earth we need to take into account the information from the accelerometer as well. We pass this information to the SensorManager.getRotationMatrix() method, and later we pass that result to SensorManager.getOrientation() method, which ultimately provides the rotation information that we need to apply to the animated object on the screen.

7.5. Location Information

The release of the iPhone 3G with a GPS chip, and then the massive adoption of Android, have made location-bound applications extremely popular on all mobile app stores. The reason for the proliferation of location-enabled applications is most probably due to the ease of access to location information from the devices. We know that in iOS, the CLLocationManager class in the Core Location framework provides all the information required.

In the case of Android, the situation is a bit more complicated, since there are several ways to access this information at this moment in time. Historically, location information was available through the android.location.LocationManager class, but lately the preferred method is through the com.google.android.gms.location.LocationServices class. This last class is available through the Play Services library, which is available in all Android devices including Google’s own Play Store – that is, most of them.

We are going to learn in this section how to use the information provided by this library and how to consume location information properly.

Follow along

The code of this section is located in the Sensors/Location folder.

First things first; as you might imagine, we need to modify the app Gradle file with some new libraries.

Adding Google Play Services as a dependency
implementation 'com.google.android.gms:play-services-location:10.0.0'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"

Once this is ready, the symbols of this library will be available throughout the project for us to use. But then again, because consuming location information represents a potential privacy problem, we have to request the permissions in the AndroidManifest.xml file.

Locations Permission in the AndroidManifest.xml file
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.INTERNET"/>

We are going to make our MainActivity class a listener for location events; we are just going to implement the GoogleApiClient.ConnectionCallbacks and GoogleApiClient.OnConnectionFailedListener interfaces.

Implementing the listener interfaces
class MainActivity : AppCompatActivity(),
        GoogleApiClient.ConnectionCallbacks,
        GoogleApiClient.OnConnectionFailedListener {

Then we need to create an instance of the GoogleApiClient class. Not only that, but we are also going to make the current activity connect and disconnect only when the activity becomes visible; this will save battery power on the device and will make a better use of the resources.

Creating an instance of GoogleApiClient
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    googleApiClient = GoogleApiClient.Builder(this)
            .addConnectionCallbacks(this)
            .addOnConnectionFailedListener(this)
            .addApi(LocationServices.API)
            .build()
}

override fun onStart() {
    super.onStart()
    googleApiClient?.connect()
}

override fun onStop() {
    super.onStop()
    googleApiClient?.disconnect()
}

As soon as the GoogleApiClient instance is connected to the Google backend systems, our application will be notified and we should, since Android 6, ask the user for permissions once again. This is a new recommended practice, and it only involves a couple more lines of code.

Connection callbacks and permission checks
override fun onConnected(bundle: Bundle?) {

    val fine = Manifest.permission.ACCESS_FINE_LOCATION
    val coarse = Manifest.permission.ACCESS_COARSE_LOCATION
    if (!hasPermission(fine) && !hasPermission(coarse)) {
        val permissions = arrayOf(fine, coarse)
        ActivityCompat.requestPermissions(this, permissions, 0)
        return
    }
    retrieveLocation()
}

private fun hasPermission(permission: String): Boolean {
    val check = ActivityCompat.checkSelfPermission(this, permission)
    return check == PackageManager.PERMISSION_GRANTED
}

override fun onRequestPermissionsResult(requestCode: Int,
                                        permissions: Array<String>,
                                        grantResults: IntArray) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults)
    if (requestCode == 0) {
        var granted = true

        for (grantResult in grantResults) {
            granted = granted && grantResult == PERMISSION_GRANTED
        }

        if (granted) {
            retrieveLocation()
        }
    }
}

After all of this back and forth, we are finally ready to process some location information. The LocationServices.FusedLocationApi.requestLocationUpdates() takes a callback object as a parameter, and inside of it we are going to inspect the values we are interested in, like in this case, the latitude and the longitude of the current user.

Finally accessing the location information
private fun retrieveLocation() {
    try {
        val request = LocationRequest.create()
        request.priority = LocationRequest.PRIORITY_HIGH_ACCURACY
        request.numUpdates = 1
        request.interval = 0

        LocationServices.FusedLocationApi.requestLocationUpdates(googleApiClient,
                request
        ) { location ->
            val lat = location.latitude
            val lng = location.longitude
            latitudeView.text = lat.toString()
            longitudeView.text = lng.toString()
        }
    } catch (e: SecurityException) {
        Log.e("MainActivity", "Error: ", e)
    }

}

The Play Store API is not as straightforward as the one offered by CLLocationManager, but the end result is the same.

7.6. Retrieving Address Information

Using the code of the previous section as a basis, we are going to extend it and ask Google for the current address of our location in Planet Earth. This will involve a few more callback methods, and a new concept: Intent Services, used to perform long-running operations in a separate thread.

Follow along

The code of this section is located in the Sensors/Address folder.

The bulk of the application is pretty much the same as the one from the previous section; same permissions, same callbacks, same Google Play Services client.

Launching the address retrieval as a ServiceIntent request
protected fun startIntentService() {
    val intent = Intent(this, AddressService::class.java)
    intent.putExtra(Constants.RECEIVER, resultReceiver)
    intent.putExtra(Constants.LOCATION_DATA_EXTRA, lastLocation)
    startService(intent)
}

This will instantiate and launch a new Intent Service of the AddressService class. Intent Services are used in Android as an alternative to AsyncTask instances, and are more appropriate for some long-running tasks.

Inside of the AddressService we unpack the latitude and longitude passed by the caller activity, and we proceed to call the geocoder service.

Using a Geocoder object to retrieve address data
val geocoder = Geocoder(this, Locale.getDefault())
addresses = geocoder.getFromLocation(
        location.latitude,
        location.longitude,
        1)

Once the AddressService instance has received the required information, it passes it back to the calling activity as follows.

Passing information back from the Intent Service to the Activity
private fun deliverResultToReceiver(resultCode: Int, message: String) {
    val bundle = Bundle()
    bundle.putString(Constants.RESULT_DATA_KEY, message)
    receiver?.send(resultCode, bundle)
}

This information is made available to the activity through a subclass of the android.support.v4.os.ResultReceiver class, in our case the AddressResultReceiver class.

Receiving information from an Intent Service
internal inner class AddressResultReceiver(handler: Handler) : ResultReceiver(handler) {

    override fun onReceiveResult(resultCode: Int, resultData: Bundle?) {

        // Display the address string
        // or an error message sent from the intent service.
        val address = resultData!!.getString(Constants.RESULT_DATA_KEY)
        view_address.text = address
    }
}

7.7. Summary

Android devices are bundled with lots of different sensors, but thankfully the API used to access most of them is centralized in a single class, which provides uniformity and makes consuming their data much simpler. It is very important to query the current device for the list of available sensors, since not all Android devices are bundled with the same hardware, and then, only if the sensor is available, your code should proceed.

Android provides a native class for accessing location information, but most applications use the one provided in the Google Play Services library, which is available in all the devices that include the Google Play Store. This library is updated independently of the Android operating system, through the Play Store application, a fact which seemingly updates older devices to new functionality in spite of the slow upgrade rate of newer versions of the operating system.

Finally, the new permission request system in place since Android 6 means that applications must ask for permission at runtime, before accessing critical information.

8. Multimedia

Android devices are fully fledged multimedia stations. People use them to connect to their friends in social media, posting pictures in Instagram, sharing videos on YouTube and even recording and publishing podcasts. This chapter will provide a short introduction to the most important APIs available to developers for managing image, audio and video data.

8.1. TL;DR

For those of you in a hurry, the table below summarizes the most important pieces of information in this chapter.

Table 15. Android Multimedia Capabilities
Android iOS

Display image

android.widget.ImageView

UIImageView

Display video

android.widget.VideoView

MPMoviePlayerViewController

Viewer application

Gallery

Photos

Image picker

Intent.ACTION_PICK

UIImagePickerController

Audio recorder

android.media.MediaRecorder

AVAudioRecorder

Audio player

android.media.MediaPlayer

AVAudioPlayer

Text to speech engine

android.speech.tts.TextToSpeech

AVSpeechSynthesizer

8.2. Taking Pictures

Arguably, the simplest and most common thing we all do with our smartphones, Android or not, is to take pictures. Smartphone cameras have become so popular that some camera vendors had to adapt their strategies for this new world, and new business models and startups have appeared and flourished around those little cameras in our pockets.

So let us start our discussion by learning how to retrieve an image from the Android camera. Turns out, it is as simple as doing it on iOS.

Follow along

The code of this section is located in the Multimedia/Photos folder.

The first thing you have to do in your project is to add the proper permissions, as usual. In this case we are going to notify the operating system that this application requires a camera; this has the effect of filtering out from the results in the Play Store in case a device does not include a camera (does anyone buy a smartphone without a camera these days?)

Adding permissions in the AndroidManifest.xml file
<uses-feature android:name="android.hardware.camera"
              android:required="true" />

Once this is done, you can request the camera activity from within your code just by creating an Intent instance with the proper parameters.

Requesting the camera with an Intent
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
if (intent.resolveActivity(packageManager) != null) {
    startActivityForResult(intent, CODE)
}

Once the user has taken the picture, your main activity can simply retrieve the image from the intent using the "data" key (in this case, shown as the KEY constant in the code)

Retrieving the image from the Intent
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
    super.onActivityResult(requestCode, resultCode, data)

    if (requestCode == CODE && resultCode == Activity.RESULT_OK) {
        val extras = data.extras
        if (extras != null) {
            val imageBitmap = extras.get(KEY) as Bitmap
            view_picture.setImageBitmap(imageBitmap)
        }
    }
}

companion object {
    private val CODE = 1
    private val KEY = "data"
}

The CODE constant is an integer, used to distinguish the intent from other data requests performed in the same activity.

Gallery Application

As you might expect, images and videos taken by the user are automatically saved and visible in the "Gallery" application, just like in the "Photos" application in iOS.

8.3. Recording Video

As you will see, using the camera to record video is not much more complicated. The difference is the type of Intent, and the fact that instead of retrieving the whole data generated by the operation, we are going to receive a Uri from the intent, which we will feed to an instance of the android.widget.VideoView class.

Follow along

The code of this section is located in the Multimedia/Videos folder.

The permissions we need to add in our AndroidManifest.xml is the same shown previously. Once this is done, just create the Intent and start it.

Requesting the camera for videos
val intent = Intent(MediaStore.ACTION_VIDEO_CAPTURE)
if (intent.resolveActivity(packageManager) != null) {
    startActivityForResult(intent, CODE)
}

Once the user has taken the picture, your main activity can simply retrieve the image from the intent using the "data" key (in this case, shown as the KEY constant in the code)

Retrieving the Uri to the video
override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent) {
    super.onActivityResult(requestCode, resultCode, intent)

    if (requestCode == CODE && resultCode == Activity.RESULT_OK) {
        val videoUri = intent.data
        videoView.setVideoURI(videoUri)
        videoView.start()
    }
}

companion object {
    private val CODE = 1
}

To be able to control the playback of the video, please remember to always add an instance of android.widget.MediaController to your activity; otherwise the user will not be able to launch, pause or stop the playback of the video.

Adding a MediaController to the VideoView
val controller = MediaController(this)
videoView.setMediaController(controller)
VideoView and Layouts

The VideoView class, when inside a RelativeLayout, does not keep the aspect ratio of the video; to solve this problem, remember to always insert it inside a LinearLayout, which solves the problem. You can then embed the LinearLayout inside your RelativeLayout, if needed.

8.4. Picking Images

Asking the user to select a picture from his local gallery is another very simple task, which as you might imagine requires asking for permissions and to create an intent.

You might have noticed, however, that in some cases it is enough to ask for the permission on the AndroidManifest.xml file, while in other cases we have had to ask for permission in our code as well. Why is this?

The reason for this is that the permission model in Android API 23+ has changed, and there are now two types of permissions:

  1. Normal permissions, which have very little privacy risk (as assessed by Google) such as accessing the internet, the Bluetooth stack, setting alarms or vibrating. These can be requested safely in the AndroidManifest.xml file without further due, and the operating system grants them automatically upon installation.[4]

  2. Dangerous permissions, on the other hand, can raise serious privacy issues, and as such since API 23 developers must explicitly ask for them at runtime. Examples of these permissions include those to access the calendar, the contacts, the microphone, the location or the telephony subsystem.[5]

In our code we are going to need to access the photo library, which requires a "dangerous" permission, so we will need to add the code required to request that permission.

Follow along

The code of this section is located in the Multimedia/PickImage folder.

We are going to modify our AndroidManifest.xml file to include the required permissions, a compatibility measure with devices running older versions of Android.

Adding permissions in the AndroidManifest.xml file
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

Then we need to add the required permission request in our activity.

Requesting permissions at runtime
val permission = ActivityCompat.checkSelfPermission(this,
        Manifest.permission.WRITE_EXTERNAL_STORAGE)

if (permission != PackageManager.PERMISSION_GRANTED) {
    // We don't have permission so prompt the user
    ActivityCompat.requestPermissions(
            this,
            PERMISSIONS_STORAGE,
            REQUEST_EXTERNAL_STORAGE
    )
}

We can then proceed and ask the system to show the image picker for the current user; but these days there are many different sources for images. The current user might have a collection of pictures in a Dropbox folder that she might want to use, so why not giving them the chance to use them?

The listing below shows how to create a chooser intent, which will display a very handy menu to your user, allowing her or him to pick an image from a large variety of sources, as shown in the following image. Choosing "Android System" will display the standard "Photos" or "Gallery" applications, while the "Files" option will bring the "Files" application, allowing the user to select an image from Dropbox, Google Drive, a folder in the local device or any other location available.

Showing an image picker with multiple sources
val getIntent = Intent(Intent.ACTION_GET_CONTENT)
getIntent.type = "image/*"

val picker = Intent(Intent.ACTION_PICK,
        android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
picker.type = "image/*"
val intents = arrayOf(picker)

val chooser = Intent.createChooser(getIntent, "Select Image")
chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, intents)

startActivityForResult(chooser, CODE)
Image picker with multiple sources
Figure 69. Image picker with multiple sources

Once the user chooses an image, the activity can retrieve its Uri and display it in the application.

Retrieving the Uri to the picture
override fun onActivityResult(requestCode: Int,
                              resultCode: Int,
                              data: Intent) {
    super.onActivityResult(requestCode, resultCode, data)

    if (requestCode == CODE && resultCode == Activity.RESULT_OK) {
        val uri = data.data
        view_picture.setImageURI(uri)
    }
}

companion object {
    private val CODE = 1
    private val REQUEST_EXTERNAL_STORAGE = 1
    private val PERMISSIONS_STORAGE = arrayOf(
            Manifest.permission.READ_EXTERNAL_STORAGE,
            Manifest.permission.WRITE_EXTERNAL_STORAGE)
}

8.5. Recording and Playing Audio

Android devices usually feature a microphone, most commonly associated for voice calls, and speakers, generally used for music playback. But nothing – apart from pesky but necessary permissions – prevent developers to access them and to arbitrarily record audio. This section will show how simple it is to record and play audio on an Android application.

Follow along

The code of this section is located in the Multimedia/Recorder folder.

As usual, we are going to add the required permissions to our AndroidManifest.xml file.

Requesting Permissions in the AndroidManifest.xml file
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>

Of course, we have to deal with permissions at runtime as well since API 23, so let us do that in the onStart() method.

Requesting permissions at runtime
val write = Manifest.permission.WRITE_EXTERNAL_STORAGE
val record = Manifest.permission.RECORD_AUDIO
if (!hasPermission(write) || !hasPermission(record)) {
    val permissions = arrayOf(write, record)
    setState(ApplicationState.FORBIDDEN)
    ActivityCompat.requestPermissions(this, permissions, 0)
    return
}
setState(ApplicationState.IDLE)

By the way, the hasPermission() method is a little helper.

The code of the hasPermission() helper method
private fun hasPermission(permission: String): Boolean {
    val check = ActivityCompat.checkSelfPermission(this, permission)
    return check == PackageManager.PERMISSION_GRANTED
}

We have to check the response from the user, of course; if the permissions were granted, then proceed as expected. Otherwise, just disable the user interface, because there is not much to do.

The callback from the operating system granting permissions – or not
override fun onRequestPermissionsResult(requestCode: Int,
                                        permissions: Array<String>,
                                        grantResults: IntArray) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults)

    if (grantResults.size > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

        setState(ApplicationState.IDLE)
    } else {
        setState(ApplicationState.FORBIDDEN)
        Toast.makeText(this,
                "Unfortunately, cannot do much without permissions...",
                Toast.LENGTH_LONG).show()
    }
}

If the user has granted us the permissions, the next step would be to record some sounds; for that, the android.media.MediaRecorder is straightforward to setup and launch.

Setting up a recorder object
recorder = MediaRecorder()
recorder?.setAudioSource(MediaRecorder.AudioSource.MIC)
recorder?.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
recorder?.setOutputFile(fileName)
recorder?.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB)
recorder?.prepare()

setState(ApplicationState.RECORDING)
recorder?.start()

The recorder is set to write an MP3 file directly on the file system; if this file exists, the PLAY will be enabled automatically, and we can launch the playback. For this we use an instance of android.media.MediaPlayer, and off we go.

Setting up a player object
player = MediaPlayer()
player?.setDataSource(fileName)
player?.setOnCompletionListener { stop() }
player?.prepare()

setState(ApplicationState.PLAYING)
player?.start()
Similarities with iOS

As the attentive reader might have discovered by now, the android.media.MediaRecorder and MediaPlayer classes have extremely similar interfaces to their iOS counterparts, AVAudioRecorder and AVAudioPlayer.

The player stops automatically when the sound file reaches its end, but we can always hit the STOP button and cancel all operations. In this case we have to release our player (or our recorder) and set those references to null, to ensure that the garbage collector will take care of those old objects for us.

Stopping playback
private fun stop() {
    if (ApplicationState.RECORDING == state && recorder != null) {
        recorder?.stop()
        recorder?.release()
        recorder = null
    } else if (ApplicationState.PLAYING == state && player != null) {
        player?.stop()
        player?.release()
        player = null
    }
    setState(ApplicationState.IDLE)
}

8.6. Playing Music

Android applications can have special components called Services, which do not have a user interface and which provide a mechanism to execute long-running code that should not be stopped when the user leaves the application. For example, long downloads, or music playback are excellent examples for tasks that should be accomplished through services.

Follow along

The code of this section is located in the Multimedia/Music folder.

Services are executed in the same UI thread than the activity that calls them. This means that if you are going to have CPU-intensive code in your service, you might want to use threading, AsyncTask instances or other means to avoid blocking your user interface.

You can create a service just by subclassing the android.app.Service class:

A music playing service
class MusicService : Service() {
    private var player: MediaPlayer? = null

    override fun onBind(intent: Intent): IBinder? {
        return null
    }

    override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
        val play = intent.getBooleanExtra("PLAY", true)
        if (play) {
            createPlayerIfNeeded()
            player?.start()
        } else {
            player?.pause()
        }

        return Service.START_NOT_STICKY
    }

    override fun onDestroy() {
        super.onDestroy()
        player?.stop()
    }

    private fun createPlayerIfNeeded() {
        if (player == null) {
            player = MediaPlayer.create(this, R.raw.song)
            player?.setOnCompletionListener {
                val notification = Intent("STOPPED")
                val manager = LocalBroadcastManager.getInstance(this)
                manager.sendBroadcast(notification)
                stopSelf()
            }
        }
    }
}

Services have a set of defined lifecycle methods, just like Activities and Fragments do.

Once created, you must notify the operating system of its existence in order to be able to use it, and this happens as usual in the AndroidManifest.xml file.

Manifest declaring a service
<service
    android:name=".MusicService"
    android:enabled="true"
    android:exported="true" />

As usual in Android, the way to start and stop a service is using instances of android.content.Intent. Just create one and pass it to either startService() or stopService(). In this case, since we want the music to pause rather than to stop, we will use startService() for both cases, passing a different boolean parameter each time.

Starting a service
val intent = Intent(this, MusicService::class.java)
intent.putExtra("PLAY", true)
startService(intent)

When you call startService() in your activity, your service is notified through the onStartCommand() method shown above.

If the music stops playing without user intervention, we would like the activity to receive a notification so that it can set the enabled property of the buttons properly. For that we will use the android.support.v4.content.LocalBroadcastManager class from the Support Library.

Receiving a notification when a service stops
private val receiver = object : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        buttonsPlaying(false)
    }
}

8.7. Speech Synthesizer

To close this chapter, we are going to learn a very simple API that drives the built-in text-to-speech system of the Android operating system, available since Lollipop (API 21). This API is very similar to that of the AVSpeechSynthesizer class in iOS, as you will see.

Follow along

The code of this section is located in the Multimedia/TextToSpeech folder.

API 21 required

This API is only available since API 21 (Lollipop, Android 5.0) so make sure your app/build.gradle file points to the right version.

Using the API involves using the android.speech.tts.TextToSpeech class.

Using the TextToSpeech class
class MainActivity : AppCompatActivity(), TextToSpeech.OnInitListener {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val tts = TextToSpeech(this, this)
        tts.language = Locale.US

        speakButton.setOnClickListener {
            tts.speak(textField.text,
                    TextToSpeech.QUEUE_ADD,
                    null,
                    "test")
        }

        clearButton.setOnClickListener { textField.setText("") }
    }

    override fun onInit(i: Int) {
        speakButton.isEnabled = true
    }
}

After running the application on a device, the user can type any text in English in the TextView shown on the screen, and the device will speak that text back.

8.8. Summary

Android devices offer a large array of possibilities when it comes to image, audio and video. Using the proper permissions and intents, applications can prompt users to take and select images, videos, and to record and play audio files.

However interesting these capabilities are, they raise important privacy concerns, and since API 23 developers are expected to request permissions to access these features at runtime; this means that the classical permissions in AndroidManifest.xml are no longer enough. There is an important distinction between "normal" and "dangerous" permissions; the former are granted automatically by the operating system upon installation, while the latter must be requested explicitly in newer versions of the operating system.

There is also a very easy to use text-to-speech system built in the operating system, which opens the door to many interesting applications.

Part 5: High Quality Apps

Raising The Bar

Mobile applications used to be small projects. As time passed and as their usefulness grew, Android applications became incredibly big enterprises, involving tens of developers and spanning through tens or hundreds of lines of code. This part will delve into some useful techniques to ensure that your codebases stay flexible, maintainable and that your team retains its sanity and happiness.

9. Architecture

Mobile applications are only small in terms of screen size. As they become popular and increase their feature set, their code can become more and more complex, and the teams building them are at risk of increasing their technical debt every day.

This book includes a whole section about code quality, but it is important to have a discussion on code quality through this chapter about architecture. Right now you know how to create applications, with activities and fragments, and you also feel confident about adding networking and storage code; it is now time to organise that code for evolution.

9.1. TL;DR

For those of you in a hurry, the table below summarizes the most important pieces of information in this chapter.

Table 16. Android Application Architecture
Android iOS

Notification center

LocalBroadcastManager

NSNotificationCenter

Flexible designs

Interface-oriented programming

Protocol-oriented programming

Dependency injection

Dagger

Typhoon – Swinject – Cleanse …

ReactiveX

RxJava & RxAndroid

RxSwift

Observing data

rx.Observable

willSet & didSet – KVO

9.2. Principles of Good Android Architecture

This section will introduce some techniques of Android application architecture that will make your code more manageable and easier to support.

Organise your UI around Fragments

Organising your UI code in fragments is by far the most important tip you can keep in mind while you build your user interface. By design, Android activities are what Apple used to call "screenfuls of information," which presents problems when you port your applications to larger screens, such as tablets or even large phones.

Activities should only host fragments and never contain any "business logic"; in many cases, for example when creating layouts for smartphones, they will only hold one big fragment that takes the whole screen. In the case of tablets, there might be many fragments sharing the screen space at any given time.

By using fragments, you will have the freedom to organise your user experience in different ways, and this fact will bring an additional benefit to your code: modularity, separation of concerns, and easier maintainability.

Save Activity State

One of the most radically different features of Android compared to iOS is the fact that activities are destroyed and recreated when users change the orientation of their devices. This is something that puzzles iOS developers to no end when they start writing applications for this platform.

However, as surprising and annoying as it is, there is a benefit hiding beneath the hood. Just like in iOS, the Android operating system reserves its right to destroy any application and its associated activities at any given time, depending on the current memory requirements of ths system.

By adding the required state management code to your activities, you will actually be able to recover gracefully from low memory conditions, in which your application might be forced to quit. As soon as the user reselects your activity, you will be able to restore the current application state gracefully, and this is something that your user will be thankful for.

Listen to Memory Warnings

The memory management scheme in Android devices is not very different from that of iOS; in both cases, the operating system keeps an eye on the overall memory consumption of all applications, giving some privilege to the one that is currently in the foreground. If the memory requirements of any application or service becomes critical, the operating system reverse its right to start removing applications from memory, but before this happens, it will signal all applications with "memory warnings" (does it ring a bell?)

All applications should listen to these memory warnings at all time, and behave properly by releasing memory when required. The same heuristics apply to Android as to iOS:

  • Release early and often those objects you will not need anymore.

  • Whatever can be cached, should be cached.

  • Whatever can be recalculated, should be recalculated.

  • Remember that lazy loading is your friend, and never allocate more than you really need.

Please refer to the "Out Of Memory When Using Graphics" section in chapter "Graphics" of this book, which contains an extensive explanation of common low-memory situations in Android devices (hint, it is not very different from iOS) and some strategies to solve them, as well as the APIs involved.

Organise your Code in Packages

Android Studio makes it really easy to organise your code in packages, reflecting logical groups of classes, enums or interfaces. Isolating the code components in separate groups could help you in the future, for example to reuse particular components in another application.

"Protocol Oriented Programming" in Kotlin

Arguably, one of the most popular new concepts brought by Swift to iOS and Cocoa is that of "Protocol Oriented Programming", a set of techniques to ensure that your code is flexible, adaptive and manageable.

However excited as Swift developers could be, this idea is, historically speaking, hardly new. Scott Meyers was telling C++ developers to use "Abstract Base Classes" (ABCs) back in the 90s. Bloch also mentioned in his book the importance of "coding against interfaces" in Java.

Whatever name you give to this technique, it always boils down to the following rule: always try to use the most abstract possible type for your variables and objects. By following this simple principle, you will ensure your architecture is flexible enough to be used in other contexts, with other concrete types, but always with testability and type-checking in mind.

Protocol or Interface Extensions

Please keep in mind that one of the basic principles of protocol-oriented programming in Swift, namely protocol extensions, is available in Kotlin. This means that you can use many of the idioms you are already familiar with in Swift in your Kotlin applications.

9.3. Model View View Model

The "Model View View Model" architecture, usually referred to as MVVM, provides an interesting level of abstraction for complex applications. Originally developed by Microsoft for its ASP.NET web application framework, this architecture has become popular in many other contexts and technologies.

Follow along

The code of this section is located in the Architecture/MVVM folder.

The idea behind the MVVM architecture is the existence of a "ViewModel" family of classes, and in the case of Android, the use of "data binding" in the XML layout files, to automatize the update of values in the user interface as soon as the values of our model change.

To use Android Data Binding, we must first modify our module build.gradle file with the following entries:

Gradle file to enable data binding
apply plugin: 'kotlin-kapt'

android {
    dataBinding {
        enabled = true
    }

Once this is done, we are going to modify the main layout of our activity to use Data Binding. This requires us to wrap the existing ConstraintLayout with a higher object <layout> with the capability of specifying data variables:

Layout file with data binding variables
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"> (1)

    <data>
        <variable
            name="viewModel"
            type="training.akosma.mvvm.ViewModel" /> (2)
    </data>

    <android.support.constraint.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context="training.akosma.mvvm.MainActivity">
        (3)
        <SeekBar
            android:id="@+id/seekBar"
            style="@style/Widget.AppCompat.SeekBar.Discrete"
            android:layout_width="258dp"
            android:layout_height="20dp"
            android:layout_marginBottom="8dp"
            android:layout_marginEnd="8dp"
            android:layout_marginStart="8dp"
            android:max="10"
            android:onProgressChanged="@{viewModel::onProgressChanged}"
            android:progress="@{viewModel.progress}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/textView" />
1 This <layout> tag wraps the existing ConstraintLayout and provides data binding features to our views.
2 This section specifies the variables that can be modified from the source code.
3 This SeekBar is databound; whenever the user changes its state, a method is called in the associated viewModel; inversely, this layout now "observes" the progress field of the view model, and updates the seekbar accordingly.

Then we need to create the required interfaces and implementation, so that our View Model layer is as flexible as possible:

ViewModel interface
interface ViewModel {
    var model: Model?
    val text: ObservableField<String>
    val progress: ObservableField<Int>

    fun onProgressChanged(seekBar: SeekBar, i: Int, b: Boolean)
}
ViewModel implementation
class ViewModelImpl : ViewModel {
    var backingModel: Model? = null

    override var model: Model?
        get() = backingModel
        set(value) {
            backingModel = value
            progress.set(backingModel?.age)
            text.set(backingModel?.toString())
        }

    override val text = ObservableField<String>()
    override val progress = ObservableField<Int>()

    override fun onProgressChanged(seekBar: SeekBar, i: Int, b: Boolean) {
        model?.age = i
        text.set(model?.toString())
    }
}

Finally, we use our activity to wire everything together.

Activity code wiring the view model implementation
viewModel = ViewModelImpl()
val binding: ActivityMainBinding   (1)
        = DataBindingUtil.setContentView(this, R.layout.activity_main)
binding.setVariable(BR.viewModel, viewModel)
binding.executePendingBindings()

This mechanism extends the use of the view, to connect it to an instance of a class implementing the ViewModel interface; this makes the whole system very flexible, since we can at any time assign different implementations, changing the behaviour of the system. Even better, some of the boilerplate code that wires views to their event handlers is hidden in the layout XML file, and last but not least, we can test our implementations separately.

The downside of this approach is the added complexity, and the difficulty to discover the behaviours embedded inside of the layout XML files for new team members.

9.4. Dagger

If you have been following closely the previous section, you will know by now that Kotlin interfaces are the key element for code flexibility. Using interfaces, you can replace objects of one implementation with others featuring a different one; the only requirement being that both objects share the same interface.

But let us take this concept a bit further down the road. Let us say that your hobby is electronics and one weekend you decide that you want to build a calculator. You go to the hardware store, you buy an LED display, a couple of labeled plastic keys, some logic components and you wire all of them on the backyard. Of course, all of these components are heavily standardized, and as along as they support a similar voltage and that their connectors are standard, you could swap one capacitor for another, maybe cheaper or easier to find in your area.

This idea of composing complex objects out of simpler ones is a very powerful one, and using it in software yields incredibly good results. By decomposing your "high-level," most complex objects into other simpler ones, you can achieve many good things:

  • You can test each component separately, just like you can test each capacitor separately to make sure that they work in your electronics project.

  • You can swap one component by another easily, just by removing the old one and putting the new one in its place.

The capability of replacing a component with another sharing the same interface lead practicioners and computer scientists to invent the concept of "Dependency Injection." Using DI, an object can specify the concrete types to be used at runtime, instead of specifying them "by hand" or "hard coded" during development. The configuration of the object – that is, the concrete types to be used at runtime – can be specified in a separate object, or even on a configuration file outside of the project.

Follow along

The code of this section is located in the Storage/RxCalculator folder.

In this section we are going to learn a bit more about Dependency Injection using Dagger, a project now maintained by Google (originally created by developers from Square) which allows applications to be composed at runtime, as we explained previously.

To demonstrate how to use Dagger, we are going to build… precisely a calculator!

An Obsession with Calculators

If you have read my article on Medium called Being a Developer at 40 maybe you remember that I mentioned that, every time I learn a new programming language, I build a calculator with it. Well, this time is no exception! We are going to learn about Dependency Injection and Dagger by building one.

First we need to integrate Dagger in our project. To do that, as usual, we are going to edit the Gradle file of our project:

Adding Dagger as a dependency
implementation 'com.google.dagger:dagger:2.13'
annotationProcessor 'com.google.dagger:dagger-compiler:2.13'
kapt 'com.google.dagger:dagger-compiler:2.13'

Android Studio will now download and compile the symbols of Dagger into our application.

Let us now analyze the structure of our calculator project in Android Studio. The most important thing to know about it is that it uses Kotlin interfaces extensively; there is a Calculator interface, which itself requires instances of the Digit interface to be entered, and uses an Operation (yet another interface) to calculate the final result. All of these elements are grouped into their own packages, too.

If you take a look at the MainActivity class, you will see that it does absolutely nothing; its layout file, on the other hand, references CalculatorFragment – because we are following good practices here!

Layout file for MainActivity
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

</FrameLayout>

The interesting part comes next; our CalculatorFragment has two public fields marked with the @Inject attribute.

Inject attributes in CalculatorFragment
@Inject
lateinit var calculator: Calculator

@Inject
lateinit var storage: Storage

These annotations are required by Dagger to know where to "inject" the concrete implementations of the Calculator and Storage interfaces. But where do we specify those concrete implementations?

This information is located in the CalculatorModule class, a Dagger @Module which @Provides the required implementations. Of course, this could enjoy yet another level of abstraction, using a configuration file or some other dynamic feature, but for the moment let us just use simple new statements to return the concrete objects.

A @Module which @Provides concrete objects
@Module
class CalculatorModule {

    @Provides
    internal fun provideCalculator(): Calculator {
        return LongIntegerCalculator()
    }

    @Provides
    internal fun provideStorage(): Storage {
        return MemoryStorage()
    }
}

So now we have a CalculatorFragment class which requires some objects to be @Inject -ed, and we have a CalculatorModule which @Provides those objects. We need to add some glue between both! This glue is the CalculatorComponent interface:

A @Component acting as a glue
@Component(modules = arrayOf(CalculatorModule::class))
interface CalculatorComponent {
    fun inject(fragment: CalculatorFragment)
}

When Dagger reads this file at compile time, it will automatically create a hidden class called DaggerCalculatorComponent using the module specified as a parameter. This will happen transparently, without any user intervention, and finally the only thing that remains to be done is to use that generated code.

Assembling the puzzle
// Instead of hard-wiring dependencies:
// calculator = IntegerCalculator()
// storage = MemoryStorage()

// We will use dependency injection:
val app = activity?.application as CalculatorApplication
app.calculatorComponent.inject(this)
calculator.setStorage(storage)

As you can see above, we ask Dagger to build a component of type CalculatorComponent and then we ask that component to inject() the current fragment with objects generated by the CalculatorModule class.

As a bonus, I have left commented out the code that performs exactly the same task, but using new statements hardcoded in the fragment class.

This is arguably a very complex setup for such a simple construction, but imagine if you were not building just a calculator, but a vehicle, a machine or any other construction with lots of moving parts. Imagine now being able to specify the interfaces of all those component types, and then let Dagger assemble the whole structure for your dynamically at application startup.

9.5. RxAndroid

There is a classic article in the web about software architecture, referenced once and again, its diagrams copied and rearranged a thousand time, one that the author of this book felt compelled to include as a reference, called The Clean Architecture, by "Uncle Bob", Robert C. Martin.

The architectural diagram included in that article, reproduced below (with a link to its original) departs from the classic vertical design to a more organic structure, in which outer layers can have knowledge and access to the inner ones, but not the other way around.

More about Clean Architecture

If you are interested in the concept of Clean Architecture and would like to learn more, please check this video with Robert C. Martin himself explaining the concept.

The Clean Architecture
Figure 70. The Clean Architecture
Follow along

The code of this section is located in the Storage/RxCalculator folder.

In order to make our calculator project more "clean" we would like to avoid two things:

  1. Whenever the user presses a button in the calculator, we have to update the display reflecting the current state of the calculator object. We would like to avoid having to poll this object, and instead we would love to be notified of those state changes.

  2. The calculator should be completely unaware of the fact that it lives its life inside of an Android application fragment, and that its current state will be displayed on a TextView instance.

iOS developers reading this can imagine lots of different mechanisms to reach this kind of simultaneous isolation and collaboration: NSNotification, "Key-Value Observing", delegate protocols, etc. We could simply implement a delegate protocol in Kotlin using interfaces, for example; but in this case we are going to go a bit overboard, and we will use RxAndroid instead.

RxAndroid is a port to Android of RxJava, the Reactive Extensions for the JVM. They allow developers to create asynchronous, event-based applications with observables.

To include RxAndroid in our project, as usual, we need to modify the application’s Gradle file:

Adding RxAndroid as a dependency
implementation 'io.reactivex:rxandroid:1.2.1'
implementation 'io.reactivex:rxjava:1.1.6'

With this library available, we are going to make our LongIntegerCalculator fully conform to the Calculator interface and expose an observable to the outer world.

Publish an observable
private val subject = PublishSubject.create<Long>()

override val observable: Observable<Long>
    get() = subject

The mechanism enabled by the code above is eerily similar to that provided by KVO in Cocoa, or by the property observers provided by Swift, didSet & willSet.

Now the LongIntegerCalculator instance can call mSubject.onNext(mRegister) every time that its internal register value changes.

Notify observers of a change
subject.onNext(register)

But of course, what is an observable without an observer? Let us add the required code in the CalculatorFragment class to make sure that the display of the calculator follows the evolution of the internals of the calculator.

Subscribe to an observable
// Wire the observable
calculator.observable.subscribe { value ->
    text_view_display.text = value?.toString()
}

And now, every time that the user interacts with the calculator interface in the CalculatorFragment, the underlying Calculator implementation will change its behavior, and the TextView used as LED display will be notified of that change, as expected.

9.6. Summary

Good architecture goes a long way, and this is not specific of Kotlin. Most good practices applicable to all software apply to Android, and following them will spare you headaches and team conflicts in the future.

Dagger is a popular dependency injection library that you can use in your projects, to help your team compose objects at runtime with testable components.

Finally, RxAndroid is a recent addition to the ReactiveX family, bringing the power of Reactive Extensions for the JVM to the Android world. It will help you create asynchronous, event-based architectures with fewer dependencies among your components, helping you test and deploy better applications.

10. Testing

Quality is not a property of things, but rather of the process used to create things. In the realm of software, quality can be described as the process by which one makes the right software, while making the software right.

Few software development techniques have had more impact in the past quarter of a century than the spread of automated unit and functional testing. Fortunately for us, Android Studio incorporates very advanced testing features, including the capability of running some "pure Kotlin, no UI" tests in the IDE, running other tests in a device or an emulator, and finally to simulate complete user interactions using functional tests. In this chapter we are going to learn about these capabilities.

10.1. TL;DR

For those of you in a hurry, the table below summarizes the most important pieces of information in this chapter.

Table 17. Testing Android Apps
Android iOS

Unit testing framework

JUnit 4

XCTest

Mock objects framework

Mockito

OCMock – OCMockito

Most common issue

Kotlin optionals

(ObjC) Messages to nil & (Swift) optionals

Runtime warnings

StrictMode

Xcode Runtime Issues

10.2. Defensive Programming Techniques

Chapter 8 of the famous "Code Complete, Second Edition" book by Steve McConnell is titled "Defensive Programming", and starts like this:

In defensive programming, the main idea is that if a routine is passed bad data, it won’t be hurt, even if the bad data is another routine’s fault. More generally, it’s the recognition that programs will have problems and modifications, and that a smart programmer will develop code accordingly.

— Steve McConnell
Code Complete, Second Edition

Defensive programming techniques include: input checking, assertions, error handling techniques – including exceptions, debugging aids and barricades. In this section we are going to learn how to apply some of those techniques in Android projects.

Exceptions

The JDK has a well-defined exception handling mechanism, but it takes some time to learn to use it effectively, particularly because it is somewhat different to the error mechanisms available in Objective-C and in particular to those from Swift. Its misuse can cause trouble to both users and other team members.

Types of Exceptions

The JDK has two categories of exceptions: Checked and Unchecked. The difference between both is that code advertising checked exceptions in the method signature must be wrapped in try / catch / finally statements by developers; Swift actually uses a similar feature, and this makes the compiler whine to the developers that do not wrap such code around do / try statements.

On the other hand, unchecked exceptions represents errors which are not recoverable, like the dreaded java.lang.OutOfMemoryError, usually referred to as OOME in literature.

Exception Hierarchy

The diagram below shows the hierarchy of exception classes in the JDK. All errors and exceptions inherit from Throwable; errors are by definition unchecked, as instances of RuntimeException. Developers are expected to recover from checked exceptions safely, shown with a gray background in the diagram.

diagram exceptions
Figure 71. JDK Exceptions Hierarchy
Exceptions & Errors between Android and Cocoa

Please be aware of this curious fact: the JDK and Cocoa use similar names for two opposite concepts; NSException is similar to Error (it represents non-recoverable situations), while NSError is similar to Exception (it indicates a recoverable error).

Exception Handling Guidelines

To avoid having code sprinkled with a myriad of try / catch / finally statements, it is recommended to follow the following best practices :

  • Catch exceptions as close to the user as possible.

  • Code that is meant for reuse (libraries or shared code among multiple applications) should not try to do error handling. It can, however, translate technology-specific exceptions (usually checked) into unchecked, generic ones; as a canonical example, API code could wrap a FileNotFoundException (checked) into a RuntimeException that would ultimately be thrown.

  • Always report exceptions, and report only once. Do not leave empty catch blocks in your code, and do not rethrow after doing something with the exception (like logging). Particularly in Android, exceptional situations might be interesting for the end user, and should be reported.

  • Prefer toasts and snackbars for unimportant information, and only use dialogs for important notifications that require the attention of the user.

NullPointerException

One word of warning against one of the most common sources of crash in the history of software, one which Java is particularly well known for: the NullPointerException. This is a subclass of RuntimeException, that is, an unchecked exception thrown when a method is invoked on a null reference.

I call it my billion-dollar mistake. It was the invention of the null reference in 1965. At that time, I was designing the first comprehensive type system for references in an object oriented language (ALGOL W). My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn’t resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years.

— Sir Charles Antony Richard Hoare
Null References: The Billion Dollar Mistake

iOS developers, both coming from Objective-C and Swift, are particularly blessed in this respect; to begin with, Objetive-C has no problem sending messages to nil pointers anyway, which means that Cocoa applications written in this language will never crash because of this problem. Of course, this situation raises other problems, and to solve them Swift included the notion of optionals, which make explicit the possibility of a reference being nil, forcing the developer to "unwrap" it if needed.

Thankfully, Kotlin includes support for optionals, with a syntax very similar to that of Swift. The section "Kotlin" of the "Toolchain" chapter contains more information about Kotlin optionals.

Assertions

TODO: use Kotlin assertions

Using require, check and assert
override fun enterDigit(digit: Digit) {
    require(digit.value >= 0 && digit.value < 10) { (1)
        "The value must always be a single digit"
    }

    if (digitsCount == 0L) {
        register = digit.value.toLong()
    } else {
        register = register * 10 + digit.value
    }

    check(register >= digit.value) { (2)
        "The current state of the register must make sense"
    }

    digitsCount += 1

    subject.onNext(register)

    assert(digitsCount > 0) { (3)
        "At this stage, we should have more than one digit "
    }
}
1 require() throws IllegalArgumentException when its condition is false. Use it to test function arguments.
2 check() throws IllegalStateException when its condition is false. Use it to test object state.
3 assert() throws AssertionError when its condition is false (but only if JVM assertions are enabled with -ea). Use it to clarify outcomes and check your work.

Even with assertions off, the code documents the fact that a particular situation is not expected in its context.

Assertions and Design by Contract

Assertions allow developers to specify pre- and postconditions in their code, following a technique formalized and pushed forward by the Eiffel programming language, created by Bertrand Meyer, called "Design by Contract™."

There is often confusion in programmers between assertions vs exceptions; when should they use either one? The answer is to remember that assertions should protect your code from your own errors, while exceptions are used to protect your code from errors coming from external libraries. As a rule of thumb, you should only use assertions in private methods, while only public methods should throw exceptions.

Of course, extensive unit testing – which, in itself, is based on the assertion principle – is a key element to increase your confidence in your shipped code.

10.3. The Monkey

The Android Monkey is a program that runs on your emulator or device generating pseudo-random streams of user events such as taps or gestures, as well as some system-level events. The idea of the Monkey is to stress-test applications under development, hoping that you will get a crash before your users do.

The Monkey is a command-line tool that that you can run on any emulator instance or on a device. It sends a pseudo-random stream of user events into the system, which acts as a stress test on the application software you are developing. The monkey can be launched from the command line, on an emulator or a device, and watching it using your application can be really fun!

adb shell monkey -p training.akosma.rxcalculator --throttle 100 -s 43686 -v 50000 | tee monkey.log

The parameters of adb shell monkey command are the following:

  • -p specifies the package name to test.

  • --throttle specifies the delay in milliseconds between the events.

  • -s specifies a seed value for the random number generator. This value should be changed every so often, to generate different interactions on the application UI.

  • -v specifies the verbose option.

  • 50000 is the number of events to be simulated.

The Monkey requires an instance of the Emulator running, with the application specified in the -p parameter installed in it. The emulator can be run from the command line using the script below:

The output of the Monkey tool looks like this (edited for brevity):

…
:Sending Touch (ACTION_DOWN): 0:(310.0,195.0)
:Sending Touch (ACTION_UP): 0:(398.46033,164.18097)
:Sending Trackball (ACTION_MOVE): 0:(-1.0,2.0)
:Sending Touch (ACTION_DOWN): 0:(785.0,685.0)
:Sending Touch (ACTION_UP): 0:(800.0,763.3364)
:Sending Trackball (ACTION_MOVE): 0:(-1.0,-3.0)
:Sending Trackball (ACTION_MOVE): 0:(-1.0,-2.0)
    //[calendar_time:2013-01-29 14:08:36.853  system_uptime:106295]
    // Sending event #100
:Sending Touch (ACTION_DOWN): 0:(274.0,1077.0)
:Sending Touch (ACTION_UP): 0:(270.2971,1078.4357)
:Sending Touch (ACTION_DOWN): 0:(28.0,84.0)
:Sending Touch (ACTION_UP): 0:(11.232578,80.12936)
:Sending Touch (ACTION_DOWN): 0:(547.0,1189.0)
:Sending Touch (ACTION_UP): 0:(629.37836,1216.0)
:Sending Trackball (ACTION_MOVE): 0:(-3.0,-5.0)
:Sending Touch (ACTION_DOWN): 0:(209.0,113.0)
…

The log file will also include crashes or anomalies found during the execution of the application.

Monkey sometimes causes problems with the adb server. If needed, use the following commands to restart the adb server: adb kill-server; adb start-server. This happens because the Monkey generates events faster than the emulator or the device can handle them, so pay attention to the number you pass in the command line.

10.4. Local Unit Testing

Let us jump to the most interesting subject of unit testing. Historically, unit testing in Android represented quite a challenge, one that iOS developers sometimes struggled to understand. Because Android does not run on the standard JVM, it meant that unit tests involving classes of the Android SDK could only run on the emulator. This made testing applications a bit difficult given the slow speed of the first emulators available.

Although this situation persists to this day – Android code can only run on the Android Runtime – thankfully Android Studio allows code involving only Kotlin libraries to be unit tested directly on the IDE. These tests are called local unit tests and are extremely simple to write and run without running an emulator instance.

Follow along

The code of this chapter is located in the Testing/RxCalculator folder.

Local unit tests look exactly like you would expect from a modern unit testing suite. The biggest difference with XCTest, however, strives in the fact that test methods do not begin with the test name, but are rather decorated with the @Test attribute.

Here is an admittedly simple local unit test for our RxCalculator project:

Local unit test
class AdditionTest {

    @Test
    @Throws(Exception::class)
    fun execute() {
        val op = Addition<Float>()
        val op1 = 2F
        val op2 = 3F
        val result = op.execute(op1, op2)
        assertEquals(result.toFloat(), 5.0f, 0.001f)
    }

    @Test
    @Throws(Exception::class)
    fun getString() {
        val op = Addition<Float>()
        val op1 = 2F
        val op2 = 3F
        val value = op.getString(op1, op2)
        assertEquals(value, "2.0 + 3.0 = 5.0")
    }
}

If you would like to run these tests from the command line, just type the ./gradlew test command and Gradle will gladly run them for you. This can be particularly useful for configuring Continuous Integration (CI) systems.

10.5. Instrumented Unit Testing

In contrast with local unit tests, Android Studio allows you to create instrumented unit tests, which run on an emulator or a device, and have full access to the complete Android SDK.

Robolectric

For those remembering the times of the Eclipse Android Developer Tools, instrumented unit tests were something one could achieve using a tool like Robolectric.

The listing below shows an instrumented unit tests, decorated with the required @RunWith(AndroidJUnit4.class) attribute.

Instrumented unit test
@RunWith(AndroidJUnit4::class)
class MainActivityTest {

    @Rule @JvmField
    var activityTestRule = ActivityTestRule(MainActivity::class.java)

    @Test
    @Throws(Exception::class)
    fun onCreate() {
        val view = activityTestRule.activity.findViewById<FrameLayout>(R.id.activity_main)
        assertEquals("android.widget.FrameLayout", view.javaClass.name)

        val text = view.findViewById<TextView>(R.id.text_view_display)
        assertNotNull(text)
    }

}

10.6. User Interface Testing

Instrumented unit tests open the door to automated functional testing directly from within the Android Studio IDE using the Espresso library. The first step to use this feature consists in making sure that you have the required libraries in your application Gradle build file.

Including the testing libraries in build.gradle
androidTestCompile 'org.hamcrest:hamcrest-library:1.3'
androidTestCompile 'com.android.support.test.espresso:espresso-core:3.0.1'
testCompile 'junit:junit:4.12'

Of course, creating functional tests by hand is quite a complex task, and thankfully, once again, Android Studio comes to the rescue. Select the Run  Record Espresso Test runs the application and records the sequence of steps performed in an instrumented test.

The listing below shows the final result of simulating an interaction with the calculator, one that can be repeated ad aeternum et ad nauseam by your continuous integration server.

A recorded interaction with the calculator
val appCompatButton9 = onView(
        allOf(withId(R.id.button_8), withText("8"), isDisplayed()))
appCompatButton9.perform(click())

val appCompatButton10 = onView(
        allOf(withId(R.id.button_delete), withText("DEL"), isDisplayed()))
appCompatButton10.perform(click())

val textView = onView(
        allOf(withId(R.id.text_view_display), withText("7"),
                childAtPosition(
                        childAtPosition(
                                withId(R.id.activity_main),
                                0),
                        0),
                isDisplayed()))
textView.check(matches(withText("7")))

10.7. Code Coverage in Android Studio

The logical question after including a suite of unit tests in your project is, "how much of my code is covered by the tests?" Thankfully, once again Android Studio has you… covered.

You can easily configure the project to include the code coverage information, which is then compiled into a set of handy HTML files, ready for anyone in the team to inspect and refer to. To do that, select the Run  Edit Configurations… menu, and create a JUnit Run configuration to run all local tests, using the parameters shown in the following figure.

Android Studio test run configuration
Figure 72. Android Studio test run configuration

Once you have done this, you can select the "Run all local tests" configuration in the pop-up menu in the toolbar of Android Studio, and launch the code coverage operation clicking on the "Run with Coverage" button, highlighted in the figure below. This figure also shows the coverage pane on the right hand side of the screen, as well as the Run pane at the bottom with the results of the last unit test run.

Running unit tests with coverage
Figure 73. Running unit tests with coverage

You can export the code coverage from the dedicated pane on Android Studio, which creates HTML files and optionally opens the default browser to display those results.

HTML report with code coverage
Figure 74. HTML report with code coverage
Follow along

The coverage report of this chapter is located in the Testing/RxCalculator/coverage folder.

If you want to generate the code coverage report from the command line, just execute ./gradlew createDebugCoverageReport.

10.8. Miscellaneous Tips

This section presents a series of simple tips and tricks that can be useful to increase the quality of your Android code.

StrictMode

StrictMode is a code checker that generates warnings at runtime, similarly to how Xcode 8 and 9 show "Runtime Issues" about your code. For example, StrictMode is used to warn about disk or network access on the application’s main thread.

To enable StrictMode, extend your project adding a custom subclass of the android.app.Application class:

Enabling StrictMode
if (isDebug()) { (1)
    StrictMode.enableDefaults();

    val threadPolicy = StrictMode.ThreadPolicy.Builder()
            .detectDiskReads()
            .detectDiskWrites()
            .detectNetwork()
            .penaltyLog()
            .build()
    val vmPolicy = StrictMode.VmPolicy.Builder()
            .detectLeakedSqlLiteObjects()
            .detectLeakedClosableObjects() (2)
            .penaltyLog()
            .penaltyDeath()
            .build()

    StrictMode.setThreadPolicy(threadPolicy);
    StrictMode.setVmPolicy(vmPolicy);
}
1 We only set the StrictMode when executing our application in the emulator; this code should not be executed in production applications.
2 You can also call detectAll() to detect all problems at once.

Naming Threads

Give every thread, HandlerThread and ThreadPoolExecutor a meaningful name. It makes stack dumps much more meaningful and debugging sessions shorter, and easier to follow and document.

Immutable Objects

Kotlin favors the use of immutable objects in your code. Make sure to use the val keyword every time as needed, and only use var when needed. Also make sure to return non-mutable data structures from your getters and setters, if required by making a copy. Immutability is always your friend, particularly in multi-threaded code, loops or complex user interfaces.

If your getters return objects, return a copy, not the actual object held by the backing store, to make sure that it will not be mutated by the calling code.

Performance

The Android documentation features several useful application performance tips that are worth enumerating here:

  1. Avoid creating unnecessary objects.

  2. Prefer static over virtual.

  3. Use static final for constants.

  4. Avoid internal getters/setters.

  5. Use enhanced for loop syntax.

  6. Consider package instead of private access with private inner classes.

  7. Avoid using floating-point.

  8. Know and use the libraries.

  9. Use native methods carefully.

  10. Know and use the libraries.

  11. Use native methods judiciously.

Even if these recommendations might appear primarily directed to Java developers, remember that all Kotlin code is compiled to the same bytecode as Java applications, so they are worth keeping in mind.

More Quality Tips

Some other useful tips for testing and debugging your apps.[6]

  • If you need to unit test code using SQLite databases, create the database in memory; for that, remember that SQLiteOpenHelper() creates an in-memory database when the second parameter, name, is null.

  • Use Kotlin data classes as often as possible. They include default implementations of toString() and hashCode and can be used safely as POJOs.

  • Please take a look at the Core App Quality checklists in the Android documentation before you ship your applications. These checklists range from visuals to functionality, and they can help you deliver quality software in time and budget, whatever your project.

10.9. Summary

Quality is much more than just adding tests. Quality starts by the recognition of the multiple sources of errors that exist, beginning with our own human limitations.

It is fundamental, then, to code defensively, using assert to protect your program from your own errors, and to catch Exceptions to protect it from errors coming from the outside world.

Remember that Exceptions and Errors in the JDK have opposite meanings than in Cocoa; JDK Errors and unchecked Exceptions are not recoverable, while checked Exceptions are recoverable. Wrap code that throw exceptions in try / catch blocks, and when you catch them, fail gracefully. Either alert the user, if required; relaunch the operation, if possible; and in all cases, log the situation.

11. Multithreading

Android, being based in Linux, has access to all the multithreading capabilities of a modern operating system. Even better, many new devices running Android feature multicore CPUs, capable of stunning graphics and performance. This chapter will provide a deep look into the different multithreading models of Android, which are quite different to those of iOS.

11.1. TL;DR

For those of you in a hurry, the table below summarizes the most important pieces of information in this chapter.

Table 18. Multithreading in Android
Android iOS

Mechanisms

Threads, AsyncTasks, etc

Grand Central Dispatch

Units

java.lang.Runnable or android.os.AsyncTask

Blocks

Queues

android.os.Handler

GCD dispatch queues

Long-running

android.support.v4.app.JobIntentService

Background modes & fetch

11.2. Introduction

Android applications, just like in iOS and many other operating systems, have a "main thread" where the user interface is drawn and refreshed. This thread is created when the app starts, and has its own android.os.Looper instance, used to dispatch events to widgets and to redraw the screen following any changes. This is very similar to what happens in iOS, where the UIApplicationMain() function of Cocoa Touch creates a main thread with an NSRunLoop instance in it.

The similarities between iOS and Android, however, end right there. Android does not count with a unified API for multithreading like Grand Central Dispatch; instead, developers must use a variety of classes and mechanisms to perform work units on the background. On the other hand, Android applications are actually able to provide features currently not available on iOS, for example:

  • To spawn long-running processes into services or jobs;

  • To schedule jobs to be executed when the device boots up;

  • To restrict the execution of jobs when the battery is low.

As soon as applications become complex enough to require background processes, a certain number of problems must be addressed:

  • How to avoid blocking the UI thread? Which mechanisms are available to execute code in the background?

  • How to start a process on a separate thread of execution?

  • How can the main thread be notified of the current progress status of a long-running task?

  • How can users be notified of a success or failure at the end of the execution?

  • How can a long-running task be cancelled?

Android provides several mechanisms to execute long-running processes in the background; they have their relative strengths and weaknesses, and each is adapted for a particular scenario:

  • Runnable, Thread and Handler

  • ThreadPoolExecutor

  • AsyncTask

  • HandlerThread and Handler

  • Kotlin coroutines

  • Service

  • IntentService

  • JobService

  • JobIntentService

We are going to learn a bit about each of them in this chapter.

11.3. Long Running Operations and the UI Thread

There is a certain number of considerations to take into account when creating multithreaded Android applications; the first thing is to always remember the golden rule: Never block the main thread.

The Android operating system watches your code and will prevent applications from doing the following:

  • Accessing UI widgets from threads other than the main thread;

  • Performing long-running processes on the main thread, like network requests or database queries.

Both of these can have awful consequences, and might cause the termination of your application. To illustrate the situation, we are going to create a small app that tries to perform a long operation, taking several seconds to complete, on the main thread.

Follow along

The code of this section is located in the Multithreading/Blocking folder.

This application has a class called LongRunningTask with a simple execute() method, which simply loops without doing anything really interesting, taking around 10 seconds to complete, all while blocking the thread in which it runs. This code is shown below.

In a "real" application, this LongRunningTask would encapsulate some task that can take really long:

  • Sorting a large dataset in memory;

  • Processing a video;

  • Applying a filter to an image;

  • Uploading a large file to a remote server.

A loop taking 10 seconds to complete
fun execute() {
    listener.started(this)
    var count = -1
    while (count < 10 && !isCancelled) {
        count += 1
        val progress = count * 10
        try {
            TimeUnit.SECONDS.sleep(1)
        } catch (e: InterruptedException) { /* Do nothing */ }

        listener.reportProgress(this, progress)
    }
    if (isCancelled) {
        listener.cancelled(this)
    } else {
        listener.finished(this)
    }
}

This LongRunningTask class uses a listener interface to notify about the fact that it has started, about its current progress, and about whether it has finished (or whether it has been cancelled.) This listener protocol looks like this:

Listener interface for the LongRunningTask class
interface LongRunningTaskListener {
    fun started(task: LongRunningTask)
    fun reportProgress(task: LongRunningTask, progress: Int)
    fun finished(task: LongRunningTask)
    fun cancelled(task: LongRunningTask)
}

Run the application on a device or an emulator and hit the "Execute" button. Try now to move the SeekBar instance, or checking the CheckBox instance, and you will see that you cannot. Because the execute() method is running on the main thread, it blocks its Looper from consuming and dispatching events to the widgets in the user interface, and as a result, the whole user interface is blocked.

A quick look at the logcat pane will show an entry like the following:

Skipped 1333 frames! The application may be doing too much work on its main thread.

And if we try to use the widgets in the UI, the operating system will realize that the application is not responding to events, and asks the user whether it should close it, as shown in the figure below. The progress bar jumps to 0 to 100% at the end of the process, and even worse, we cannot cancel the task in any way.

Title
Figure 75. Non-responding app warning

This is clearly not acceptable. We are going to have to adapt our code a little, to be able to execute our long running task in the background, and free the UI thread from this annoyance. We also would like to see its completion percentage, and to be able to cancel it if required by the user.

11.4. Runnables and Threads

The first approach to solve the problem involves using the java.lang.Runnable class and the java.lang.Thread classes. They are part of the JDK and represent the "Java way" when it comes to multithreading. Using these classes we can send the heavy load to a background thread, and our user interface will be usable during the process.

Follow along

The code of this section is located in the Multithreading/Runnable folder.

In this application we make our LongRunningTask a subclass of Runnable and instead of an execute() method, we are going to override the run() method.

Overriding the Runnable.run() method
override fun run() {
    started()
    var count = -1
    while (count < 10 && !isCancelled) {
        count += 1
        val progress = count * 10
        try {
            TimeUnit.SECONDS.sleep(1)
        } catch (e: InterruptedException) { /* Do nothing */ }

        reportProgress(progress)
    }
    if (isCancelled) {
        cancelled()
    } else {
        finished()
    }
}

If you run this sample application, and hit the "Execute" button, you will still be able to use the seek bar and the checkbox, while the threads execute on a separate context. This keeps users happy and in control, which is exactly what we want.

Starting a Thread

To execute this runnable on a separate thread, just create a new LongRunningTask and pass it as a parameter to the contructor of the Thread class:

Creating a Thread with a Runnable
task1 = LongRunningTask(this)
val thread1 = Thread(task1)
thread1.start()

Reporting Progress

The problem is now the communication between the new thread and the main thread, where the UI is drawn. How can we inform of the current progress of the execution of a thread?

To send messages between threads, we need to use the android.os.Handler class. This class has a very convenient handleMessage() method to override, which is executed in a separate thread; in this case, we specify through the constructor that we want this handler to be located in the main thread; that is, it will process messages in the Looper.getMainLooper() of the main thread.

Handler handling messages
private val handler = object : Handler(Looper.getMainLooper()) {
    override fun handleMessage(msg: Message) {
        when (msg.what) {
            WHAT_STARTED -> listener.started(this@LongRunningTask)
            WHAT_PROGRESS -> {
                val progress = msg.data.getInt(PROGRESS)
                listener.reportProgress(this@LongRunningTask, progress)
            }
            WHAT_FINISHED -> listener.finished(this@LongRunningTask)
            WHAT_CANCELLED -> listener.cancelled(this@LongRunningTask)
        }
    }
}

To report progress, our LongRunningTask has a method, executed in the background thread, which sends a android.os.Message to our Handler:

Sending a Message to a Handler
private fun reportProgress(progress: Int) {
    val bundle = Bundle()
    bundle.putInt(PROGRESS, progress)
    val message = Message()
    message.data = bundle
    message.what = WHAT_PROGRESS
    handler.sendMessage(message)
}

Message instances have a simple what property, of type int which can be used as a simple, private communication protocol between threads. If more information is needed, you can always attach it to the message as raw data of any type; in this case, the progress value.

When running this application, both threads run concurrently and each updates its own progress bar.

Title
Figure 76. Sample application

Cancelling Execution

How can we cancel the execution of this task? Unfortunately the Runnable class does not provide a mechanism to do this. In our case we check the value of the isCancelled() getter at every iteration of our while loop. This getter reads the value of a property marked with the @Volatile keyword:

Using a volatile boolean to cancel the task
@Volatile
var isCancelled = false
    private set

In Kotlin, variables marked as @Volatile can be modified by several threads at once.

Drawbacks

Although simple at first sight, this approach requires the developer to watch out for different situations:

  • Each Thread has a certain overhead in terms of memory and resources; what would happen if we spawn hundreds of them at once, particularly in less powerful devices?

  • What would happen if there were dependencies among tasks, that is, one needed the output of another to start?

  • What would happen if threads had to share information?

All of these issues make the use of threads very complex. The last item above, however, can be the tricker one to tackle, and it is called a "Race Condition", explained in detail in the next section.

Race Conditions

A "Race Condition" is common in multithreaded code, and is the name given to the situation caused by two threads reading or writing the same location in memory. When this happens, the execution of the program becomes non-deterministic, as it is impossible to assert for certain the outcome of the operations.

We are going to create a small application that shows graphically how a race condition can manifest itself.

Follow along

The code of this section is located in the Multithreading/RaceCondition folder.

First we need a Runnable to run on a background Thread executing some Task that takes a bit longer than expected.

Runnable mutating variables at specific points in time
val runnable = Runnable {
    val task = Task(start, end, update)

    while (counter < RUN_COUNT) {
        alive = true  (1)
        task.execute()
        alive = false (2)

        Sleeper.sleepSeconds(Constants.SECONDS_PAUSE)
        counter += 1
    }
}

// Launch the execution
button.isEnabled = false
progressBar.progress = 0
counter = 0

val thread = Thread(runnable)
thread.start()
1 Here we modify a volatile boolean to true and then we execute some long-running code.
2 Right after the long-running code is finished, we set the volatile boolean variable to another value.

The Task class uses a subclass of Handler to talk back to the main thread.

The Task class and its Handler
internal class Task(start: Runnable,
                    end: Runnable,
                    update: Runnable) {
    private val handler = TaskHandler(start, end, update)

    fun execute() {
        handler.sendStartMessage()
        Sleeper.sleepSeconds(Constants.SECONDS_PAUSE)
        handler.sendEndMessage()
        Sleeper.sleepNanoseconds(Constants.NANOSECONDS_PAUSE)
        handler.sendUpdateMessage()
    }
}

When the Task class sends the END message, its Handler executes a callback on the main thread which looks like this:

Task callback code
val text = if (alive) "Cat is alive" else "Cat is dead"
textView.text = text
// The value of "alive" can change in the meantime,
// and we end up with "Cat is alive" in red!
val color = if (alive) Color.GREEN else Color.RED
textView.setTextColor(color)

In this code we read the value of the boolean volatile field (called "alive") twice, and we set different properties of our TextView. Executing this code, we can easily reach a weird situation, in which the text reads "Cat is alive"… but the color of the text is red!

Title
Figure 77. Race conditions with Multiple Threads

There are three possible outcomes every time, "Cat is dead" in red, "Cat is alive" in red, and "Cat is alive" in green. The fact that "Cat is alive" appears in red indicates a race condition, and henceforth the outcome of the program cannot be determined beforehand.

Together with the source code of this example you will find a movie called "demo.mp4" showing a sample run of this application.

This is what is commonly called a Heisenbug; it cannot be clearly explained, reproduced or debugged, and as such, it is undesirable and must be avoided at all costs. To avoid this problem, it is tantamount to avoid shared data at all costs, and make background processes work with copies of data, and return separate results.

Recording movies using ADB

You can easily make videos out of your interactions on the device using the following ADB commands:

$ adb shell screenrecord /sdcard/movie.mp4

You can cancel the recording using Ctrl+C. Then copy the movie to your computer using:

$ adb pull /sdcard/movie.mp4
$ adb shell rm /sdcard/movie.mp4

If ADB has several devices or emulators connected, use the -s xxxxxxx argument in any of these commands. You can retrieve the list of connected devices using $ adb devices -l at any time.

11.5. ThreadPoolExecutor

Given the overhead required by individual threads, it is wiser to use the java.util.concurrent.ThreadPoolExecutor class. A ThreadPoolExecutor manages a pool of available threads, configurable depending on the amount of cores of the CPU of the current device, and queues Runnables to be executed in those threads as they become available. This provides a saner situation for devices of all kinds, with a very simple API to use.

Follow along

The code of this section is located in the Multithreading/ThreadPoolExecutor folder.

Using a ThreadPoolExecutor requires a little bit of infrastructure; but the Runnable objects are exactly the same required when using standalone Thread instances. We also need an instance of java.util.concurrent.ThreadFactory, which will be used by the executor to create new threads whenever needed.

Setup required for a ThreadPoolExecutor
val factory = ThreadFactory { runnable -> Thread(runnable) }

val coresCount = Runtime.getRuntime().availableProcessors()
executor = ThreadPoolExecutor(coresCount * 2,
        coresCount * 2,
        60L,
        TimeUnit.SECONDS,
        LinkedBlockingQueue(),
        factory)
Changing the thread pool size to just one thread makes task execute in sequence instead of parallel.

Sending a Runnable to a ThreadPoolExecutor is very simple:

Executing a Runnable with a ThreadPoolExecutor
task1 = LongRunningTask(this)
executor?.execute(task1)
The ThreadPoolExecutor class contains a submit() method returning a java.util.concurrent.Future instance, which can be used to retrieve the result of a long calculation.

11.6. AsyncTask

The android.os.AsyncTask is probably one of the most famous classes in the Android toolkit. It has been around since early versions of Android, and provides a convenient object-oriented wrapper around the Thread, Runnable, and Handler classes described in previous sections.

We are going to implement the same system as we did previously, but this time using AsyncTask as a building block.

Follow along

The code of this section is located in the Multithreading/AsyncTask folder.

This time we implement the LongRunningTask as a subclass of AsyncTask. This class uses generics and takes three types as a parameter:

  1. The input type of the task, when it is launched.

  2. The type of the progress information, sent at regular intervals.

  3. The type of the output of the task, when it has finished executing.

Our task will use our usual listener interface (or if you prefer, a delegate protocol) to talk to the main thread.

Definition of the LongRunningTask class
class LongRunningTask(private val listener: LongRunningTaskListener)
    : AsyncTask<Void, Int, Boolean>() {

The next step is overriding key methods provided by the AsyncTask class.

doInBackground()

This is by far the most important method to override, as it is the one that performs the heavy duty in a background thread. This method is always executed in a separate thread from the caller code.

Overridden doInBackground() method
override fun doInBackground(vararg voids: Void): Boolean? {
    var count = -1
    while (count < 10 && !isCancelled) { (1)
        count += 1
        val progress = count * 10
        try {
            TimeUnit.SECONDS.sleep(1)
        } catch (e: InterruptedException) { /* Do nothing */ }

        publishProgress(progress)        (2)
    }
    return true
}
1 The loop calls isCancelled at every iteration to know whether it should continue or if it should stop immediately. This method is provided by AsyncTask and it becomes true if the caller code calls the cancel() method on the task.
2 Every so often, this method can call publishProgress() with an integer value. This will be used by the onProgressUpdate() method, to provide information about the progress of the current task.

onPreExecute()

This method is executed right before doInBackground() and is always called on the main thread.

Overridden onPreExecute() method
override fun onPreExecute() {
    listener.started(this)
}

onProgressUpdate()

This method is called on the main thread every time the background task calls publishProgress().

Overridden onProgressUpdate() method
override fun onProgressUpdate(vararg values: Int?) {
    listener.reportProgress(this, values.get(0) ?: 0)
}

onCancelled()

This method is called asynchronously on the main thread when this task has been cancelled by the caller.

Overridden onCancelled() method
override fun onCancelled(bool: Boolean?) {
    listener.cancelled(this)
}

onPostExecute()

This method is called after the background task has finished executing and has not been cancelled in the meantime.

Overridden onPostExecute() method
override fun onPostExecute(bool: Boolean?) {
    listener.finished(this)
}

Launching an AsyncTask

To start the task, just create a new instance and call the .execute() method on it.

Launching an AsyncTask
task1 = LongRunningTask(this)
task1?.execute()

Cancelling an AsyncTask

To cancel an AsyncTask, keep a reference to it and call its .cancel() method. This method takes a boolean parameter; if the value is true, then the thread of this task will be killed without waiting for the background process to finish. In our case, we have code that dutifully checks whether the current task is still alive, hence we can pass false and be confident that our code will stop executing promptly.

Cancelling an AsyncTask
task1?.cancel(false)

Parallel Execution

Since Android Honeycomb (3.0, API 11), the default implementation of AsyncTask uses a serial executor running on a single thread; this means that tasks will be executed sequentially, one after the other. If you prefer to execute AsyncTasks in parallel, just create a ThreadPoolExecutor as explained previously and use the executeOnExecutor method, passing the executor as parameter.

Executing AsyncTasks in parallel with a ThreadPoolExecutor
task1 = LongRunningTask(this)
task1?.executeOnExecutor(executor)

You can use the checkbox on the top right of the screen in the sample application to test the execution of tasks in parallel or in sequence.

11.7. HandlerThread

The threads created in previous sections do not have Looper instances associated with them. This makes them unusable for some tasks, for example posting Toast messages, which must be sent from within threads with Loopers. To simplify the creation of threads with loopers, developers can use the android.os.HandlerThread method, which has some similarities to a DispatchQueue in Swift.

Follow along

The code of this section is located in the Multithreading/HandlerThread folder.

To use a HandlerThread, create an instance of the class passing a name as parameter. Then create a Handler instance using the Looper of that handler as parameter.

Creating a HandlerThread and its associated Handler
handlerThread = HandlerThread("MyHandlerThread")
handlerThread?.start()
handlerObject = Handler(handlerThread?.looper)

You can now pass any Runnable to this Handler and they will be executed sequentially in the Looper of the HandlerThread. You can have several HandlerThread instances in your application, just like you would have separate DispatchQueue instances in your Swift code.

Posting a Runnable to a Handler
task1 = LongRunningTask(this)
handlerObject?.post(task1)

Executing Runnables with Delay

Just like with the DispatchQueue.asyncAfter method, you can execute runnables after a delay, using the postDelayed() method and passing a number of milliseconds as parameter.

Delaying the execution of a runnable
task1 = LongRunningTask(this)
handlerObject?.postDelayed(task1, 20000)

You can test the delayed execution of runnables in the sample application by checking the checkbox on the upper right side of the screen; this will change the order of execution of the tasks.

Mimicking Cocoa’s DispatchQueue

Kotlin and Swift are similar enough to allow a little experiment; what if we tried to mimic our beloved DispatchQueue in Kotlin and Android? It turns out, it can be done very easily using HandlerThread and Kotlin lambdas.

Here is the definition of the DispatchQueue class in Kotlin; it uses the companion object feature to provide the main and global() queues, ready to use. As expected, the main queue dispatches on the Looper.getMainLooper() of the main thread.

DispatchQueue in Kotlin
class DispatchQueue(private val label: String = "main") {
    val handlerThread: HandlerThread?
    val handler: Handler

    init {
        if (label == "main") {
            handlerThread = null
            handler = Handler(Looper.getMainLooper())
        } else {
            handlerThread = HandlerThread(label)
            handlerThread.start()
            handler = Handler(handlerThread.looper)
        }
    }

    fun async(runnable: Runnable) = handler.post(runnable)

    fun async(block: () -> (Unit)) = handler.post(block)

    fun asyncAfter(milliseconds: Long, function: () -> (Unit)) {
        handler.postDelayed(function, milliseconds)
    }

    fun asyncAfter(milliseconds: Long, runnable: Runnable) {
        handler.postDelayed(runnable, milliseconds)
    }

    override fun toString() = label

    companion object {
        val main = DispatchQueue()

        private val global = DispatchQueue("global")
        fun global() = global
    }
}

This implementation is not complete, but for small applications built with nostalgia, it might do the trick. The API can dispatch not only Kotlin lambdas, but any kind of standard Runnable as well. It features also an asyncAfter() method, taking a delay in milliseconds and a lambda or runnable to execute after a delay.

Using this class is even more surprising; the code cannot be distinguished from its Swift equivalent!

Using DispatchQueue in Kotlin
val queue1 = DispatchQueue("Worker1")
queue1.async {
    for (i in 1..10) {
        DispatchQueue.main.async {
            textView1.text = "Step $i"
            progressBar1.progress = i * 10
        }
        TimeUnit.SECONDS.sleep(1)
    }

    DispatchQueue.main.async {
        executeButton.isEnabled = true
    }
}

You can test this implementation on the sample application, clicking on the "Dispatch Queue" checkbox on the top right corner of the UI. Two worker queues (actually, two worker HandlerThread instances) will be processing Kotlin lambdas asynchronously.

11.8. Kotlin Coroutines

Kotlin provides yet another multithreading mechanism, called Coroutines, in the form of a library called kotlinx.coroutines library. Coroutines are simply lightweight threads, automatically handled by the library, which can be used to send work to the background using simple keywords and functions. They are an experimental feature of Kotlin, still under heavy development, but they can be used already in your applications.

The first requirement to use coroutines in your application is to include the required items in the module Gradle file, as usual:

Gradle configuration for coroutines
kotlin {
    experimental {
        coroutines "enable"
    }
}

dependencies {
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:0.21.1"

Using this library, we can modify our LongRunningTask class to use the suspend keyword. This indicates that this method is meant to be run on a coroutine.

Suspend function
suspend fun run() {
    launch(UI) { (1)
        listener.started(this@LongRunningTask)
    }
    var count = -1
    while (count < 10 && !isCancelled) {
        count += 1
        val progress = count * 10
        delay(1000) (2)

        launch(UI) {
            listener.reportProgress(this@LongRunningTask, progress)
        }
    }
    if (isCancelled) {
        launch(UI) {
            listener.cancelled(this@LongRunningTask)
        }
    } else {
        launch(UI) {
            listener.finished(this@LongRunningTask)
        }
    }
}
1 The launch(UI) function call allows the code in the coroutine to call methods on the main thread of the application. Just with any other multithreading mechanism, calling methods on the main thread from a coroutine will cause your application to crash.
2 The delay function does to coroutines what Thread.sleep() does to threads; it blocks its execution for a specified amount of time.

To call a coroutine, just use the async keyword, passing a block of code as a parameter; this will automatically create a coroutine from the main thread, and will dispatch your code to be executed in it.

Running coroutines
executeButton.setOnClickListener {
    task1 = LongRunningTask(this)
    task2 = LongRunningTask(this)
    async {
        task1?.run()
    }
    async {
        task2?.run()
    }
}

One of the interesting aspects of coroutines is that they are extremely lightweight, much more than typical threads; this means that you can safely create thousands of them in one shot, and they will have a much smaller impact than a similar number of AsyncTask or threads. Other interesting aspects include:

  • withTimeout() wraps a set of coroutines and automatically cancels their execution if they take longer than expected to finish.

  • Lazily started async allows for code to be executed only when actually needed, and can be done using the async(start = CoroutineStart.LAZY) { task.run() } call.

  • Using the async(CoroutineName("task1")) { task1.run() } call gives a name to the coroutine, which can be useful in debugging sessions.

Coroutines are a very long subject, and they are still marked as "experimental" at the time of this writing; if you are interested in knowing more about them, check out these articles:

11.9. Service

In the examples so far, we have always used the current activity as the support for our multithreading architecture. The problem with this approach is that activities can be killed by the system at any time, depending on the available memory, on the device orientation, and other usage conditions, and as such they are a very poor support for long running processes.

To solve this problem, the Android operating system provides a series of "services", which allow to offload code from activities into longer-running processes, detached and with a lifetime of their own. There are many available processes, each with their own characteristics:

  • android.app.Service

  • android.app.IntentService

  • android.app.job.JobService

  • android.support.v4.app.JobIntentService

Each of these are built on top of the previous one, so we are going to tackle them in order, starting with the plain Service.

Follow along

The code of this section is located in the Multithreading/Service folder.

Services run in the main thread, just like the activities calling them. This means that for a LongRunningTask like ours, we need to use one of the multithreading mechanisms learnt earlier. In this case we are going to use a ThreadPoolExecutor executing Runnable instances.

Service subclass
class MyService : Service(), LongRunningTaskListener {

Services must be declared in the AndroidManifest.xml file as well:

AndroidManifest.xml file declaring the service
<service
    android:name=".MyService"
    android:enabled="true"
    android:exported="true" />

Our Service must implement a couple of methods. First, the one executed when the service is bound to another context of execution.

OnBind() method of the Service
override fun onBind(intent: Intent): IBinder? {
    return binder
}

We also need the method that is executed when the service is started.

OnStartCommand() method of the Service
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
    if (intent != null) {
        val action = intent.action
        val taskID = intent.getIntExtra(TASK_ID, 0)
        if (ACTION_START == action) {
            val task = LongRunningTask(this)
            task.taskID = taskID
            tasks.add(task)
            executor.execute(task)
        }
    }

    return Service.START_NOT_STICKY (1)
}
1 The START_NOT_STICKY parameter indicates that the service should not be restarted automatically by the operating system after it finishes. If this behaviour is required, return START_STICKY instead.

To use this service, an Activity should bind itself to it, and this is done through the android.content.ServiceConnection interface.

Binding to a service
override fun onBindingDied(name: ComponentName) {
    service?.setListener(null)
    service = null
}

override fun onServiceConnected(componentName: ComponentName, iBinder: IBinder) {
    val binder = iBinder as MyService.MyBinder
    service = binder.service (1)
    service?.setListener(this)     (2)
}

override fun onServiceDisconnected(componentName: ComponentName) {
    service?.setListener(null)
    service = null
}
1 This gives a direct connection from the activity to the service…
2 …and this gives an opposite connection, from the service to the activity.

Finally, the most important part: launch the service using an android.content.Intent.

Starting a service
taskID1 = 0
val intent1 = Intent(this@MainActivity, MyService::class.java)
intent1.action = MyService.ACTION_START
intent1.putExtra(MyService.TASK_ID, taskID1)
startService(intent1)

One of the major advantages of using services is that activities can offload tasks, and no matter what happens to those activities, they can reattach to services later on. For that, a few lifecycle methods can help.

Activity lifecycle methods to the rescue
override fun onResume() {
    super.onResume()
    val intent = Intent(this, MyService::class.java)
    bindService(intent, this, Context.BIND_AUTO_CREATE)
}

override fun onPause() {
    super.onPause()
    unbindService(this)
}

override fun onSaveInstanceState(bundle: Bundle?) {
    super.onSaveInstanceState(bundle)
    if (bundle != null) {
        bundle.putInt(TASK_ID_1, taskID1)
        bundle.putInt(TASK_ID_2, taskID2)
        bundle.putString(TEXT_VIEW_1, textView1.text.toString())
        bundle.putString(TEXT_VIEW_2, textView2.text.toString())
        bundle.putBoolean(EXEC_BTN_ENABLED, executeButton.isEnabled)
        bundle.putBoolean(CANCEL_BTN_ENABLED, cancelButton.isEnabled)
    }
}

As an architectural approach, services provide a simple mechanism to make activities more lightweight and to reuse long-running operations across several activities.

11.10. IntentService

In our previous example we had to create a ThreadPoolExecutor to avoid blocking the main thread. As a convenience for developers, android.app.IntentService is a subclass of android.app.Service which does just that; it executes in its own handler thread.

Follow along

The code of this section is located in the Multithreading/IntentService folder.

In this case, there is a higher level of disconnection between the IntentService and its calling activity, and for that reason the connection between both will use the android.support.v4.content.LocalBroadcastManager class. This class works very similarly to the NotificationCenter in Cocoa Touch, and allows one class to broadcast messages to other components in the application.

IntentService subclasses must override the onHandleIntent() method.

onHandleIntent() method
override fun onHandleIntent(intent: Intent?) {
    if (intent != null) {
        val action = intent.action
        if (ACTION_START == action) {
            MyIntentService.cancelled = false

            val task1 = LongRunningTask(this)
            task1.taskID = 0
            task1.execute()
            val task2 = LongRunningTask(this)
            task2.taskID = 1
            task2.execute()
        }
    }
}

Just like with Service subclasses, we must announce the existence of this IntentService to the operating system through the AndroidManifest.xml file.

AndroidManifest.xml file announcing the IntentService
<service
    android:name=".MyIntentService"
    android:exported="false" />

Just like with Service, IntentService instances are activated using Intent objects and calling the startService() method.

Starting an IntentService
val intent = Intent(this, MyIntentService::class.java)
intent.action = MyIntentService.ACTION_START
startService(intent)

Progress Notifications

To receive information about the execution progress of our IntentService, we are going to setup some listeners for specific broadcasts.

Creating a broadcast listener
private val progressReceiver = object : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        val progress = intent.getIntExtra(MyIntentService.PROGRESS, 0)
        val taskID = intent.getIntExtra(MyIntentService.TASK_ID, 0)
        reportProgress(taskID, progress)
    }
}
Registering a broadcast listener with a filter
val manager = LocalBroadcastManager.getInstance(this)
val filter = IntentFilter(PROGRESS_BROADCAST)
manager.registerReceiver(progressReceiver, filter)

Cancellation

The IntentService class was not created with cancellation in mind. The best approach would be then to add a new action, and to separate the execution of the tasks in a separate context, like a ThreadPoolExecutor for example.

However, for the sake of this example, we are going to use a quick and dirty mechanism to cancel the execution of this IntentService: a volatile boolean variable.

First we need to create a public static volatile boolean variable, to be accessed from both the activity and the LongRunningTask class.

Variable used to cancel the IntentService
@Volatile
var cancelled = false
We cannot use methods here, because during the execution, our code blocks the IntentService thread; we need to provide direct access to this boolean from all contexts.

Now we need to access this variable from the LongRunningTask to check whether the task should continue or not.

Check performed by the LongRunningTask
private val isCancelled: Boolean
    get() = MyIntentService.cancelled

Finally, our activity can set this flag to true at any time, and this will cause the execution to stop.

Cancelling the execution of the IntentService
MyIntentService.cancelled = true
This is not the best way to cancel the execution of an IntentService, and is only mentioned here for educational purposes. The following sections in this chapter show more advanced ways to deal with this problem.

11.11. JobService

android.app.job.JobService is a relatively new (API 21, Lollipop, 5.0) subclass of Service used to schedule long-running jobs in the background. It has the advantage of being more aware of the current operating conditions of the device. This allows the operating system to execute jobs more efficiently and preserving battery life.

For example, JobService instances can be scheduled to be run with certain frequence, when the device is plugged in and the battery is charging, or even when the device has just booted up.

Just like regular Service instances, JobService objects run in the main thread of the application, and thus require a separate threading system.

Follow along

The code of this section is located in the Multithreading/JobService folder.

Just like with Service and IntentService, JobService subclasses must be declared in the AndroidManifest.xml file for the operating system to find.

AndroidManifest.xml file declaring a JobService class
<service
    android:name=".MyJobService"
    android:enabled="true"
    android:permission="android.permission.BIND_JOB_SERVICE"
    android:exported="true" />

JobService subclasses should override the onStartJob() method, called when the job is started by the job scheduler.

OnStartJob() method of the JobService
override fun onStartJob(jobParameters: JobParameters): Boolean {
    val task = LongRunningTask(this)
    task.taskID = jobParameters.jobId
    task.parameters = jobParameters
    tasks.add(task)
    executor.execute(task)

    // Return true when it is a long-running service
    return true
}

Similarly, the onStopJob() method is called when the job scheduler requests a job to stop.

OnStartJob() method of the JobService
override fun onStopJob(jobParameters: JobParameters): Boolean {
    for (task in tasks) {
        if (jobParameters.jobId == task.taskID) {
            task.cancel()
        }
    }

    // Return false to drop this job, and avoid rescheduling
    return false
}

Launching Jobs

To launch a job, get a reference to the job scheduler service, and post instances of JobInfo, describing the job and the conditions of its execution.

Launching jobs
taskID1 = 0
taskID2 = 1
val component = ComponentName(this,
        MyJobService::class.java)

val builder1 = JobInfo.Builder(taskID1, component)
builder1.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY).build()
val info1 = builder1.build()

val builder2 = JobInfo.Builder(taskID2, component)
builder2.setRequiresDeviceIdle(false)

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    builder2.setRequiresBatteryNotLow(true)
}

val info2 = builder2.build()

val sched = getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler
sched.schedule(info1)
sched.schedule(info2)

Cancelling Jobs

To cancel jobs, just pass the job ID to the cancel() method of the job scheduler.

Cancelling jobs
val sched = getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler

sched.cancel(taskID1)
sched.cancel(taskID2)

Using EventBus

In this code sample, instead of using the default LocalBroadcastManager we used previously, we are going to use a popular open-source library called EventBus. This library requires the addition of a line in the application’s build.gradle file:

Library added in the build.gradle file of the module
implementation 'org.greenrobot:eventbus:3.1.1'
Another common alternative to EventBus used to be Otto, but it has been deprecated since 2015.

Then we need to define the events that our JobService will send, as static inner classes:

Event posted by the JobService
class ProgressEvent(val taskID: Int, val progress: Int)

The next step consists in setting up the listeners for those events, in the main activity.

Subscribing to events on the main activity
@Subscribe
fun onStartEvent(event: MyJobService.StartEvent) {
    started(event.taskID)
}

Activities should unregister themselves from events as soon as possible, for example when they are stopped.

Reacting properly to lifecycle events
public override fun onStart() {
    super.onStart()
    EventBus.getDefault().register(this)
}

public override fun onStop() {
    super.onStop()
    EventBus.getDefault().unregister(this)
}

And finally, we need to send events from the JobService itself, for example when a task has been cancelled.

Posting events from the JobService
jobFinished(task.parameters, false) (1)
val event = CancelEvent(task.taskID)
EventBus.getDefault().post(event)
1 This shows a very important aspect of JobService classes: they must call jobFinished() when they are finished or cancelled.

11.12. JobIntentService

The wide variety of background services (Service, IntentService, JobService) raises the obvious question, which one to use? In which context?

The continuous evolution of Android makes this answer trickier than expected. In 2017 Google announced new limitations for the execution of background services such as IntentService starting in Oreo (Android 8.0, API 26) and applications using them had to be modified to avoid weird or unexpected behaviours.

To simplify the use of background jobs, both in future and past versions of Android, Google added a new member to the Android Support Library 26.0.0: android.support.v4.app.JobIntentService:

  • When running in devices with versions of Android older than 8.0, JobIntentService behaves just like IntentService, starting work using Context.startService().

  • When running in Oreo or later, it uses the Job Scheduler service, using the JobScheduler.enqueue() method.

Follow along

The code of this section is located in the Multithreading/JobIntentService folder.

To use the JobIntentService class, we need to add the required Support Library entry in our build.gradle file.

Using the Support Library in our module
implementation 'com.android.support:support-compat:27.0.2'

Just like any other Service, JobIntentService subclasses must be declared in the AndroidManifest.xml file.

AndroidManifest.xml file declaring a JobIntentService
<service
    android:name=".MyJobIntentService"
    android:exported="true"
    android:permission="android.permission.BIND_JOB_SERVICE" />

Finally, subclasses of JobIntentService must override the onHandleWork() method to perform their work.

onHandleWork() method of the JobIntentService subclass
override fun onHandleWork(intent: Intent) {
    val action = intent.action
    val taskID = intent.getIntExtra(TASK_ID, 0)
    if (ACTION_START == action) {
        val task = LongRunningTask(this)
        task.taskID = taskID
        tasks.add(task)
        executor.execute(task)
    }
}

Finally, to launch the execution, use the JobIntentService.enqueueWork() method, which will trigger either a call to Context.startService() in versions of Android older than Oreo, or JobScheduler.enqueue() in Oreo and later.

Launching the execution of a JobIntentService
taskID1 = 0
val intent1 = Intent(this,
        MyJobIntentService::class.java)
intent1.action = MyJobIntentService.ACTION_START
intent1.putExtra(MyJobIntentService.TASK_ID, taskID1)
startService(intent1)

JobIntentService.enqueueWork(this,
        MyJobIntentService::class.java,
        JOB_ID + 1,
        intent1)
the JobIntentService class is, at the time of this writing, the recommended option for creating long-running jobs in Android applications.

11.13. Summary

Each multithreading option has relative strengths and weaknesses:

  • If your application requires sporadic access to a REST API here and there, then maybe a couple of AsyncTasks might do the trick. If, on the other hand, you require a permanent socket connected to a remote server, like in the case of a chat application, then a JobIntentService might be what you need.

  • If you need to queue lots of small jobs, a HandlerThread might be enough. However, if you are continuously sending long-running jobs to the background, like in an image processing app, you might want to setup a separate service and dispatch jobs into it.

If you execute long-running processes that might outlive your main activity, remember to use the new JobIntentService class, which translates into IntentService for devices running versions of Android older than Oreo, and uses jobs for the newest version of the operating system. It allows you to schedule your jobs depending on many factors, including the availability of a network connection, the level of the battery, and even to relaunch them after a device reboot.

12. Cross-Platform Applications

TODO: add an introduction

12.1. TL;DR

For those of you in a hurry, the table below summarizes the most important pieces of information in this chapter.

Table 19. Android Native Development Kit
Android iOS

Bridging Header

extern "C" JNIEXPORT void JNICALL

Bridging-Header.h

Platforms

arm64-v8a armeabi armeabi-v7a armeabi-v7a-neon mips mips64 x86 x86_64

arm64 armv7 armv7s

12.2. General Considerations

Thanks to the JVM, Android applications can run in a wide array of devices and architectures, both 32 and 64-bit. On the other side, C and C++ libraries are compiled in native code, and as such Android projects that use pre-compiled libraries must include all the binaries for each possible architecture where Android runs:

  • arm64-v8a

  • armeabi

  • armeabi-v7a

  • armeabi-v7a-neon

  • mips

  • mips64

  • x86

  • x86_64

Why would we want to use C code? It turns out that C is one of the most widely used languages in the world, and there are lots of very powerful and interesting libraries, with code tested over the years and ready to be used in many ways. Moreover, using such a cross-platform language would allow us to reuse it in other platforms, for example if we wanted to create an iOS application with the same logic.

12.3. JavaScript with Rhino

TODO: write this section

12.4. C++ with the Native Development Kit

Android applications can include native code, usually written in C and C++ in the form of source code files or pre-compiled libraries.

Android uses a mechanism built into the JVM called "Java Native Interface" or JNI for short, which allows code running indide of the JVM to call C and C++ code in the "outer world." This chapter will explain how to create a simple application using the "Native Development Kit" (NDK) and consuming an open source library built in C.

Creating an NDK Application

When creating an application targeting the NDK, you must check the "Include C++ support" checkbox in the "New Project" wizard. When this option is selected, a new screen appears at the end of the wizard with additional options.

Selecting C++ support for a new project
Figure 78. Selecting C++ support for a new project
C++ options
Figure 79. C++ options

Example Application: RSA

In our example we are going to provide a custom implementation of the RSA algorithm, used to generate pairs of private and public keys.

Follow along

The code of this section is located in the CrossPlatform/RSA folder.

This algorithm requires the use of very large numbers, for which it is usually not possible to use native numeric times such as long or int. We are going to use instead the GNU Multiple Precision Arithmetic Library, provided by the GNU Project, and of which there is a prebuilt set of binaries in Github.

Installing NDK and CMake

CMake is a cross-platform, open source build tool that has become the standard for building C and C projects. Android Studio uses CMake to build the C and C code files, and to create the final native libraries that will be accessed through JNI by the NDK.

If you encounter an error message saying "Unable to get the CMake version" when opening the same project, open the preferences and select Appearance & Behavior, System Settings, Android SDK; select the SDK Tools tab and check the "CMake" option.

Solving the CMake version build error
Figure 80. Solving the CMake version build error

Preparing your Project

The first thing we need to do is to provide our Android project with the required settings to enable JNI and the NDK:

Gradle settings to use JNI and NDK
android {
    compileSdkVersion 27
    defaultConfig {
        externalNativeBuild {
            cmake {
                cppFlags "" (3)
            }
        }
        applicationId "training.akosma.rsa"
        minSdkVersion 21
        targetSdkVersion 27
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    sourceSets {
        main {
            jniLibs.srcDirs = ['libs'] (1)
        }
    }
    externalNativeBuild {
        cmake {
            path "CMakeLists.txt" (2)
        }
    }
1 This setting indicates the location in the project of the external GMP libraries.
2 This indicates the path to the CMakeLists.txt file, which is used by Android Studio to drive the build process of the C and C code. https://cmake.org/[CMake] is the standard build tool used by C and C developers.
3 This setting gives some parameters to the C++ compiler.

The CMake build file looks like this:

CMakeLists.txt file used for the build of C++ code
cmake_minimum_required(VERSION 3.4.1)
set(CMAKE_CXX_STANDARD 11)

include_directories(${CMAKE_SOURCE_DIR}/src/main/cpp/include/)
set(SOURCES
    src/main/cpp/rsa_interface.cpp
    src/main/cpp/rsalib.cpp)

add_library(rsa SHARED ${SOURCES})
add_library(gmp SHARED IMPORTED)
add_library(gmpxx SHARED IMPORTED)

set_target_properties(gmp
                      PROPERTIES IMPORTED_LOCATION
                      ${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libgmp.so)

set_target_properties(gmpxx
                      PROPERTIES IMPORTED_LOCATION
                      ${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libgmpxx.so)

target_link_libraries(rsa gmp gmpxx)

C++ Implementation of the RSA Algorithm

The implementation of the RSA algorithm using the GMP library is located in the app/src/main/cpp folder; for example, this is the source code that encrypts a number:

C++ code to encrypt a number using the RSA algorithm
// RSA encryption
mpz_class rsa_encrypt(const mpz_t message,
                      const mpz_t e,
                      const mpz_t n) {
    mpz_t encrypted;
    mpz_init(encrypted);
    mpz_powm(encrypted, message, e, n);
    mpz_class result{encrypted};
    mpz_clear(encrypted);
    return result;
}

The interface of this code is exposed through a header file, just like any other C or C++ library:

Header interface file exposing the RSA algorithm
#ifndef CPPAPP_RSALIB_H
#define CPPAPP_RSALIB_H

#import <string>
#import <map>
#import <utility>

using std::string;
using std::pair;
using std::map;
using keyset = map<string, pair<string, string>>;

keyset rsa_keys(string p, string q, string e);
string rsa_encrypt(string message, string e, string n);
string rsa_decrypt(string message, string d, string n);

#endif //CPPAPP_RSALIB_H
About the RSA Algorithm

For the curious reader, we suggest the lecture of the rsa_keys() function in rsalib.cpp, which shows how to calculate a public/private pair of cryptographic keys. Using RSA involves at least four steps:

  1. Key calculation.

  2. Key distribution.

  3. Encryption.

  4. Decryption.

In this example we are only going to deal with steps one, three and four.

Key Calculation

The mathematics involved are not very complex and can be summarized in the following algorithm:

First, choose two distinct prime numbers p and q, with different digit length, to make them harder to guess. Then multiply both numbers and assign the result to n:

\[n = p \times q\]

Calculate the least common multiple of p – 1 and q – 1 and call it lambda.

\[\lambda = lcm(p - 1, q - 1)\]

Choose a number e coprime to lambda so that

\[1 < e < \lambda\]

and such as:

\[gcd(e, \lambda) = 1\]

Find the modular multiplicative inverse of e and lambda (using the mpz_invert() function of GMP) and call it d such as:

\[d \times e \bmod \lambda = 1\]

The public key is the pair of integers (n, e), and the private key is the pair of integers (n, d).

Encryption

To encrypt a message m, just apply the following formula (implemented with the mpz_powm() GMP function):

\[z = m^e \bmod n\]

Where z is the encrypted message.

Decryption

To find the original message m from its encrypted version z, apply this formula (implemented with the mpz_powm() GMP function):

\[m = z^d \bmod n\]

JNI Interface

To make this C++ code available in the JVM we need to create two things:

  1. A Kotlin class with external functions;

  2. A C++ file with special annotations to provide the implementation to the external functions in the Java class.

In this sense, the C++ code will serve as the real implementation of the Java class:

JNI code in C++
extern "C"
JNIEXPORT jstring JNICALL
Java_training_akosma_rsa_RSAManager_encrypt(
        JNIEnv *env,
        jobject thisObj,
        jstring msg) {

    string n = valueOfField(env, thisObj, n_field);
    string e = valueOfField(env, thisObj, e_field);
    const char *message = env->GetStringUTFChars(msg, JNI_FALSE);
    string result = rsa_encrypt(message, e, n);
    env->ReleaseStringUTFChars(msg, message);
    return env->NewStringUTF(result.c_str());
}

And the Kotlin class looks like this:

Kotlin class using the C++ implementatin
class RSAManager {
    val p = "162259276829213363391578010288127"
    val q = "618970019642690137449562111"
    val e = "170141183460469231731687303715884105727"
    var n = ""
    var d = ""

    init {
        initialize()
        keys()
    }

    external fun initialize() (2)
    external fun destroy()
    external fun keys()
    external fun encrypt(text: String): String
    external fun decrypt(text: String): String

    companion object {
        init {
            System.loadLibrary("rsa") (1)
        }
    }
}
1 This code is required from the JVM side, and loads the "rsa" library, compiled from the C++ files shown above.
2 This initialization is required per the JNI standard, and performs the connection between variables inside the JVM and those in the C class. Please refer to the `Java_training_akosma_rsa_RSAManager_initialize` in the C file for details.

In JNI, the name of the C++ code Java_training_akosma_rsa_RSAManager_encrypt follows a very strict naming convention and signature: it includes the prefix Java, followed by the complete namespace and class name of our Kotlin application (training.akosma.rsa.RSAManager) and finally the name of the method itself (encrypt). This, plus the sequence of parameters, are all part of the JNI specification.

Consuming the JNI Code

Now that we have a Kotlin class, we can use it from our application just like we would any other class:

Activity using JNI code
val rsa = RSAManager()
val message = "1234567890"
val encrypted = rsa.encrypt(message)
val decrypted = rsa.decrypt(encrypted)

originalView.text = "Message: $message"
encryptedView.text = "Encrypted: $encrypted"
decryptedView.text = "Decrypted: $decrypted"
publicKeyView.text = "Public key: (${rsa.e}, ${rsa.n})"
privateKeyView.text = "Private key: (${rsa.d}, ${rsa.n})"

rsa.destroy()

The final application will display an output similar to the one in the image below.

RSA implementation running in device
Figure 81. RSA implementation running in device
Size of the keys

The image above shows the length of the keys used for the encryption and decryption, and explains the need of a library such as GMP to perform these calculations. Those integers are way beyond the maximum limit of any integer type, even in 64-bit platforms.

iOS Example

To show the possibility of reuse of C code between Android and iOS, the `CrossPlatform/RSA_iOS` folder includes an iOS implementation of the same application, using the same C libraries, to perform the same operation.

Follow along

The code of this section is located in the CrossPlatform/RSA_iOS folder.

The iOS application, to be opened with Xcode 9 and written in Swift 4, uses a "Bridging Header" to access an implementation in Objective-C which in turn provides access to the C GMP libraries. At the time of this writing, Swift does not allow to consume C++ code directly, but this approach does the trick.

Bridging Header allowing Swift to access C++ code
#ifndef RSA_Bridging_Header_h
#define RSA_Bridging_Header_h

#import "RSAManager.h"

#endif /* RSA_Bridging_Header_h */

The execution of the code is shown in the screenshot below.

RSA implementation running in iOS
Figure 82. RSA implementation running in iOS

12.5. Conclusion

C++ provides a complex yet powerful mechanism for code reuse among iOS and Android projects. Thanks to JNI, Android applications can consume native code, but given the large array of CPU architectures that Android targets, developers must provide compiled libraries for each platform. These libraries can be consumed in Objective-C and Swift using standard bridging headers.

TODO: expand the conclusion

Part 6: Wrapping Up

Time to say goodbye!

Did you find this book useful? Let me know!

13. Conclusion

It is with joy that I close this book, hoping that it will be useful for you during your migration from iOS to Android. The Android development toolkit has evolved tremendously in the past few years, and new tools keep appearing every day. Long gone are the days of slow emulators, clunky IDEs and difficult installations. And now with Kotlin, Android is now more approachable than ever!

Where to go from now? There is no better teacher than experience, so I suggest that you start porting your iOS applications to the Android environment, taking care of following the design guidelines from Google and making sure that you craft a great user experience. iOS and Android have some big differences in philosophy, so you should never just translate the source code when porting your apps – even though Kotlin makes that worringly easy. Make sure to follow and embrace the "Android way" at every step, to make your users comfortable and happy.

I look forward to downloading and using your apps!

Bibliography

This is the list of books and articles used during the preparation of this book.

Books
Websites and Blog Articles

Appendix A: Android Studio Shortcuts

This appendix lists the most common Android Studio shortcuts. For a complete list, please refer to the Android Studio Keyboard Shortcuts web page.

Table 20. Android Studio Shortcuts
Shortcut Purpose

+L

Go to line

Option+Return

Autocomplete imports

Ctrl++F12

Maximize/minimize editor

Ctrl+G

Start multiple select/replace session

+O

Find class

+Shift+A

Find action

Shift+Shift

Quick search everywhere

+Shift+O

Quick open file

Option+

Jump to Lint issue in code

+E

Recently opened files pop-up

+N

Generate code (getters, setters, constructors, etc.)

Ctrl+T

Refactor selected element

Shift+F6

Rename refactoring

+O

Override methods

+D

Duplicate current line

Ctrl+Space

Basic code completion

+U

Go to super method/class

+/

Comment/uncomment current line(s)

+Shift+/

Comment/uncomment current line(s) with C style

+Option+L

Reformat code

Shift++Option+L

Reformat code dialog

Control+Option+O

Optimize imports

Ctrl+R

Build and run

Ctrl+D

Debug

F8

Step over

F7

Step into

+F8

Toggle breakpoint

Shift++F8

Open Breakpoints window

+Option+M

Extract method

+Shift+T

Edit or create test for current class

Ctrl+Shift+R

Run current test

Appendix B: Third Party Android Developer Tools

This appendix contains links to various third-party developer tools (some commercial, some open source and free) available to Android developers besides the standard Android Studio development environment described in this book.

Table 21. Programming Environments
Name Programming Language Link

Corona

Lua

coronalabs.com/

Delphi

Object Pascal

www.embarcadero.com/products/delphi/

Kivy

Python

kivy.org/

Lazarus

Free Pascal

www.lazarus-ide.org/

Processing

Processing

processing.org/

Qt

C++

www.qt.io/

React Native

JavaScript

facebook.github.io/react-native/

RubyMotion

Ruby

www.rubymotion.com/

Scala on Android

Scala

scala-android.org/

Silver

Swift

elementscompiler.com/elements/silver/

Visual Studio

C++

www.visualstudio.com/

Xamarin

C#

www.xamarin.com/

Table 22. Other Tools
Name Kind Link

ACRA

Crash reporter

acra.ch/

Android Dev Metrics

Performance library

github.com/frogermcs/AndroidDevMetrics/

Android File Transfer

File management

www.android.com/filetransfer/

Appium

UI testing

appium.io/

Barista

UI testing

schibstedspain.github.io/Barista/

Briefs

UI prototyping

giveabrief.com/

Buck

Build tool

buckbuild.com/

Butter Knife

UI library

jakewharton.github.io/butterknife/

Dagger

Dependency injection

google.github.io/dagger/

Droid @ Screen

Screen sharing

droid-at-screen.org/

EventBus

Event library

greenrobot.org/eventbus/

FindBugs Static Analyzer

Static analyzer

findbugs.sourceforge.net/

Genymotion

Android Emulator

www.genymotion.com/

Glide

Network library

github.com/bumptech/glide

Gson

Serialization library

github.com/google/gson/

JUnit

Unit testing

junit.org/

Kotlin Style Guide

Reference

android.github.io/kotlin-guides/style.html

Kotlin – Java Interop Guide

Reference

android.github.io/kotlin-guides/interop.html

LogCat

Log viewer

github.com/yepher/LogCat/

NimbleDroid

Performance analysis service

nimbledroid.com/

OkHttp

Network library

square.github.io/okhttp/

OrmLite

Storage library

ormlite.com/

Origami

UI prototyping

origami.design/

PaintCode

UI prototyping

www.paintcodeapp.com/

PID cat

Log viewer

github.com/JakeWharton/pidcat/

Realm

Storage library

realm.io/

Reflector 2

Screen sharing

www.airsquirrels.com/reflector/

Retrofit

Networking library

square.github.io/retrofit/

Retrolambda

Library

github.com/orfjackal/retrolambda/

Robolectric

Unit testing runner

robolectric.org/

Robotium

UI testing

github.com/RobotiumTech/robotium/

Screen Siz.es

Reference

screensiz.es/

Socket.io

Network library

socket.io/

SQLCipher for Android

Storage library

www.zetetic.net/sqlcipher/sqlcipher-for-android/

Stetho

Debugging tool

facebook.github.io/stetho/

SwiftKotlin

Development tool

github.com/angelolloqui/SwiftKotlin

Vysor

Screen sharing

vysor.io/

Viewport Sizes

Reference

viewportsizes.com/

Appendix C: TL;DR

This appendix contains all the TL;DR tables at the beginning of each chapter, ready to print and keep within reach.

Table 23. Toolchain
Android iOS

IDE

Android Studio

Xcode

Profiling

Android Device Monitor

Instruments

Preview

Android Emulator

iOS Simulator

Programming Language

Kotlin or Java

Swift or Objective-C

Command Line

gradlew – ant

xcodebuild

Hacking

Rooting

Jailbreaking

Application metadata

AndroidManifest.xml

Info.plist

Dependency Manager

Gradle

CocoaPods – Carthage

Distribution

APK

IPA

Debugger

ADB + DDMS

LLDB

Logger

LogCat

NSLog() or print()

View Debugging

Hierarchy viewer

Xcode view debugging

Static Analysis

Android Lint

Clang Static Analyzer

Table 24. Debugging
Android iOS

Debugger

JDB

LLDB

Log output

logcat

Xcode console

Remote debugging

yes

yes

Log viewers

PID Cat & LogCat

libimobiledevice & deviceconsole

Network logger

NSLogger

NSLogger

Table 25. UI
Android iOS

UI design

Layout files

NIB/XIB/Storyboard

Controllers

Activity

UIViewController

Callbacks

Anonymous Classes

IBAction

Views

android.view.View

UIView

Connecting views

findViewById(R.id.xxxxx)

IBOutlet

Text fields

EditText

UITextField

Buttons

Button

UIButton

Text labels

TextView

UILabel

Translatable strings

strings.xml

Localizable.strings

Navigation between controllers

Intent

Storyboard Segue

UI decomposition

Fragment

Children UIViewController

Serialization

Parcelable

NSPropertyListSerialization

Dialog boxes

AlertDialog

UIAlertController

Table 26. Graphics
Android iOS

Framework

android.graphics

UIKit

Views

View

UIView

Coordinate system

Origin at top left

Origin at top left

Location on screen

LayoutParams

CGRect

Images

ImageView

UIImageView

Colors

Color (manipulates int!)

UIColor

Bezier curves

Path

UIBezierPath

Drawing method

onDraw()

draw()

Drawing context

Canvas

CGContext

Mark as "dirty"

invalidate()

setNeedsDisplay()

Gestures

GestureDetector

UIGestureRecognizer

Pinch gesture

ScaleGestureDetector

UIPinchGestureRecognizer

Affine Transformations

Matrix

CGAffineTransform

Simple animations

View.animate()

animate(withDuration:animations:)

Complex animations

android.animation.Animator

CAAnimation

Application-level memory warnings

Application.onLowMemory()

applicationDidReceiveMemoryWarning()

Activity-level memory warnings

Activity.onTrimMemory()

didReceiveMemoryWarning()

Table 27. Networking
Android iOS

Native networking library

HttpURLConnection

NSURLConnection

Background mechanism

android.os.AsyncTask

NSOperation

JSON Parser

org.json.JSONObject

NSJSONSerialization

JSON (de)serialization

Gson

NSPropertyListSerialization

XML SAX

org.xmlpull.v1.XmlPullParser

NSXMLParser

XML DOM

org.w3c.dom.Document

KissXML

Array

List<> & ArrayList<>

NSArray & NSMutableArray

Table view

RecyclerView

UITableView

Table view data

RecyclerView.Adapter

UITableViewDataSource

Table view cell

RecyclerView.ViewHolder

UITableViewCell

REST Client

Retrofit

RESTKit

Popular networking library

OkHttp

AFNetworking

Web view

android.webkit.WebView

WKWebView

Zeroconf Service

android.net.nsd.NsdServiceInfo

NetService

Zeroconf Browser

android.net.nsd.NsdManager

NetServiceBrowser

Table 28. Storage
Android iOS

Local documents

Context.getFilesDir()

NSSearchPathForDirectoriesInDomains()

External storage

Environment.getExternalStorageDirectory()

n/a

Bundled resource

getResources()

NSBundle

Downloading files

DownloadManager

UIBackgroundTaskIdentifier

Notifications

BroadcastReceiver

NSNotificationCenter

Periodic tasks

android.os.Handler & Runnable

NSTask

Preferences

PreferenceManager & SharedPreferences

NSUserDefaults

Sqlite wrapper

SQLiteOpenHelper

FMDB

Object Relational Mapper

Room

Core Data

Realm

Realm

Realm

Table 29. Sensors
Android iOS

Framework

android.hardware

Core Motion & Core Location

Main class

SensorManager

CMMotionManager

Callback methods

SensorEventListener

Blocks

Sensor data

SensorEvent

CMGyroDataCMAccelerometerDataCMAttitude

Location

LocationManager

CLLocationManager

Table 30. Multimedia
Android iOS

Display image

android.widget.ImageView

UIImageView

Display video

android.widget.VideoView

MPMoviePlayerViewController

Viewer application

Gallery

Photos

Image picker

Intent.ACTION_PICK

UIImagePickerController

Audio recorder

android.media.MediaRecorder

AVAudioRecorder

Audio player

android.media.MediaPlayer

AVAudioPlayer

Text to speech engine

android.speech.tts.TextToSpeech

AVSpeechSynthesizer

Table 31. Architecture
Android iOS

Notification center

LocalBroadcastManager

NSNotificationCenter

Flexible designs

Interface-oriented programming

Protocol-oriented programming

Dependency injection

Dagger

Typhoon – Swinject – Cleanse …

ReactiveX

RxJava & RxAndroid

RxSwift

Observing data

rx.Observable

willSet & didSet – KVO

Table 32. Testing
Android iOS

Unit testing framework

JUnit 4

XCTest

Mock objects framework

Mockito

OCMock – OCMockito

Most common issue

Kotlin optionals

(ObjC) Messages to nil & (Swift) optionals

Runtime warnings

StrictMode

Xcode Runtime Issues

Table 33. Multithreading
Android iOS

Mechanisms

Threads, AsyncTasks, etc

Grand Central Dispatch

Units

java.lang.Runnable or android.os.AsyncTask

Blocks

Queues

android.os.Handler

GCD dispatch queues

Long-running

android.support.v4.app.JobIntentService

Background modes & fetch

Table 34. Cross Platform
Android iOS

Bridging Header

extern "C" JNIEXPORT void JNICALL

Bridging-Header.h

Platforms

arm64-v8a armeabi armeabi-v7a armeabi-v7a-neon mips mips64 x86 x86_64

arm64 armv7 armv7s

Appendix D: Supported Media Formats

This table summarizes the most important media formats supported by Android.[7]

Table 35. Image
Extension Container Format Data Format/Codec

.jpg

JPEG

JPEG

.gif

GIF

GIF

.png

PNG

PNG

.bmp

BMP

BMP

.webp

WebP

WebP

Table 36. Audio
Extension Container Format Data Format/Codec

.3gp – .mp4 – .m4a – .aac

3GPP

AAC – AMR

.flac

FLAC

FLAC

.mp3

MP3

MP3

.mid – .xmf – .rtx – .ota

MIDI Type 0 – RTTTL/RTX

MIDI

.ogg

Ogg

Vorbis

.wav

PCM – WAVE

WAVE

.mkv

Matroska

Opus

Table 37. Video
Extension Container Format Data Format/Codec

.3gp – .mp4

3GPP – MPEG-4

H.263 – H.265

.mp4

MPEG-4

H.265

.webm – .mkv

WebM – Matroska

VP8 – VP9


1. Source: stackoverflow.com/a/2025541/133764
2. Actually, this is a recommended practice for iOS development as well, but for good or for bad, most iOS developers I have met usually overlook this check.
3. Screenshot taken from a OnePlus 3 unit.
4. The full list of normal permissions is avalable in the developer website: developer.android.com/guide/topics/security/normal-permissions.html
5. The full list of dangerous permissions is available in the developer website: developer.android.com/guide/topics/security/permissions.html#normal-dangerous
6. Adapted from stackoverflow.com/a/490985/133764
7. Source: developer.android.com/guide/appendix/media-formats.html