By Subham Aggarwal | 6/12/2017 | General |Beginners

Guide to Threading in Android

Guide to Threading in Android

Every Android developer, at one instance or another, needs to work with threads in their apps.

When an application is started in Android, it creates the first thread of execution, known as the “main” thread. It is the main thread which dispatches events to the corresponding UI widgets as well as communicating with components from the Android UI toolkit.

To keep an application responsive, it is essential to avoid using the main thread to perform any operation that may end up keeping it blocked.

Network operations, database calls, and loading of certain components, are common examples of blocking operations which should not be done in the main thread. These operations are synchronous, which means that the UI will remain completely blocked until the operation completes and this can take time—enough to annoy a user and crash the app with the horrible ANR error. For this reason, they are usually performed in separate threads, which thereby avoids blocking the UI while they are being performed (i.e., they are performed asynchronously from the UI).

Android provides many ways of creating and managing threads, and many third-party libraries exist that make thread management a lot more pleasant. However, with so many different approaches and options, choosing the right one can be confusing.

Threading in Android

In Android, threading components can be summed up into two basic categories:

  1. Threads that are coupled to an activity/fragment: These threads are binded to the lifecycle of the activity/fragment and are ended as soon as the activity/fragment is destroyed/finished.
  2. Threads that are not coupled to any activity/fragment: These threads can continue to operate beyond the lifetime of the activity/fragment (if any) from which they were generated or spawned.

Threading Components that Attach to an Activity/Fragment

AsyncTask

AsyncTask is the simplest Android component for threading. It’s simple to use and can be good for basic scenarios.

Let’s look at a simple code snippet to show its usage:

public class ExampleActivity extends Activity {

   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       
       new MyTask().execute(url);
   }

   private class MyTask extends AsyncTask<String, Void, String> {

       @Override
       protected String doInBackground(String... params) {
           String url = params[0];
           return doSomeWork(url);
       }

       @Override
       protected void onPostExecute(String result) {
           super.onPostExecute(result);
           // do something with result 
       }
   }
}

AsyncTask, however, falls short if you need your deferred a task to run beyond the lifetime of the activity/fragment. It is worth noting that even something as simple as screen rotation can cause the activity to be destroyed.

Loaders

Loaders are the solution for the problem mentioned above. Loaders can automatically stop when the activity is destroyed, and can also restart themselves after the activity is recreated.

There are mainly two types of loaders: AsyncTaskLoader and CursorLoader. You will learn more about CursorLoader later in this article.

AsyncTaskLoader is similar to AsyncTask, but a bit more complicated.

Let’s look at a simple code snippet to show its usage:

public class ExampleActivity extends Activity{

   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);

       getLoaderManager().initLoader(1, null, new MyLoaderCallbacks());
   }
   
   private class MyLoaderCallbacks implements LoaderManager.LoaderCallbacks {

       @Override
       public Loader onCreateLoader(int id, Bundle args) {
           return new MyLoader(ExampleActivity.this);
       }

       @Override
       public void onLoadFinished(Loader loader, Object data) {

       }

       @Override
       public void onLoaderReset(Loader loader) {

       }
   }

   private class MyLoader extends AsyncTaskLoader {

       public MyLoader(Context context) {
           super(context);
       }

       @Override
       public Object loadInBackground() {
           return someWorkToDo();
       }
       
   }
}

 

Threading Components that Don’t Attach to an Activity/Fragment

Service

Service is a component that is useful for performing long (or potentially long) operations without any UI.

Service runs in the main thread of its hosting process; the service does not create its own thread and does not run in a separate process unless you specify otherwise.

Let’s look at a simple code snippet to show its usage:

public class ExampleService extends Service {

   @Override
   public int onStartCommand(Intent intent, int flags, int startId) {
       doSomeLongProccesingWork();
       stopSelf();

       return START_NOT_STICKY;
   }

   @Nullable
   @Override
   public IBinder onBind(Intent intent) {
       return null;
   }
}

 

With Service, it is your responsibility to stop it when its work is complete by calling either the stopSelf() or the stopService() method.

IntentService

Like Service, IntentService runs on a separate thread, and stops itself automatically after it completes its work.

IntentService is usually used for short tasks that don’t need to be attached to any UI.

Let’s look at a simple code snippet to show its usage:

public class ExampleService extends IntentService {
   
   public ExampleService() {
       super("ExampleService");
   }

   @Override
   protected void onHandleIntent(Intent intent) {
       doSomeShortWork();
   }
}

 

Making a request over a network without requiring a response from the server

Sometimes you may want to send an API request to a server without needing to worry about its response. For example, you may be sending a push registration token to your application’s backend.

Since this involves making a request over the network, you should do it from a thread other than the main thread.

