Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Asynchronous Android Programming
Asynchronous Android Programming

Asynchronous Android Programming: Unlock the power of multi-core mobile devices to build responsive and reactive Android applications , Second Edition

Arrow left icon
Profile Icon Liles Profile Icon Vasconcelos
Arrow right icon
₹800 per month
Full star icon Full star icon Full star icon Full star icon Full star icon 5 (3 Ratings)
Paperback Jul 2016 394 pages 2nd Edition
eBook
₹799 ₹3276.99
Paperback
₹4096.99
Subscription
Free Trial
Renews at ₹800p/m
Arrow left icon
Profile Icon Liles Profile Icon Vasconcelos
Arrow right icon
₹800 per month
Full star icon Full star icon Full star icon Full star icon Full star icon 5 (3 Ratings)
Paperback Jul 2016 394 pages 2nd Edition
eBook
₹799 ₹3276.99
Paperback
₹4096.99
Subscription
Free Trial
Renews at ₹800p/m
eBook
₹799 ₹3276.99
Paperback
₹4096.99
Subscription
Free Trial
Renews at ₹800p/m

What do you get with a Packt Subscription?

Free for first 7 days. ₹800 p/m after that. Cancel any time!
Product feature icon Unlimited ad-free access to the largest independent learning library in tech. Access this title and thousands more!
Product feature icon 50+ new titles added per month, including many first-to-market concepts and exclusive early access to books as they are being written.
Product feature icon Innovative learning tools, including AI book assistants, code context explainers, and text-to-speech.
Product feature icon Thousands of reference materials covering every tech concept you need to stay up to date.
Subscribe now
View plans & pricing
Table of content icon View table of contents Preview book icon Preview Book

Asynchronous Android Programming

Chapter 1. Asynchronous Programming in Android

Asynchronous programming has become an important topic of discussion in the past few years, especially when using the concurrent processing capabilities available on the most recent mobile hardware.

In recent years, the number of independent processing units (cores) available on the CPU have increased, so to benefit from this new processing power, a new programming model called asynchronous programming has appeared to orchestrate the work between the several independent hardware-processing units available on the device. Asynchronous programming comes to the rescue to solve the problems that could arise from this new processing paradigm.

Android applications, since they mostly run on devices with multiple units of processing, should take advantage of asynchronous programming to scale and improve the application performance when blocking operations, and when CPU-intensive tasks are required.

Android is an open source operating system (OS) based on Linux kernel that was devised in 2003 by Andy Rubin, Nick Sears, Chris White, and Rick Miner, and then acquired by Google in July, 2005.

The Android OS, actually maintained by Google and the Open Handset Alliance, was created to provide an open mobile-device platform for devices with limited resources of computation, memory, and energy.

The platform has been incorporating advanced mobile devices standards, such as NFC and Bluetooth LE, and its scope has grown from a pure smartphone platform to a broader software platform for smart watches, TVs, tablets, and consoles.

The maintainers have been regularly updating the platform with great features and some improvements over minor and major releases since the first release.

The following diagram displays the Android versions over time:

Asynchronous Programming in Android

Android software stack

Android software stack (C libraries and Java frameworks), orchestrated by the Android runtime (Dalvik VM, and most recently, ART) was created around the Linux kernel to provide highly interactive user experiences over a well-proven group of technologies.

In each new OS version, a well-defined application interface (API) is provided to the developer in order to create applications around the new features and standards introduced with the release.

The Android application compiled code (bytecode), typically a Java compiled code, runs on a virtual machine based on Dalvik or ART.

Dalvik runtime

The Dalvik VM (DVM) runtime, created by Dan Borstein, was the first runtime for the platform and is a register-based virtual machine that was created to run the Java code efficiently in a constrained runtime with a limited amount of power processing, RAM, and electric power.

Dalvik's creators claim that the DVM is, on an average, around 30% more efficient than the standard Java VM (Oracle). According to Bornstein, it requires 30% less instructions and 35 % less coding units.

Clearly, Google has gone to great lengths to squeeze every drop of performance out of each mobile device to help developers build responsive applications.

The virtual machine, which runs the Java code compiled and transformed to the dex format over the dx tool, runs on a Linux process with its own memory space and file descriptors. It also manages its own group of threads.

In more advanced architectures, an Android application might run a service in a separate process and communicate over the IPC mechanism, but most of the time, it runs on a single self-contained process.

The dex file and application resources are packed in an Android application package (APK) by the AAPT and installed over Google Play in the end user devices.

Note

The application store distribution model has become extremely popular on the mobile platforms since the launch of the Apple iPhone in 2007.

Since Android 2.2 the DVM comes with a trace-based Just-In-Time (JIT) compilation feature that actively optimizes every time the application runs some short segments of frequently used bytecode called traces.

The generated machine code provides significant performance improvements in the application execution and on the time spent on some intensive CPU tasks, and thereafter, decreases the battery power used.

ART runtime

The ART runtime is a new version of the DVM and was introduced to improve the runtime performance and memory consumption. The new runtime was introduced in Android 4.4 KitKat as an experimental runtime, and since the Android 5.0 Lollipop, it has become the main Android runtime.

This new runtime, making use of the ahead-of-time (AOT) compilation, brings new app-performance optimizations on startup time and application execution. The AOT, as opposed to DVM JIT (Just in Time), compiles the dex files during the installation time using the device dex2oat tool. The compiled code generated from the dex2oat tool generates system-dependent code for the target device and removes the delay introduced by the JIT compilation during each application execution.

The AOT compiler also reduces the number of processor cycles used by the application as it removes the time spent by the JIT compiler to convert the code into machine code, and then uses less battery power to run the application.

One of the drawbacks of the AOT compilation is the larger memory footprint in comparison with the JIT used by DVM.

With the new runtime, some improvements were also introduced in the memory allocation and on Garbage Collection (GC), resulting in a more responsive UI and better application experience.

Memory sharing and Zygote

Basically, the platform runs an instance of DVM/ART for each application, but large optimization of the platform is brought about by the way a new DVM instance is created and managed.

