Book Image

Asynchronous Android Programming - Second Edition

By : Steve Liles
Book Image

Asynchronous Android Programming - Second Edition

By: Steve Liles

Overview of this book

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.
Table of Contents (19 chapters)
Asynchronous Android Programming Second Edition
Credits
About the Author
About the Reviewer
www.PacktPub.com
Preface
2
Performing Work with Looper, Handler, and HandlerThread
Index

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() {
          //
        }
    ...