Android AsyncTaskLoader Tutorial
View more Tutorials:
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.
Android AsyncTaskLoader Javadocs:See more:
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; } }