A special process called the Zygote (first life cell in an animal's reproduction)—the process that all the Android applications are based on—is launched when an Android device initially boots.

The Zygote starts up a virtual machine, preloads the core libraries, and initializes various shared structures. It then waits for instructions by listening on a socket.

When a new Android application is launched, the Zygote receives a command to create a virtual machine to run the application on. It does this by forking its pre-warmed VM process and creating a new child process that shares some memory portions with the parent, using a technique called copy-on-write (COW).

The COW technique, available on most Unix systems, only allocates new memory on the child process when the process tries to change the memory cloned from the parent process.

This technique has some fantastic benefits, as listed in the following:

  • First, the virtual machine and core libraries are already loaded into the memory. Not having to read this significant chunk of data from the filesystem to initialize the virtual machine drastically reduces the startup overhead.
  • Second, the memory in which these core libraries and common structures reside is shared by the Zygote with all other applications, resulting in saving a lot of memory when the user is running multiple apps.

Android process model

Android is a multiuser, multitasking system that can run multiple applications in parallel, where all the applications attempt to acquire CPU time to execute its job.

Each application runs independently on an isolated Linux process cloned from the Zygote process, and by default, all the Android components run within the same process with the same name as the application package specified in Android Application Manifest (AAM).

The Linux kernel will fairly allocate small amounts of CPU time for application execution called CPU time slices. This time-slicing approach means that even a single-processor device can appear to be actively working in more than one application at the same time, when in fact, each application is taking very short turns on the CPU.

Process ranks

The Android operating system tries to maintain the application running for as long as possible, but when the available memory is low, it will try to free resources in the system by killing the processes with lower importance first.

This is when process ranking comes into the picture; the Android processes are ranked in the next five categories from the higher priority to the lower priorities:

  • Foreground process: This is a process that hosts an activity or service that the user is currently interacting with: a service started in the foreground or service running its life cycle callbacks
  • Visible process: This is a process that hosts a paused activity or service bounded to a visible activity
  • Service process: This is a process that hosts a service not bound to a visible activity
  • Background process: This is a process that hosts a non-visible activity; all background processes are sorted over a Least-Recently-Used (LRU) list, therefore, the most recently used processes are the last killed processes when they have the same rank
  • Empty process: This is a process used to cache inactive Android components and to improve any component startup time

When the system reaches a point that it needs to release resources, the processes available to be killed will be sorted, taking into account the process rank, last used processes, and components running.

Process sandboxing

The Android application always runs under a unique Linux user ID (UID) assigned to the application during the application installation so that the process runs on a sandboxed environment, which by default, isolates your data and code execution from other apps.

In some cases, a user could explicitly be required to share the UID with another application to have access to their data:

USER     PID   PPID  VSIZE  RSS  PC  NAME
root            319   1     1537236 31324 S zygote
….
u0_a221   5993  319   1731636 41504 S com.whatsapp
u0_a96    3018  319   1640252 29540 S com.dropbox.android
u0_a255   4892  319   1583828 34552 S com.accuweather.android…

The preceding table that results from running the adb shell ps command in the computer with Android SDK Table is a list of Android running processes.

The first column shows the user identifier (UID) assigned at the time of installation, the second column is the process ID (PID), the third column shows the parent process ID (PPID) that for Android applications is the Zygote process, and the last column shows the application package.

From this list, we can assure that the WhatsApp application is running under the user ID u0_a221 with the process ID 5993 and the parent process is the Zygote process with the PID 319.

Android thread model

Within an Android process, there may be many threads of execution. Each thread is a separate sequential flow of control within the overall program—it executes its instructions in order, one after the other, and they also share allocated slices of CPU time managed by the operating system task scheduler.

While the application process is started by the system and prevented from directly interfering with data in the memory address space of other processes, the threads may be started by an application code and can communicate and share data with other threads within the same process. Apart from the shared data that all the threads share in the same process, a thread can use its own memory cache to store its data in its own memory space.

The main thread

When the application process starts, apart from DVM housekeeping threads, the system creates a thread of execution called main. This thread, as the name explains, plays a crucial role in the application lifetime as it is the thread that interacts with the Android UI components, updating the state and their look on the device screen.

Moreover, by default, all the Android application components (Activity, Service, ContentProvider, and BroadcastsReceiver) are also executed over the main thread line of execution. The following image shows the lists of threads running inside an application process with the main thread at the top of the list with a unique thread ID (TID) assigned by the system:

The main thread

The main thread, also known as UI Thread, is also the thread where your UI event handling occurs, so to keep your application as responsible as possible, you should:

  • Avoid any kind of long execution task, such as input/output (I/O) that could block the processing for an indefinite amount of time
  • Avoid CPU-intensive tasks that could make this thread occupied for a long time

The following diagram displays the main interactions and components involved in the Looper line of execution thread:

The main thread

The UI/Main thread, which has a Looper facility attached to it, holds a queue of messages (MessageQueue) with some unit of work to be executed sequentially.

When a message is ready to be processed on the queue, the Looper Thread pops the message from the queue and forwards it synchronously to the target handler specified on the message.

When the target Handler finishes its work with the current message, the Looper thread will be ready to process the next message available on the queue. Hence, if the Handler spent a noticeable amount of time processing the message, it will prevent Looper from processing other pending messages.

For example, when we write the code in an onCreate() method in the Activity class, it will be executed on the main thread. Likewise, when we attach listeners to user-interface components to handle taps and other user-input gestures, the listener callback executes on the main thread.

For apps that do little I/O or processing, such as applications that don't do complex math calculations, don't use the network to implement features, or don't use filesystem resources, this single thread model is fine. However, if we need to perform CPU-intensive calculations, read or write files from permanent storage, or talk to a web service, any further events that arrive while we're doing this work will be blocked until we're finished.

Note

Since the Android 5.0 (Lollipop), a new important thread named RenderThread was introduced to keep the UI animations smooth even when the main thread is occupied doing stuff.

The Application Not Responding (ANR) dialog

As you can imagine, if the main thread is busy with a heavy calculation or reading data from a network socket, it cannot immediately respond to user input such as a tap or swipe.

An application that doesn't respond quickly to user interaction will feel unresponsive—anything more than a couple of hundred milliseconds delay is noticeable. This is such a pernicious problem that the Android platform protects users from applications that do too much on the main thread.

Note

If an app does not respond to user input within five seconds, the user will see the Application Not Responding (ANR) dialog and will be offered the option to quit the application.

The following screenshot shows a typical Android ANR dialog:

The Application Not Responding (ANR) dialog

Android works hard to synchronize the user interface redraws with the hardware-refresh rate. This means that it aims to redraw at the rate of 60 frames per second—that's just 16.67 ms per frame. If we do work on the main thread that takes anywhere near 16 ms, we risk affecting the frame rate, resulting in jank—stuttering animations, jerky scrolling, and so on.

Ideally, of course, we don't want to drop a single frame. Jank, unresponsiveness, and especially the ANR, offer a very poor user experience, which translates into bad reviews and unpopular applications. A rule to live by when building Android applications is: do not block the main thread!

Note

Android provides a helpful strict mode setting in Developer Options on each device, which will flash on the screen when applications perform long-running operations on the main thread.

Further protection was added to the platform in Honeycomb (API level 11) with the introduction of a new Exception class, NetworkOnMainThreadException, a subclass of RuntimeException that is thrown if the system detects network activity initiated on the main thread.

Maintaining responsiveness

Ideally then, we may want to offload any long-running operations from the main thread so that they can be handled in the background by another thread, and the main thread can continue to process user-interface updates smoothly and respond in a timely fashion to user interactions.

The typical time-consuming tasks that should be handled on a background thread include the following:

  • Network communications
  • Input and output file operations on the local filesystem
  • Image and video processing
  • Complex math calculations
  • Text processing
  • Data encoding and decoding

For this to be useful, we must be able to coordinate the work and safely pass data between cooperating threads—especially between background threads and the main thread, and it is exactly to solve this problem that asynchronous programming is used.

Let's get started with the synchronous versus asynchronous diagram:

Maintaining responsiveness

The preceding example graphically shows the main differences between the two models of processing. On the left-hand side, the data download task occurs on the main thread, keeping the thread busy until the download data is finished. So if the user interacts with the UI and generates an event such as a touch event, the application will suffer a lag or will become unresponsive if the download task takes a substantial amount of time to finish.

On the right-hand side, the asynchronous model will hand over the download data task to another background thread, keeping the main thread available to process any event coming from the UI interaction. When the downloaded data is available, the background task could post the result to the main thread if the data handling needs to update any UI state.

When we use an asynchronous model to program our application, the Android OS will also take advantage of additional CPU cores available in the most recent devices to execute multiple background threads at the same time and increase the application's power efficiency.

Note

This simultaneous execution of separate code paths that potentially interact with each other is known as concurrency.

The simultaneous execution of subunits of work in parallel to complete one unit of work is known as parallelism.

Concurrency in Android

As explained before, in order to achieve a scalable application in a multicore device environment, the Android developer should be capable of creating concurrent lines of execution that combine and aggregate data from multiple resources.

The Android SDK, as it is based on a subset of Java SDK, derived from the Apache Harmony project, provides access to low-level concurrency constructs such as java.lang.Thread, java.lang.Runnable, and the synchronized and volatile keywords.

These constructs are the most basic building blocks to achieve concurrency and parallelism, and all the high-level asynchronous constructs are created around these building blocks.

The most basic one, java.lang.Thread, is the class that is mostly used and is the construct that creates a new independent line of execution in a Java program:

public class MyThread extends Thread {
    public void run() {
        Log.d("Generic", "My Android Thread is running ...");
    }
}

In the preceding code, we subclassed java.lang.Thread to create our own independent line of execution. When Thread is started, the run method will be called automatically and it will print the message on the Android log:

MyThread myThread = new MyThread();
myTread.start();

At this time, we will create an instance of our MyThread, and when we start it in the second line, the system creates a thread inside the process and executes the run() method.

Other helpful thread-related methods include the following:

  • Thread.currentThread(): This retrieves the current running instance of the thread
  • Thread.sleep(time): This pauses the current thread from execution for the given period of time
  • Thread.getName() and Thread.getId(): These get the name and TID, respectively so that they can be useful for debugging purposes
  • Thread.isAlive(): This checks whether the thread is currently running or it has already finished its job
  • Thread.join(): This blocks the current thread and waits until the accessed thread finishes its execution or dies

The Runnable interface, which is another building block that comes from the Java API, is an interface defined to specify and encapsulate code that is intended to be executed by a Java thread instance or any other class that handles this Runnable:

package java.lang;

public interface Runnable {   
    public abstract void run();
}

In the following code, we basically created the Runnable subclass so that it implements the run() method and can be passed and executed by a thread:

public class MyRunnable implements Runnable {

    public void run(){
        Log.d("Generic","Running in the Thread " +
                        Thread.currentThread().getId());
	// Do your work here
	...
    }
}

Now our Runnable subclass can be passed to Thread and is executed independently in the concurrent line of execution:

Thread thread = new Thread(new MyRunnable());
thread.start();

While starting new threads is easy, concurrency is actually a very difficult thing to do. Concurrent software faces many issues that fall into two broad categories: correctness (producing consistent and correct results) and liveness (making progress towards completion). Thread creation could also cause some performance overhead, and too many threads can reduce the performance, as the OS will have switch between these lines of execution.

Correctness issues in concurrent programs

A common example of a correctness problem occurs when two threads need to modify the value of the same variable based on its current value. Let's consider that we have a myInt integer variable with the current value of 2.

In order to increment myInt, we first need to read its current value and then add 1 to it. In a single-threaded world, the two increments would happen in a strict sequence—we will read the initial value 2, add 1 to it, set the new value back to the variable, and then repeat the sequence. After the two increments, myInt holds the value 4.

In a multithreaded environment, we will run into potential timing issues. It is possible that two threads trying to increment the variable would both read the same initial value 2, add 1 to it, and set the result (in both cases, 3) back to the variable:

int myInt = 2;
...
public class MyThread extends Thread {

    public void run() {
         super.run();
         myInt++;
   }
}
...
Thread t1 = new MyThread();
Thread t2 = new MyThread();
t1.start();
t2.start();

Both threads behaved correctly in their localized view of the world, but in terms of the overall program, we will clearly have a correctness problem; 2 + 2 should not equal 3! This kind of timing issue is known as a race condition.

A common solution to correctness problems, such as race conditions, is mutual exclusion—preventing multiple threads from accessing certain resources at the same time. Typically, this is achieved by ensuring that threads acquire an exclusive lock before reading or updating shared data.

To achieve this correctness, we can make use of the synchronized construct to solve the correctness issue on the following piece of code:

Object lock = new Object();
public class MyThread extends Thread {
    public void run() {
        super.run();
        synchronized(lock) {
            myInt++;
        }
    }
}

In the preceding code, we used the intrinsic lock available in each Java object to create a mutually exclusive scope of code that will enforce that the increment sentence will work properly and will not suffer from correctness issues as explained previously. When one of the threads gets access to the protected scope, it is said that the thread acquired the lock, and after the thread gets out of the protected scope, it releases the lock that could be acquired by another thread.

Another way to create mutually exclusive scopes is to create a method with a synchronized method:

int myInt = 2;
synchronized void increment(){
    myInt++;
}
...
public class IncrementThread extends Thread {
    public void run() {
        super.run();
        increment();
    }
}

The synchronized method will use the object-intrinsic lock, where myInt is defined to create a mutually exclusive zone so IncrementThread, incrementing myInt through the increment(), will prevent any thread interference and memory consistency errors.

Liveness issues in concurrent programs

Liveness can be thought of as the ability of the application to do useful work and make progress towards goals. Liveness problems tend to be an unfortunate side effect of the solution to the correctness problems.

Both properties should be achieved in a proper concurrent program, notwithstanding the correctness is concerned with making progress in a program preventing a deadlock, livelock, or starvation from happening, and the correctness is concerned with making consistent and correct results.

Note

Deadlock is a situation where two or more threads are unable to proceed because each is waiting for the others to do something. Livelock is a situation where two or more threads continuously change their states in response to the changes in the other threads without doing any useful work.

By locking access to data or system resources, it is possible to create bottlenecks where many threads are contending to access a single lock, leading to potentially significant delays.

Worse, where multiple locks are used, it is possible to create a situation where no thread can make progress because each requires exclusive access to a lock that another thread currently owns—a situation known as a deadlock.

Thread coordination

Thread coordination is an important topic in concurrent programming, especially when we want to perform the following tasks:

  • Synchronize access of threads to shared resources or shared memory:
    • Shared database, files, system services, instance/class variables, or queues
  • Coordinate work and execution within a group of threads:
    • Parallel execution, pipeline executions, inter-dependent tasks, and so on

When we want to coordinate thread efforts to achieve a goal, we should try to avoid waiting or polling mechanisms that keep the CPU busy while we wait for an event in another thread.

The following example shows us a small loop where we will continuously occupy the CPU while we wait for a certain state change to happen:

while(!readyToProcess) {
  // do nothing .. busy waiting wastes processor time.
}

To overcome the coordination issue, and to implement our own constructs, we should use some low-level signals or messaging mechanisms to communicate between threads and coordinate the interaction.

In Java, every object has the wait(), notify(), and notifyAll()methods that provide low-level mechanisms to send thread signals between a group of threads and put a thread in a waiting state until a condition is met.

This mechanism, also known as monitor or guard, is a design pattern commonly used in another languages and it ensures that only one thread can enter a given section of code at any given time with an ability to wait until a condition happens.

This design pattern, in comparison with our previous example, delivers a better and efficient CPU-cycle management while waiting for any particular situation to happen on another thread, and is generally used in situations where we need to coordinate work between different lines of execution.

In the following code example, we are going to explain how to use this construct to create a basic multithreaded Logger with 10 threads that will wait in the monitor section until a message is pushed (condition) by any other thread in the application.

The Logger, which is responsible for logging on to the output, has a queue with a maximum of 20 positions to store the new logging text messages:

public class Logger {
    LinkedList<String> queue = new LinkedList<String>();
    private final int MAX_QUEUE_SIZE = 20;
    private final int MAX_THREAD_COUNT = 10;

In the next code, we will create a Runnable unit of work that runs indefinitely and retrieves a message from the queue to print the message on the Android log.

After that, we will create and start 10 threads that are going to execute the Runnable unit of work task:

public void start() {
    // Creates the Loop as a Runnable
    Runnable task = new Runnable() {
        @Override
        public void run() {
            while(true) {
                String message = pullMessage();
                Log.d(Thread.currentThread().
                         getName(),message);
		     // Do another processing
             }
         }
     };
    // Create a Group of Threads for processing
    for(int i=0; i< MAX_THREAD_COUNT; i++){
         new Thread(task).start();
    }
 }

The pullMessage(), which is a synchorized method, runs a mutual exclusion and puts the thread in the waiting state when it reaches the wait() method. All the created threads will stay in this state until another thread calls notifyAll():

// Pulls a message from the queue
// Only returns when a new message is retrieves
// from the queue.
private synchronized String pullMessage(){
    while (queue.isEmpty()) {
        try {
            wait();
        } catch (InterruptedException e) { ... }
    }
    return queue.pop();
}
// Push a new message to the tail of the queue if
// the queue has available positions
public synchronized void pushMessage(String logMsg) {
    if ( queue.size()< MAX_QUEUE_SIZE ) {
        queue.push(logMsg);      
        notifyAll();
    }
}

When any thread is in the waiting state, it releases the lock temporarily and gives a chance to another thread to enter the mutual exclusion to push new messages or enter into the wait state.

In the following snippet, we will first create the Logger instance and then we will call the start method to start the working threads and we will push 10 messages into a queue of work to be processed.

When the pushMessage()method is invoked, a new logging message is inserted at the end of the queue and notifiyAll() is invoked to notify all the available threads.

As the pullMessage() method runs in a mutual-exclusion (synchronized) zone, only one thread will wake up and return from the pull method. Once pullMessage() returns, the logging message is printed:

Logger logger =new Logger();
logger.start();
for ( int i=0; i< 10 ; i++) {
    ...
    logger.pushMessage(date+" : "+"Log Message #"+i);
}

In the following console output, we have an example of the output that this code will generate and the logging messages are processed by any available threads in an ordered manner:

D/Thread-108(23915): <Date>: Log Message #0
D/Thread-109(23915): ...: Log Message #1
D/Thread-110(23915): ...: Log Message #2
D/Thread-111(23915): ...: Log Message #3

This kind of low-level construct can also be used to control shared resources (polling) to manage background execution (parallelism) and control thread pools.

Concurrent package constructs

Other Java concurrent constructs provided by java.util.concurrent, which are also available on Android SDK are as follows:

  • Lock objects (java.util.concurrent): They implement locking behaviors with a higher level idiom.
  • Executors: These are high-level APIs to launch and manage a group of thread executions (ThreadPool, and so on).
  • Concurrent collections: These are the collections where the methods that change the collection are protected from synchronization issues.
  • Synchronizers: These are high-level constructs that coordinate and control thread execution (Semaphore, Cyclic Barrier, and so on).
  • Atomic variables (java.util.concurrent.atomic): These are classes that provide thread-safe operations on single variables. One example of it is AtomicInteger that could be used in our example to solve the correctness issue.

Some Android-specific constructs use these classes as basic building blocks to implement their concurrent behavior, although they could be used by a developer to build custom concurrent constructs to solve a specific use case.

Executor framework

The Executor framework is another framework available on java.util.concurrent that provides an interface to submit Runnable tasks, decoupling the task submission from the way the task will run:

public interface Executor {
  void execute(Runnable command);
}

Each Executor, which implements the interface that we defined earlier, can manage the asynchronous resources, such as thread creation destruction and caching, and task queueing in a variety of ways to achieve the perfect behavior to a specific use case.

The java.util.concurrent comes with a group of implementations available out of the box that cover most generic use cases, as follows:

  • Executors.newCachedThreadPool(): This is a thread poll that could grow and reuse previously created threads
  • Executors.newFixedThreadPool (nThreads): This is a thread pool with a fixed number of threads and a message queue for store work
  • Executors.newSingleThreadPool(): This is similar to newFixedThreadPool, but with only one working thread

To run a task on Executor, the developer has to invoke execute() by passing Runnable as an argument:

public class MyRunnable implements Runnable {
    public void run() {
        Log.d("Generic", "Running From Thread " +
              Thread.currentThread().getId());   
	 // Your Long Running Computation Task
    }
}
public void startWorking(){
    Executor executor = Executors.newFixedThreadPool(5);
    for ( int i=0; i < 20; i++ ) {
        executor.execute(new MyRunnable());
    }
}

In the preceding code, we created ThreadPool over the factory methods with a fixed number of five threads ready to process work.

After the ExecutorService instance creation, new Runnable tasks are posted for asynchronous processing.

When a new unit of work is submitted, a thread that is free to work is chosen to handle the task; but when all the threads are occupied, Runnable will wait in a local queue until a thread is ready to work.

Android primary building blocks

A typical Android application is composed of the following four main building blocks:

  • android.app.Activity
  • android.app.Service
  • android.content.BroadcastReceiver
  • android.content.ContentProvider

The Activity, Service, and BroadcastReceiver are activated explicitly or implicitly over an asynchronous message called Intent.

Each of these building blocks have their own life cycle, so they could be exposed to different concurrency issues if an asynchronous architecture is used to offload work from the main thread.

Activity concurrent issues

The Activity building block has a tight connection with a presentation layer because it's the entity that manages the UI view over a defined tree of fragments and views that display information and respond to user interactions.

Android applications are typically composed of one or more subclasses of android.app.Activity. An Activity instance has a very well-defined lifecycle that the system manages through the execution of lifecycle method callbacks, all of which are executed on the main thread.

To keep the application responsive and reactive, and the activity transition smooth, the developer should understand the nature of each Activity lifecycle callback.

The most important callbacks on the Activity lifecycle are as follows:

  • onCreate(): At this state, Activity is not visible, but it is here where all the private Activity resources (views and data) are created. The long and intensive computations should be done asynchronously in order to decrease the time when the users don't get a visual feedback during an Activity transition.
  • onStart(): This is the callback called when the UI is visible, but not able to interact on the screen. Any lag here could make the user angry as any touch event generated at this stage is going to be missed by the system.
  • onResume(): This is the callback called when Activity is going to be in the foreground and at an interactable state.
  • onPause(): This is a callback called when Activity is going to the background and is not visible. Computations should end quickly as the next Activity will not resume until this method ends.
  • onStop(): This is a callback called when Activity is no longer visible, but can be restarted.
  • onDestroy(): This is a callback called when the Activity instance is going to be destroyed in the background. All the resources and references that belong to this instance have to be released.

An Activity instance that is completed should be eligible for garbage collection, but background threads that refer to Activity or part of its view hierarchy can prevent garbage collection and create a memory leak.

Similarly, it is easy to waste CPU cycles (and battery life) by continuing to do background work when the result can never be displayed as Activity is completed.

Finally, the Android platform is free at any time to kill processes that are not the user's current focus. This means that if we have long-running operations to complete, we need some way of letting the system know not to kill our process yet.

All of this complicates the do-not-block–the-main-thread rule as we need to worry about canceling background work in a timely fashion or decoupling it from the Activity lifecycle where appropriate.

Manipulating the user interface

The other Android-specific problem lies not in what you can do with the UI thread, but in what you cannot do.

Note

You cannot manipulate the user interface from any thread other than the main thread.

This is because the user interface toolkit is not thread-safe, that is, accessing it from multiple threads may cause correctness problems. In fact, the user interface toolkit protects itself from potential problems by actively denying access to user interface components from threads other than the one that originally created these components.

If the system detects this, it will instantly notify the application by throwing CalledFromWrongThreadException.

The final challenge then lies in safely synchronizing background threads with the main thread so that the main thread can update the user interface with the results of the background work.

If the developer has access to an Activity instance, the runOnUiThread instance method can be used to update the UI from a background thread.

The method accepts a Runnable object like the one used to create an execution task for a thread:

public final void runOnUiThread (Runnable)

In the following example, we are going to use this facility to publish the result from a synonym search that was processed by a background thread.

To accomplish the goal during the OnCreate activity callback, we will set up onClickListener to run searchTask on a created thread:

// Get the Views references
Button search = (Button) findViewById(R.id.searchBut);
final EditText word = (EditText) findViewById(R.id.wordEt);

// When the User clicks on the search button 
// it searches for a synonym
search.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        // Runnable that Searchs for the synonym and
        // and updates the UI.
        Runnable searchTask = new Runnable() {
            @Override
            public void run() {
                // Retrieves the synonym for the word
                String result = searchSynomim(
                   word.getText().toString());
                // Runs the Runnable SetSynonymResult
                // to publish the result on the UI Thread
                runOnUiThread(new SetSynonymResult(result));
            }
        };
        // Executes the search synonym an independent thread
        Thread thread = new Thread(searchTask);
        Thread.start();
    }
});

