ASYNC TASK CONCEPTS AND IMPLEMENTATION

Nyame Bismark
4 min readOct 29, 2020

AsyncTask was intended to allow proper and easy use of UI thread, however, its integrations with the UI thread causes memory leaks and missed callbacks and inconsistent behaviours on different versions of a platforms from doInBackground method.

AsyncTask is designed ultimately to be a helper around Thread and Handlers and does not consist of the generic threading frameworks. This is helpful when the task is to last for few seconds but if the task is going to last long then it will be advisable to use a worker thread.

AsyncTask has three generic types namely: Params, Progress, Result and it has 4 steps namely: onPreExecute, doInBackground, onProgressUpdate and onPostExecute

AsyncTask normally uses a single thread for execution but it can also support parallel execution if you so wish by using executeOnExecutor and passing ThreadPoolExecutor as a parameter to it. For more information, you can refer to the android documentation page, https://developer.android.com/reference/android/os/AsyncTask

Now we can delve into the implementation part of it;

  1. onPreExecute: This is the part that precedes the doInBackground and it runs on the UI thread, you can use this method to do initializations and preparation before you start with the background work.
@Override
protected void onPostExecute(Void aVoid) {
// this is where you get the result after running in the background
super.onPostExecute(aVoid);
callbacks.onDone();
}

2. doInBackground: This is where the big task happens and it executes the tasks in the background thread. Though the work is done on the background thread but communication between the background thread and main thread can happen through calling publishProgress in this method and it will be received in the onProgressUpdate method hook.

/**
* This method is called to do stuff in the background
*
*
@param strings this the first params passed in the typed params of the class
**/
@Override
protected Void doInBackground(String... strings) {
// do the heavy stuffs in here
callbacks.doExecute();
return null;
}

3. onProgressUpdate: This is where communication from the background thread to the UI thread happens and execution here is on UI thread

@Override
protected void onProgressUpdate(String... values) {
super.onProgressUpdate(values);
//this is where you update the items or ui on the main thread from the background works
}

4. onPostExecute: This executes after the task is executed on the background thread is done or completed and this is also done on the UI thread.

@Override
protected void onPostExecute(Void aVoid) {
// this is where you get the result after running in the background
super.onPostExecute(aVoid);
callbacks.onDone();
}

5. onCancelled: This is executed when cancel is called on the asyncTask object or doInBackground. The cancel takes boolean parameter which when true means it should cancel the task execution uninterruptibly even when running.

@Override
protected void onCancelled() {
super.onCancelled();
// do what you want to do on the main thread when cancelling the async task
}

We could also use the getStatus to retrieve the current status of the asyncTask if it is finished or it is running.

After all these separate codes, let’s try to put things together. I have an illustration here on github; I intended to simulate a heavy work and solve it with AsyncTask implementation. You can click the link to see for yourself. Seeing is believing right!!!. The heavy work is pulling audios from the external storage of the device.

package com.bisapp.threadingexamples.asynctask;

import android.os.AsyncTask;

public class DocsRetrieveAsyncTask extends AsyncTask<String, String, Void> {

private DocsRetrieveCallbacks callbacks;

public DocsRetrieveAsyncTask(DocsRetrieveCallbacks callbacks) {
this.callbacks = callbacks;
}

@Override
protected void onPreExecute() {
super.onPreExecute();
// this is where you prepare your stuffs before trying to execute things in the background
callbacks.onStartRetrieving();
}

/**
* This method is called to do stuff in the background
*
*
@param strings this the first params passed in the typed params of the class
**/
@Override
protected Void doInBackground(String... strings) {
// do the heavy stuffs in here
callbacks.doExecute();
return null;
}

@Override
protected void onPostExecute(Void aVoid) {
// this is where you get the result after running in the background
super.onPostExecute(aVoid);
callbacks.onDone();
}

@Override
protected void onProgressUpdate(String... values) {
super.onProgressUpdate(values);
//this is where you update the items or ui on the main thread from the background works
}

@Override
protected void onCancelled() {
super.onCancelled();
// do what you want to do on the main thread when cancelling the async task
}
}

My callbacks used for the individual steps

package com.bisapp.threadingexamples.asynctask;

public interface DocsRetrieveCallbacks {

void onStartRetrieving();
void doExecute();
void onDone();
}

Calling Class is FirstFragment.java

package com.bisapp.threadingexamples

import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.fragment.app.Fragment
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.bisapp.customrecyclerview.CustomRecyclerView
import com.bisapp.threadingexamples.asynctask.DocsRetrieveAsyncTask
import com.bisapp.threadingexamples.asynctask.DocsRetrieveCallbacks
import com.bisapp.threadingexamples.utils.FetchDocumentsOnStorage
import com.bisapp.threadingexamples.utils.FileData
import kotlinx.android.synthetic.main.fragment_first.*
import java.lang.ref.WeakReference
import java.util.concurrent.Executors

/**
* A simple [Fragment] subclass as the default destination in the navigation.
*/
class FirstFragment : Fragment(), CustomRecyclerView.BindViewsListener,
DocsRetrieveCallbacks,
SwipeRefreshLayout.OnRefreshListener {

lateinit var fetchDocs: FetchDocumentsOnStorage
lateinit var docRetrieveAsynctask: DocsRetrieveAsyncTask
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_first, container, false)
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

//fetch the docs on the device internal storage
asyncTaskCall(false)
swipe_to_refresh.setOnRefreshListener(this)

category_recyclerview.setBindViewsListener(this)
}

private fun asyncTaskCall(parallelExecution: Boolean) {
fetchDocs = FetchDocumentsOnStorage(WeakReference(context))
docRetrieveAsynctask = DocsRetrieveAsyncTask(this)
if (parallelExecution)
docRetrieveAsynctask.executeOnExecutor(Executors.newFixedThreadPool(3))
else
docRetrieveAsynctask.execute()
}

override fun bindViews(view: View?, objects: MutableList<*>?, position: Int) {

val fileData = objects?.get(position) as FileData
val path = view?.findViewById<TextView>(R.id.music)
path?.text = fileData.name
}

override fun onDone() {
swipe_to_refresh.isRefreshing = false
progress_bar.visibility = View.GONE
}

override fun onStartRetrieving() {
swipe_to_refresh.isRefreshing = true
progress_bar.visibility = View.VISIBLE
}

override fun doExecute() {
val files = fetchDocs.listFiles()
if (!files.isEmpty()) {
Handler(Looper.getMainLooper()).post {
category_recyclerview.addModels(files)
}

}
}

override fun onRefresh() {
if (!docRetrieveAsynctask.isCancelled) {
docRetrieveAsynctask.cancel(true)
}

docRetrieveAsynctask = DocsRetrieveAsyncTask(this)
docRetrieveAsynctask.execute()
}
}

In conclusion, we have successfully gone through asynctask implementation and I entreat you to take a look at example where you can use asynctask to solve and it will help with the understanding. For any question, feel free to pin me and I will be glad to help. Access me through my email and thank you for taking this journey with me.

References

android official documentation page

@nyame_bismark on twitter

--

--