o7planning

Android AsyncTaskLoader Tutorial with Examples

  1. Android AsyncTaskLoader
  2. Example of AsyncTaskLoader

1. Android AsyncTaskLoader

AsyncTaskLoader is used to perform an asynchronous task in the background of the application, so the user can also interact with the application during that process. As soon as the task is completed, the result will be updated to the interface.
AsyncTaskLoader performs the same functions as AsyncTask; however, AsyncTaskLoader is thought to be better, for the following reasons:
AsyncTask and AsyncTaskLoader have different behaviors when the configuration of the device changes, for example, if the user rotates the device screen, Activity can be destroyed and re-created. In that case:
  • AsyncTask will be re-executed and a new Thread will be created, whereas the old Thread will become separate and uncontrolled.
  • AsyncTaskLoader will be re-used based on the Loader ID that has been registered with the LoaderManager before. As a result, it prevents the duplication of background tasks, and avoids the creation of useless tasks.

2. Example of AsyncTaskLoader

In this example I'm going to use AsyncTaskLoader to load a list of UserAccount and display it in the interface. The data can be retrieved from a URL or from a database. This task is implemented in the background of the application, so during this process, the user still probably interacts with the application.
On Android Studio create a project:
  • File > New > New Project > Empty Activity
    • Name: AsyncTaskLoaderExample
    • Package name: org.o7planning.asynctaskloaderexample
    • Language: Java
The application interface will look like this:
main_activity.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/button_load"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginLeft="16dp"
        android:layout_marginTop="16dp"
        android:text="Load Data"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/button_cancel"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginLeft="16dp"
        android:layout_marginTop="16dp"
        android:text="Cancel"
        app:layout_constraintStart_toEndOf="@+id/button_load"
        app:layout_constraintTop_toTopOf="parent" />

    <ProgressBar
        android:id="@+id/progressBar"
        style="?android:attr/progressBarStyle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginLeft="16dp"
        android:layout_marginTop="16dp"
        app:layout_constraintStart_toEndOf="@+id/button_cancel"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/textView"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginStart="16dp"
        android:layout_marginLeft="16dp"
        android:layout_marginTop="16dp"
        android:layout_marginEnd="16dp"
        android:layout_marginRight="16dp"
        android:layout_marginBottom="16dp"
        android:background="#F3FAED"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/button_load" />

</androidx.constraintlayout.widget.ConstraintLayout>
UserAccountTaskLoader.java
package org.o7planning.asynctaskloaderexample;

import android.content.Context;
import android.os.SystemClock;

import androidx.loader.content.AsyncTaskLoader;

import java.util.ArrayList;
import java.util.List;

public class UserAccountTaskLoader extends AsyncTaskLoader<List<UserAccount>> {

    private String param1;
    private String param2;

    public UserAccountTaskLoader(Context context, String param1, String param2) {
        super(context);
        this.param1 = param1;
        this.param2 = param2;
    }

    @Override
    public List<UserAccount> loadInBackground() {
        // Do something, for example:
        // - Download data from URL and parse it info Java object.
        // - Query data from Database into Java object.

        List<UserAccount> list = new ArrayList<UserAccount>();
        list.add(new UserAccount("tom", "tom@example.com", "Tom"));
        list.add(new UserAccount("jerry", "jerry@example.com", "Jerry"));
        list.add(new UserAccount("donald", "donald@example.com", "Donald"));

        SystemClock.sleep(2000); // 2 Seconds.

        return list;
    }
}
MainActivity.java
package org.o7planning.asynctaskloaderexample;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader;

import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;

import java.util.List;