Option 1: AsyncTask or loaders

You can use AsyncTask or loaders for making the call, and it will work.

However, AsyncTask and loaders are both dependent on the lifecycle of the activity. This means you will either need to wait for the call to execute and try to prevent the user from leaving the activity, or hope that it will execute before the activity is destroyed.

Option 2: Service

Service may be a better fit for this use case since it isn’t attached to any activity. It will therefore be able to continue with the network call even after the activity is destroyed. Plus, since the response from the server is not needed, a service wouldn’t be limiting here, either.

However, since a service will begin running on the UI thread, you will still need to manage threading yourself. You will also need to make sure that the service is stopped once the network call is complete.

This would require more effort than should be necessary for such a simple action.

Option 3: IntentService

This, in my opinion, would be the best option.

Since IntentService doesn’t attach to any activity and it runs on a non-UI thread, it serves our needs perfectly here. Moreover, IntentService stops itself automatically, so there is no need to manually manage it, either.

Making a network call, and getting the response from the server

This use case is probably a bit more common. For example, you may want to invoke an API in the back-end and use its response to populate fields on the screen.

Option 1: Service or IntentService

Although a Service or an IntentService fared well for the previous use case, using them here wouldn’t be a good idea. Trying to get data out of a Service or an IntentService into the main UI thread would make things very complex.

Option 2: AsyncTask or loaders

At first blush, AsyncTask or loaders would appear to be the obvious solution here. They are easy to use—simple and straightforward.

However, when using AsyncTask or loaders, you’ll notice that there is a need to write some boilerplate code. Moreover, error handling becomes a major chore with these components. Even with a simple networking call, you need to be aware of potential exceptions, catch them, and act accordingly. This forces us to wrap our response in a custom class containing the data, with possible error information, and a flag indicates if the action was successful or not.

That’s quite a lot of work to do for every single call. Fortunately, there is now a much better and simpler solution available: RxJava.

Option 3: RxJava

You may heard about RxJava, the library developed by Netflix. It’s almost magic in Java.

RxAndroid lets you use RxJava in Android, and makes dealing with asynchronous tasks a breeze. You can learn more about RxJava in Android here.

RxJava provides two components: Observer and Subscriber.

An observer is a component that contains some action. It performs that action and returns the result if it succeeds or an error if it fails.

A subscriber on the other hand is a component that can receive the result (or error) from an observable, by subscribing to it.

With RxJava, we first create an observable:

Observable.create((ObservableOnSubscribe<Data>) e -> {
   Data data = mRestApi.getData();
   e.onNext(data);
})

Once the observable has been created, we can subscribe to it.

With the RxAndroid library, you can control the thread in which you want to execute the action in the observable, and the thread in which we want to get the response (i.e., the result or error).

We can chain on the observable with these two functions:

.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()

 

Schedulers are components that execute the action in a certain thread. AndroidSchedulers.mainThread() is the scheduler associated with the main thread.

Given that our API call is mRestApi.getData() and it returns a Data object, the basic call can look like this:

Let’s look at a simple code snippet to show its usage:

Observable.create((ObservableOnSubscribe<Data>) e -> {
           try { 
                       Data data = mRestApi.getData();
                       e.onNext(data);
           } catch (Exception ex) {
                       e.onError(ex);
           }
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(match -> Log.i("rest api, "success"),
           throwable -> Log.e("rest api, "error: %s" + throwable.getMessage()));

 

Without even going into other benefits of using RxJava, you can already see how RxJava allows us to write more mature code by abstracting away the complexity of threading.

Conclusion

Android provides many ways to handle and manage threads, but none of them are silver bullets.

Choosing the right threading approach, depending on our use case, can make all the difference in making the overall solution easy to implement and understand. The native components fit well for some cases, but not for all. The same applies for glooming third-party solutions.

I hope you will fnd this article useful when working on your next Android project. Share with us your experience threading in Android or any use case where the above solutions work well—or don’t, for that matter—in the comments below.

By Subham Aggarwal | 6/12/2017 | General

{{CommentsModel.TotalCount}} Comments

Your Comment

{{CommentsModel.Message}}

Recent Stories

Top DiscoverSDK Experts

User photo
3355
Ashton Torrence
Web and Windows developer
GUI | Web and 11 more
View Profile
User photo
3220
Mendy Bennett
Experienced with Ad network & Ad servers.
Mobile | Ad Networks and 1 more
View Profile
User photo
3060
Karen Fitzgerald
7 years in Cross-Platform development.
Mobile | Cross Platform Frameworks
View Profile
Show All
X

Compare Products

Select up to three two products to compare by clicking on the compare icon () of each product.

{{compareToolModel.Error}}

Now comparing:

{{product.ProductName | createSubstring:25}} X
Compare Now