When the user clicks on the Search button, we will create a Runnable anonymous class that searches for the word typed in R.id.wordEt EditText and starts the thread to execute Runnable.

When the search completes, we will create an instance of Runnable SetSynonymResult to publish the result back on the synonym TextView over the UI thread:

class SetSynonymResult implements Runnable {
    final String synonym;

    SetSynonymResult(String synonym){
      this.synonym = synonym;
    }
    public void run() {
      TextView tv = (TextView)findViewById(R.id.synonymTv);
      tv.setText(this.synonym);
    }
};

This technique is sometime not the most convenient one, especially when we don't have access to an Activity instance; therefore, in the following chapters, we are going to discuss simpler and cleaner techniques to update the UI from a background computing task.

Service concurrent issues

These are the Android entities that run in the background, which usually perform tasks in the name application that does not require any user interaction.

Service, by default, runs in the main thread of the application process. It does not create its own thread, so if your Service is going to do any blocking operation, such as downloading an image, play a video, or access a network API, the user should design a strategy to offload the time of the work from the main thread into another thread.

As Service could have its own concurrent strategy, it should also take into account that, like Activity, it should update the UI over the main thread, so a strategy to post back the results from the background into the main loop is imperative.

In the Android services domain, the way the service is started distinguishes the nature of Service into the following two groups:

  • Started services: This is the service that is started by startService() that can run definitively even if the component that started it was destroyed. A started service does not interact directly with the component that started it.
  • Bound services: This service exists while at least one Android component is bounded to it by calling bindService(). It provides a two-way (client-server) communication channel for communication between components.