public class MainActivity extends AppCompatActivity //
        implements LoaderManager.LoaderCallbacks<List<UserAccount>>,
                   Loader.OnLoadCanceledListener<List<UserAccount>> {

    private static final String LOG_TAG = "AndroidExample";
    private static final int LOADER_ID_USERACCOUNT = 10000;

    private Button buttonLoad;
    private Button buttonCancel;
    private ProgressBar progressBar;
    private TextView textView;

    private static final String KEY_PARAM1 = "SomeKey1";
    private static final String KEY_PARAM2 = "SomeKey2";

    private LoaderManager loaderManager;

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

        this.buttonLoad = (Button) this.findViewById(R.id.button_load);
        this.buttonCancel = (Button) this.findViewById(R.id.button_cancel);
        this.progressBar = (ProgressBar) this.findViewById(R.id.progressBar);
        this.textView = (TextView) this.findViewById(R.id.textView);

        // Hide ProgressBar.
        this.progressBar.setVisibility(View.GONE);
        this.buttonCancel.setEnabled(false);

        this.buttonLoad.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                clickButtonLoad();
            }
        });

        this.buttonCancel.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                clickButtonCancel();
            }
        });

        this.loaderManager = LoaderManager.getInstance(this);
    }

    // User click on "Load Data" button.
    private void clickButtonLoad() {
        this.textView.setText("");
        Log.i(LOG_TAG, "loadUserAccount");
        LoaderManager.LoaderCallbacks<List<UserAccount>> loaderCallbacks = this;

        // Arguments:
        Bundle args = new Bundle();
        args.putString(KEY_PARAM1, "Some value1");
        args.putString(KEY_PARAM2, "Some value2");

        // You can pass a null args to a Loader
        Loader<List<UserAccount>> loader =  this.loaderManager.initLoader(LOADER_ID_USERACCOUNT, args, loaderCallbacks);
        try {
            loader.registerOnLoadCanceledListener(this); // Loader.OnLoadCanceledListener
        } catch(IllegalStateException e) {
            // There is already a listener registered
        }
        loader.forceLoad(); // Start Loading..
    }

    // User click on "Cancel" button.
    private void clickButtonCancel() {
        Log.i(LOG_TAG, "cancelLoadUserAccount");
        Loader<List<UserAccount>> loader = this.loaderManager.getLoader(LOADER_ID_USERACCOUNT);
        if(loader != null)  {
            boolean cancelled = loader.cancelLoad();
        }
    }

    // Implements method of LoaderManager.LoaderCallbacks
    @NonNull
    @Override
    public Loader<List<UserAccount>> onCreateLoader(int id, @Nullable Bundle args) {
        Log.i(LOG_TAG, "onCreateLoader");

        this.progressBar.setVisibility(View.VISIBLE); // To show

        if(id == LOADER_ID_USERACCOUNT) {
            this.buttonLoad.setEnabled(false);
            this.buttonCancel.setEnabled(true);
            // Parameters:
            String param1 = (String) args.get(KEY_PARAM1);
            String param2 = (String) args.get(KEY_PARAM2);
            // Return a Loader.
            return new UserAccountTaskLoader(MainActivity.this, param1, param2);
        }
        throw new RuntimeException("TODO..");
    }

    // Implements method of LoaderManager.LoaderCallbacks
    @Override
    public void onLoadFinished(@NonNull Loader<List<UserAccount>> loader, List<UserAccount> data) {
        Log.i(LOG_TAG, "onLoadFinished");

        if(loader.getId() == LOADER_ID_USERACCOUNT) {
            // Destroy a Loader by ID.
            this.loaderManager.destroyLoader(loader.getId());

            StringBuilder sb = new StringBuilder();

            for(UserAccount userAccount: data)  {
                sb.append("Username:").append(userAccount.getUserName()).append("\t") //
                        .append("Email:").append(userAccount.getEmail()).append("\n");
            }
            this.textView.setText(sb.toString());
            // Hide ProgressBar.
            this.progressBar.setVisibility(View.GONE);
            this.buttonLoad.setEnabled(true);
            this.buttonCancel.setEnabled(false);
        }
    }

    // Implements method of LoaderManager.LoaderCallbacks
    @Override
    public void onLoaderReset(@NonNull Loader<List<UserAccount>> loader) {
        Log.i(LOG_TAG, "onLoaderReset");

        this.textView.setText("");
    }

    // Implements method of Loader.OnLoadCanceledListener
    @Override
    public void onLoadCanceled(@NonNull Loader<List<UserAccount>> loader) {
        Log.i(LOG_TAG, "onLoadCanceled");

        if(loader.getId() == LOADER_ID_USERACCOUNT) {
            // Destroy a Loader by ID.
            this.loaderManager.destroyLoader(loader.getId());

            this.progressBar.setVisibility(View.GONE); // To hide
            this.buttonLoad.setEnabled(true);
            this.buttonCancel.setEnabled(false);
        }
    }
}
UserAccount.java
package org.o7planning.asynctaskloaderexample;

public class UserAccount {

    private String userName;
    private String email;
    private String fullName;

    public UserAccount(String userName, String email, String fullName) {
        this.userName = userName;
        this.email = email;
        this.fullName = fullName;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getFullName() {
        return fullName;
    }

    public void setFullName(String fullName) {
        this.fullName = fullName;
    }

}

Android Programming Tutorials

Show More