Started services issues

When we implement a started service, any application component is able to start it when it invokes the startService(Intent) method. Once the system receives startService(Intent) and the service is not yet started, the system calls onCreate() and then onStartCommand() with the arguments encapsulated on an Intent object. If the Service already exists, only onStartCommand() is invoked.

The callbacks used by a started service are as follows:

// Called every time a component starts the Service
// The service arguments are passed over the intent
int onStartCommand(Intent intent, int flags, int startId)

// Used to initialize your Service resources
void onCreate()

// Used to release your Service resources
void onDestroy()

In the onStartCommand() callback, once a long computing task is required to handle the service request, a handover to the background threads should be explicitly implemented and coordinated in order to avoid an undesired ANR:

int onStartCommand (Intent intent, int flags, int startId){
    // Hand over the request processing to your
    // background tasks
...
}

When the service is done, and it needs to publish results to the UI, a proper technique to communicate with the main thread should be used.

Bound services issues

A bound service generally used when a strong interaction between an Android component and a service is required.

When the service runs on the same process, the interaction between the Android component (client) and the bound service (server) is always provided by a Binder class returned on onBind(). With the Binder instance on hand, the client has access to the service's public methods, so when any component invokes the bound service public methods, the component should be aware of the following:

  • When a long running operation is expected to take place during the method invocation, the invocation must occur in a separate thread
  • If the method is invoked in a separated thread, and the service wants to update the UI, the service must run the update over the main thread:
    public class MyService extends Service {
     
        // Binder given to clients
        private final IBinder mBinder = new MyBinder();
    
         public class MyBinder extends Binder {
             MyService getService() {
                 // Return this instance of MyService
                 // so clients can call public methods
                 return MyService.this;
            }
        }
        @Override
       
        public IBinder onBind(Intent intent) {
            return mBinder;
        }
    
         /** Method for clients */
        public int myPublicMethod() {
          //
        }
    ...

Service in a separate process

When an Android service runs on its own process, it runs in an independent process, with its own address space, making the communication with the main process UI thread harder to implement the following:

<service
  android:name="SynonymService"
  android:process=":my_synonnym_search_proc"
  android:icon="@drawable/icon"
  android:label="@string/service_name"
  >
</service>

To implement a service in a different process, we need to use an inter-process communication (IPC) technique to send messages between your application and the service.

Note

IPC is the activity of sharing data across multiple processes, usually using a well-defined communication protocol. It typically has a process that acts as the client and a process that acts as the server.

There are two technologies available on the Android SDK to implement this, as follows:

  • AIDL (Android Interface Definition Language): This allows you to define an interface over a set of primitive types. It allows you create multithreaded processing services, but it adds other levels of complexity to your implementation. This is only recommended to advanced programmers.
  • Messenger: This is a simple interface that creates a queue of work for you in the service side. This executes all the tasks sequentially on single thread managed by a Handler.

We haven't given more details about these techniques yet; however, an example of this construct is going to be presented later in a more advanced chapter where all the concepts involved are more mature.

Broadcast receiver concurrent issues

This building block is a component that subscribes to system and application events and is notified when these events occur on the system. The broadcast receivers are defined statically in the application manifest or dynamically via the Context.registerReceiver().

The broadcast received is activated though the onReceive()callback and this method runs on the main thread, blocking another Android component from running if we try to execute time-consuming tasks.

Once onReceive() finishes, the system considers the object inactive and can release the resources attached to this instance and recycle the whole object. This behavior has a tremendous impact on what we can do inside, because if we hand over some processing to a concurrent thread, the resources that belong to BroadcastReceiver might be recycled and are no longer available, or in an extreme case, the process could be killed if there were no important components running on it.

Tip

Android version 11 introduced the goAsync()method on the broadcast receiver to keep the broadcast active after returning from the onReceive() function.

Android concurrency constructs

The good news is that the Android platform provides specific constructs to address the concurrency general issues and to solve the specific problems presented by Android.

There are constructs that allow us to defer tasks to run later on the main thread, communicate easily between cooperating threads, and issue work to the managed pools of worker threads and reintegrate the results back in the main thread.

There are solutions to the constraints of the Activity lifecycle, both for medium-term operations that closely involve the user interface and longer-term work that must be completed even if the user leaves the application.

While some of these constructs were only introduced with newer releases of the Android platform, they are available through the support libraries, and with a few exceptions, the examples in this book target devices that run API level 8 (Android 2.2) and higher versions.

Summary

In this chapter, we took a detailed look at the available Android runtimes, Android processes, and thread models.

We then introduced the concurrent issues that we would cope with when we try to implement robust concurrent programs.

Finally, we listed the basic concurrent building blocks available on the SDK to design concurrent programs.

In the next chapter, we'll take a look at some Android-specific low-level building blocks on which the other concurrency mechanisms are built: Handler, Looper, and LooperThread.

Left arrow icon Right arrow icon
Download code icon Download Code

Key benefits

  • Construct scalable and performant applications to take advantage of multi-thread asynchronous techniques
  • Explore the high-level Android asynchronous constructs available on the Android SDK
  • Choose the most appropriate asynchronous technique to implement your next outstanding feature

Description

Asynchronous programming has acquired immense importance in Android programming, especially when we want to make use of the number of independent processing units (cores) available on the most recent Android devices. With this guide in your hands you’ll be able to bring the power of Asynchronous programming to your own projects, and make your Android apps more powerful than ever before! To start with, we will discuss the details of the Android Process model and the Java Low Level Concurrent Framework, delivered by Android SDK. We will also guide you through the high-level Android-specific constructs available on the SDK: Handler, AsyncTask, and Loader. Next, we will discuss the creation of IntentServices, Bound Services and External Services, which can run in the background even when the user is not interacting with it. You will also discover AlarmManager and JobScheduler APIs, which are used to schedule and defer work without sacrificing the battery life. In a more advanced phase, you will create background tasks that are able to execute CPU-intensive tasks in a native code-making use of the Android NDK. You will be then guided through the process of interacting with remote services asynchronously using the HTTP protocol or Google GCM Platform. Using the EventBus library, we will also show how to use the Publish-Subscribe software pattern to simplify communication between the different Android application components by decoupling the event producer from event consumer. Finally, we will introduce RxJava, a popular asynchronous Java framework used to compose work in a concise and reactive way. Asynchronous Android will help you to build well-behaved applications with smooth responsive user interfaces that delight the users with speedy results and data that’s always fresh.

Who is this book for?

This book is for Android developers who want to learn how to build multithreaded and reliable Android applications using high-level and advanced asynchronous techniques and concepts. No prior knowledge of concurrent and asynchronous programming is required. This book will also be great for Java experts who are new to Android. Whether you are a beginner at Android development or a seasoned Android programmer, this book will guide you through the most basic and advanced asynchronous constructs used in Android programming.

What you will learn

  • Get familiar with the android process model and low-level concurrent constructs delivered by the Android SDK
  • Use AsyncTask and loader framework to load data in the background, delivering progress results in the meantime
  • Create services that interact with your activity without compromising the UI rendering
  • Learn the working of Android concurrency on the Native Layer
  • Interact with nearby devices over Bluetooth and WiFi communications channels
  • Create and compose tasks with RxJava to execute complex asynchronous work in a predictable way
  • Get accustomed to the use of the Android Loader construct to deliver up-to-date results

Product Details

Country selected
Publication date, Length, Edition, Language, ISBN-13
Publication date : Jul 29, 2016
Length: 394 pages
Edition : 2nd
Language : English
ISBN-13 : 9781785883248
Vendor :
Google
Category :
Languages :
Tools :

What do you get with a Packt Subscription?

Free for first 7 days. ₹800 p/m after that. Cancel any time!
Product feature icon Unlimited ad-free access to the largest independent learning library in tech. Access this title and thousands more!
Product feature icon 50+ new titles added per month, including many first-to-market concepts and exclusive early access to books as they are being written.
Product feature icon Innovative learning tools, including AI book assistants, code context explainers, and text-to-speech.
Product feature icon Thousands of reference materials covering every tech concept you need to stay up to date.
Subscribe now
View plans & pricing

Product Details

Publication date : Jul 29, 2016
Length: 394 pages
Edition : 2nd
Language : English
ISBN-13 : 9781785883248
Vendor :
Google
Category :
Languages :
Tools :

Packt Subscriptions

See our plans and pricing
Modal Close icon
₹800 billed monthly
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Simple pricing, no contract
₹4500 billed annually
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Choose a DRM-free eBook or Video every month to keep
Feature tick icon PLUS own as many other DRM-free eBooks or Videos as you like for just ₹400 each
Feature tick icon Exclusive print discounts
₹5000 billed in 18 months
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Choose a DRM-free eBook or Video every month to keep
Feature tick icon PLUS own as many other DRM-free eBooks or Videos as you like for just ₹400 each
Feature tick icon Exclusive print discounts

Frequently bought together


Stars icon
Total 11,843.97
Asynchronous Android Programming
₹4096.99
Android High Performance Programming
₹4096.99
Android Design Patterns and Best Practice
₹3649.99
Total 11,843.97 Stars icon
Banner background image

Table of Contents

13 Chapters
1. Asynchronous Programming in Android Chevron down icon Chevron up icon
2. Performing Work with Looper, Handler, and HandlerThread Chevron down icon Chevron up icon
3. Exploring the AsyncTask Chevron down icon Chevron up icon
4. Exploring the Loader Chevron down icon Chevron up icon
5. Interacting with Services Chevron down icon Chevron up icon
6. Scheduling Work with AlarmManager Chevron down icon Chevron up icon
7. Exploring the JobScheduler API Chevron down icon Chevron up icon
8. Interacting with the Network Chevron down icon Chevron up icon
9. Asynchronous Work on the Native Layer Chevron down icon Chevron up icon
10. Network Interactions with GCM Chevron down icon Chevron up icon
11. Exploring Bus-based Communications Chevron down icon Chevron up icon
12. Asynchronous Programing with RxJava Chevron down icon Chevron up icon
Index Chevron down icon Chevron up icon

Customer reviews

Rating distribution
Full star icon Full star icon Full star icon Full star icon Full star icon 5
(3 Ratings)
5 star 100%
4 star 0%
3 star 0%
2 star 0%
1 star 0%
Daryl Ducharme Mar 18, 2017
Full star icon Full star icon Full star icon Full star icon Full star icon 5
Java Concurrency in Practice is a great book for theory on concurrency in a java context but this book gets to some best practices while in Android. There's usually no need to write your own solution when these are already part of the framework and battle tested. The first several sections are very important to understand if you are going to make a robust Android application.
Amazon Verified review Amazon
Amazon Customer Feb 22, 2017
Full star icon Full star icon Full star icon Full star icon Full star icon 5
I amazed with the level of detail found in this practical book. I definitely recommend it to every who is serious about software development.
Amazon Verified review Amazon
Victor Johns Dec 21, 2016
Full star icon Full star icon Full star icon Full star icon Full star icon 5
The great thing about Android is that it is pretty much a self contained ecosystem. The bad thing about Android is that it is really hard to get your head around some of the more detailed nuances of the underlying OS. This book does a great job of delving into how the Android OS features can be taken advantage of, esp by today's multicore processors.Having come from a Linux background, I was finding it hard to get a resource that would allow me to understand exactly how the threading model of Android works. This book proved to be a great help
Amazon Verified review Amazon
Get free access to Packt library with over 7500+ books and video courses for 7 days!
Start Free Trial

FAQs

What is included in a Packt subscription? Chevron down icon Chevron up icon

A subscription provides you with full access to view all Packt and licnesed content online, this includes exclusive access to Early Access titles. Depending on the tier chosen you can also earn credits and discounts to use for owning content

How can I cancel my subscription? Chevron down icon Chevron up icon

To cancel your subscription with us simply go to the account page - found in the top right of the page or at https://subscription.packtpub.com/my-account/subscription - From here you will see the ‘cancel subscription’ button in the grey box with your subscription information in.

What are credits? Chevron down icon Chevron up icon

Credits can be earned from reading 40 section of any title within the payment cycle - a month starting from the day of subscription payment. You also earn a Credit every month if you subscribe to our annual or 18 month plans. Credits can be used to buy books DRM free, the same way that you would pay for a book. Your credits can be found in the subscription homepage - subscription.packtpub.com - clicking on ‘the my’ library dropdown and selecting ‘credits’.

What happens if an Early Access Course is cancelled? Chevron down icon Chevron up icon

Projects are rarely cancelled, but sometimes it's unavoidable. If an Early Access course is cancelled or excessively delayed, you can exchange your purchase for another course. For further details, please contact us here.

Where can I send feedback about an Early Access title? Chevron down icon Chevron up icon

If you have any feedback about the product you're reading, or Early Access in general, then please fill out a contact form here and we'll make sure the feedback gets to the right team. 

Can I download the code files for Early Access titles? Chevron down icon Chevron up icon

We try to ensure that all books in Early Access have code available to use, download, and fork on GitHub. This helps us be more agile in the development of the book, and helps keep the often changing code base of new versions and new technologies as up to date as possible. Unfortunately, however, there will be rare cases when it is not possible for us to have downloadable code samples available until publication.

When we publish the book, the code files will also be available to download from the Packt website.

How accurate is the publication date? Chevron down icon Chevron up icon

The publication date is as accurate as we can be at any point in the project. Unfortunately, delays can happen. Often those delays are out of our control, such as changes to the technology code base or delays in the tech release. We do our best to give you an accurate estimate of the publication date at any given time, and as more chapters are delivered, the more accurate the delivery date will become.

How will I know when new chapters are ready? Chevron down icon Chevron up icon

We'll let you know every time there has been an update to a course that you've bought in Early Access. You'll get an email to let you know there has been a new chapter, or a change to a previous chapter. The new chapters are automatically added to your account, so you can also check back there any time you're ready and download or read them online.

I am a Packt subscriber, do I get Early Access? Chevron down icon Chevron up icon

Yes, all Early Access content is fully available through your subscription. You will need to have a paid for or active trial subscription in order to access all titles.

How is Early Access delivered? Chevron down icon Chevron up icon

Early Access is currently only available as a PDF or through our online reader. As we make changes or add new chapters, the files in your Packt account will be updated so you can download them again or view them online immediately.

How do I buy Early Access content? Chevron down icon Chevron up icon

Early Access is a way of us getting our content to you quicker, but the method of buying the Early Access course is still the same. Just find the course you want to buy, go through the check-out steps, and you’ll get a confirmation email from us with information and a link to the relevant Early Access courses.

What is Early Access? Chevron down icon Chevron up icon

Keeping up to date with the latest technology is difficult; new versions, new frameworks, new techniques. This feature gives you a head-start to our content, as it's being created. With Early Access you'll receive each chapter as it's written, and get regular updates throughout the product's development, as well as the final course as soon as it's ready.We created Early Access as a means of giving you the information you need, as soon as it's available. As we go through the process of developing a course, 99% of it can be ready but we can't publish until that last 1% falls in to place. Early Access helps to unlock the potential of our content early, to help you start your learning when you need it most. You not only get access to every chapter as it's delivered, edited, and updated, but you'll also get the finalized, DRM-free product to download in any format you want when it's published. As a member of Packt, you'll also be eligible for our exclusive offers, including a free course every day, and discounts on new and popular titles.