public abstract class

AsyncTaskLoader<D>

extends Loader<java.lang.Object>

 java.lang.Object

androidx.loader.content.Loader<java.lang.Object>

↳androidx.loader.content.AsyncTaskLoader<D>

Subclasses:

CursorLoader

Gradle dependencies

compile group: 'androidx.loader', name: 'loader', version: '1.1.0'

  • groupId: androidx.loader
  • artifactId: loader
  • version: 1.1.0

Artifact androidx.loader:loader:1.1.0 it located at Google repository (https://maven.google.com/)

Androidx artifact mapping:

androidx.loader:loader com.android.support:loader

Androidx class mapping:

androidx.loader.content.AsyncTaskLoader android.support.v4.content.AsyncTaskLoader

Overview

Static library support version of the framework's . Used to write apps that run on platforms prior to Android 3.0. When running on Android 3.0 or above, this implementation is still used; it does not try to switch to the framework's implementation. See the framework SDK documentation for a class overview.

Summary

Constructors
publicAsyncTaskLoader(Context context)

Methods
public voidcancelLoadInBackground()

Called on the main thread to abort a load in progress.

public voiddump(java.lang.String prefix, java.io.FileDescriptor fd, java.io.PrintWriter writer, java.lang.String args[])

protected java.util.concurrent.ExecutorgetExecutor()

Returns the java.util.concurrent.Executor to use for this Loader's s.

public booleanisLoadInBackgroundCanceled()

Returns true if the current invocation of AsyncTaskLoader.loadInBackground() is being canceled.

public abstract java.lang.ObjectloadInBackground()

Called on a worker thread to perform the actual load and to return the result of the load operation.

public voidonCanceled(java.lang.Object data)

Called if the task was canceled before it was completed.

protected booleanonCancelLoad()

Subclasses must implement this to take care of requests to Loader.cancelLoad().

protected voidonForceLoad()

Subclasses must implement this to take care of requests to Loader.forceLoad().

protected java.lang.ObjectonLoadInBackground()

Calls AsyncTaskLoader.loadInBackground().

public voidsetUpdateThrottle(long delayMS)

Set amount to throttle updates by.

from Loader<D>abandon, cancelLoad, commitContentChanged, dataToString, deliverCancellation, deliverResult, forceLoad, getContext, getId, isAbandoned, isReset, isStarted, onAbandon, onContentChanged, onReset, onStartLoading, onStopLoading, registerListener, registerOnLoadCanceledListener, reset, rollbackContentChanged, startLoading, stopLoading, takeContentChanged, toString, unregisterListener, unregisterOnLoadCanceledListener
from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait

Constructors

public AsyncTaskLoader(Context context)

Methods

public void setUpdateThrottle(long delayMS)

Set amount to throttle updates by. This is the minimum time from when the last AsyncTaskLoader.loadInBackground() call has completed until a new load is scheduled.

Parameters:

delayMS: Amount of delay, in milliseconds.

protected void onForceLoad()

Subclasses must implement this to take care of requests to Loader.forceLoad(). This will always be called from the process's main thread.

protected boolean onCancelLoad()

Subclasses must implement this to take care of requests to Loader.cancelLoad(). This will always be called from the process's main thread.

Returns:

Returns false if the task could not be canceled, typically because it has already completed normally, or because Loader.startLoading() hasn't been called; returns true otherwise. When true is returned, the task is still running and the Loader.OnLoadCanceledListener will be called when the task completes.

public void onCanceled(java.lang.Object data)

Called if the task was canceled before it was completed. Gives the class a chance to clean up post-cancellation and to properly dispose of the result.

Parameters:

data: The value that was returned by AsyncTaskLoader.loadInBackground(), or null if the task threw OperationCanceledException.

public abstract java.lang.Object loadInBackground()

Called on a worker thread to perform the actual load and to return the result of the load operation. Implementations should not deliver the result directly, but should return them from this method, which will eventually end up calling Loader.deliverResult(D) on the UI thread. If implementations need to process the results on the UI thread they may override Loader.deliverResult(D) and do so there. To support cancellation, this method should periodically check the value of AsyncTaskLoader.isLoadInBackgroundCanceled() and terminate when it returns true. Subclasses may also override AsyncTaskLoader.cancelLoadInBackground() to interrupt the load directly instead of polling AsyncTaskLoader.isLoadInBackgroundCanceled(). When the load is canceled, this method may either return normally or throw OperationCanceledException. In either case, the Loader will call AsyncTaskLoader.onCanceled(D) to perform post-cancellation cleanup and to dispose of the result object, if any.

Returns:

The result of the load operation.

See also: AsyncTaskLoader.isLoadInBackgroundCanceled(), AsyncTaskLoader.cancelLoadInBackground(), AsyncTaskLoader.onCanceled(D)

protected java.lang.Object onLoadInBackground()

Calls AsyncTaskLoader.loadInBackground(). This method is reserved for use by the loader framework. Subclasses should override AsyncTaskLoader.loadInBackground() instead of this method.

Returns:

The result of the load operation.

See also: AsyncTaskLoader.loadInBackground()

public void cancelLoadInBackground()

Called on the main thread to abort a load in progress. Override this method to abort the current invocation of AsyncTaskLoader.loadInBackground() that is running in the background on a worker thread. This method should do nothing if AsyncTaskLoader.loadInBackground() has not started running or if it has already finished.

See also: AsyncTaskLoader.loadInBackground()

public boolean isLoadInBackgroundCanceled()

Returns true if the current invocation of AsyncTaskLoader.loadInBackground() is being canceled.

Returns:

True if the current invocation of AsyncTaskLoader.loadInBackground() is being canceled.

See also: AsyncTaskLoader.loadInBackground()

protected java.util.concurrent.Executor getExecutor()

Returns the java.util.concurrent.Executor to use for this Loader's s. By default will be used. Override this method to return a custom executor. Note that this method will only be called once before this Loader's first is run. It is up to the Loader to shut down the java.util.concurrent.Executor at the appropriate place (e.g. in Loader.onAbandon()) if necessary.

Returns:

the java.util.concurrent.Executor to use for this Loader's s.

public void dump(java.lang.String prefix, java.io.FileDescriptor fd, java.io.PrintWriter writer, java.lang.String args[])

Deprecated: Consider using LoaderManager.enableDebugLogging(boolean) to understand the series of operations performed by LoaderManager.

Source

/*
 * Copyright 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package androidx.loader.content;

import android.content.Context;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.SystemClock;
import android.text.format.DateUtils;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.os.OperationCanceledException;
import androidx.loader.app.LoaderManager;

import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;

/**
 * Static library support version of the framework's {@link android.content.AsyncTaskLoader}.
 * Used to write apps that run on platforms prior to Android 3.0.  When running
 * on Android 3.0 or above, this implementation is still used; it does not try
 * to switch to the framework's implementation.  See the framework SDK
 * documentation for a class overview.
 */
public abstract class AsyncTaskLoader<D> extends Loader<D> {
    private static final String TAG = "AsyncTaskLoader";
    private static final boolean DEBUG = false;

    final class LoadTask extends ModernAsyncTask<D> implements Runnable {
        // Set to true to indicate that the task has been posted to a handler for
        // execution at a later time.  Used to throttle updates.
        boolean waiting;

        /* Runs on a worker thread */
        @Override
        protected D doInBackground() {
            if (DEBUG) Log.v(TAG, this + " >>> doInBackground");
            try {
                D data = AsyncTaskLoader.this.onLoadInBackground();
                if (DEBUG) Log.v(TAG, this + "  <<< doInBackground");
                return data;
            } catch (OperationCanceledException ex) {
                if (!isCancelled()) {
                    // onLoadInBackground threw a canceled exception spuriously.
                    // This is problematic because it means that the LoaderManager did not
                    // cancel the Loader itself and still expects to receive a result.
                    // Additionally, the Loader's own state will not have been updated to
                    // reflect the fact that the task was being canceled.
                    // So we treat this case as an unhandled exception.
                    throw ex;
                }
                if (DEBUG) Log.v(TAG, this + "  <<< doInBackground (was canceled)", ex);
                return null;
            }
        }

        /* Runs on the UI thread */
        @Override
        protected void onPostExecute(D data) {
            if (DEBUG) Log.v(TAG, this + " onPostExecute");
            AsyncTaskLoader.this.dispatchOnLoadComplete(this, data);
        }

        /* Runs on the UI thread */
        @Override
        protected void onCancelled(D data) {
            if (DEBUG) Log.v(TAG, this + " onCancelled");
            AsyncTaskLoader.this.dispatchOnCancelled(this, data);
        }

        /* Runs on the UI thread, when the waiting task is posted to a handler.
         * This method is only executed when task execution was deferred (waiting was true). */
        @Override
        public void run() {
            waiting = false;
            AsyncTaskLoader.this.executePendingTask();
        }
    }

    private Executor mExecutor;

    private volatile LoadTask mTask;
    private volatile LoadTask mCancellingTask;

    private long mUpdateThrottle;
    private long mLastLoadCompleteTime = -10000;
    private Handler mHandler;

    public AsyncTaskLoader(@NonNull Context context) {
        super(context);
    }

    /**
     * Set amount to throttle updates by.  This is the minimum time from
     * when the last {@link #loadInBackground()} call has completed until
     * a new load is scheduled.
     *
     * @param delayMS Amount of delay, in milliseconds.
     */
    public void setUpdateThrottle(long delayMS) {
        mUpdateThrottle = delayMS;
        if (delayMS != 0) {
            mHandler = new Handler();
        }
    }

    @Override
    protected void onForceLoad() {
        super.onForceLoad();
        cancelLoad();
        mTask = new LoadTask();
        if (DEBUG) Log.v(TAG, "Preparing load: mTask=" + mTask);
        executePendingTask();
    }

    @Override
    protected boolean onCancelLoad() {
        if (DEBUG) Log.v(TAG, "onCancelLoad: mTask=" + mTask);
        if (mTask != null) {
            if (!isStarted()) {
                onContentChanged();
            }
            if (mCancellingTask != null) {
                // There was a pending task already waiting for a previous
                // one being canceled; just drop it.
                if (DEBUG) Log.v(TAG,
                        "cancelLoad: still waiting for cancelled task; dropping next");
                if (mTask.waiting) {
                    mTask.waiting = false;
                    mHandler.removeCallbacks(mTask);
                }
                mTask = null;
                return false;
            } else if (mTask.waiting) {
                // There is a task, but it is waiting for the time it should
                // execute.  We can just toss it.
                if (DEBUG) Log.v(TAG, "cancelLoad: task is waiting, dropping it");
                mTask.waiting = false;
                mHandler.removeCallbacks(mTask);
                mTask = null;
                return false;
            } else {
                boolean cancelled = mTask.cancel(false);
                if (DEBUG) Log.v(TAG, "cancelLoad: cancelled=" + cancelled);
                if (cancelled) {
                    mCancellingTask = mTask;
                    cancelLoadInBackground();
                }
                mTask = null;
                return cancelled;
            }
        }
        return false;
    }

    /**
     * Called if the task was canceled before it was completed.  Gives the class a chance
     * to clean up post-cancellation and to properly dispose of the result.
     *
     * @param data The value that was returned by {@link #loadInBackground}, or null
     * if the task threw {@link OperationCanceledException}.
     */
    public void onCanceled(@Nullable D data) {
    }

    @SuppressWarnings("WeakerAccess") /* synthetic access */
    void executePendingTask() {
        if (mCancellingTask == null && mTask != null) {
            if (mTask.waiting) {
                mTask.waiting = false;
                mHandler.removeCallbacks(mTask);
            }
            if (mUpdateThrottle > 0) {
                long now = SystemClock.uptimeMillis();
                if (now < (mLastLoadCompleteTime+mUpdateThrottle)) {
                    // Not yet time to do another load.
                    if (DEBUG) Log.v(TAG, "Waiting until "
                            + (mLastLoadCompleteTime+mUpdateThrottle)
                            + " to execute: " + mTask);
                    mTask.waiting = true;
                    mHandler.postAtTime(mTask, mLastLoadCompleteTime+mUpdateThrottle);
                    return;
                }
            }
            if (DEBUG) Log.v(TAG, "Executing: " + mTask);
            if (mExecutor == null) {
                mExecutor = getExecutor();
            }
            mTask.executeOnExecutor(mExecutor);
        }
    }

    @SuppressWarnings("WeakerAccess") /* synthetic access */
    void dispatchOnCancelled(LoadTask task, D data) {
        onCanceled(data);
        if (mCancellingTask == task) {
            if (DEBUG) Log.v(TAG, "Cancelled task is now canceled!");
            rollbackContentChanged();
            mLastLoadCompleteTime = SystemClock.uptimeMillis();
            mCancellingTask = null;
            if (DEBUG) Log.v(TAG, "Delivering cancellation");
            deliverCancellation();
            executePendingTask();
        }
    }

    @SuppressWarnings("WeakerAccess") /* synthetic access */
    void dispatchOnLoadComplete(LoadTask task, D data) {
        if (mTask != task) {
            if (DEBUG) Log.v(TAG, "Load complete of old task, trying to cancel");
            dispatchOnCancelled(task, data);
        } else {
            if (isAbandoned()) {
                // This cursor has been abandoned; just cancel the new data.
                onCanceled(data);
            } else {
                commitContentChanged();
                mLastLoadCompleteTime = SystemClock.uptimeMillis();
                mTask = null;
                if (DEBUG) Log.v(TAG, "Delivering result");
                deliverResult(data);
            }
        }
    }

    /**
     * Called on a worker thread to perform the actual load and to return
     * the result of the load operation.
     *
     * Implementations should not deliver the result directly, but should return them
     * from this method, which will eventually end up calling {@link #deliverResult} on
     * the UI thread.  If implementations need to process the results on the UI thread
     * they may override {@link #deliverResult} and do so there.
     *
     * To support cancellation, this method should periodically check the value of
     * {@link #isLoadInBackgroundCanceled} and terminate when it returns true.
     * Subclasses may also override {@link #cancelLoadInBackground} to interrupt the load
     * directly instead of polling {@link #isLoadInBackgroundCanceled}.
     *
     * When the load is canceled, this method may either return normally or throw
     * {@link OperationCanceledException}.  In either case, the {@link Loader} will
     * call {@link #onCanceled} to perform post-cancellation cleanup and to dispose of the
     * result object, if any.
     *
     * @return The result of the load operation.
     *
     * @throws OperationCanceledException if the load is canceled during execution.
     *
     * @see #isLoadInBackgroundCanceled
     * @see #cancelLoadInBackground
     * @see #onCanceled
     */
    @Nullable
    public abstract D loadInBackground();

    /**
     * Calls {@link #loadInBackground()}.
     *
     * This method is reserved for use by the loader framework.
     * Subclasses should override {@link #loadInBackground} instead of this method.
     *
     * @return The result of the load operation.
     *
     * @throws OperationCanceledException if the load is canceled during execution.
     *
     * @see #loadInBackground
     */
    @Nullable
    protected D onLoadInBackground() {
        return loadInBackground();
    }

    /**
     * Called on the main thread to abort a load in progress.
     *
     * Override this method to abort the current invocation of {@link #loadInBackground}
     * that is running in the background on a worker thread.
     *
     * This method should do nothing if {@link #loadInBackground} has not started
     * running or if it has already finished.
     *
     * @see #loadInBackground
     */
    public void cancelLoadInBackground() {
    }

    /**
     * Returns true if the current invocation of {@link #loadInBackground} is being canceled.
     *
     * @return True if the current invocation of {@link #loadInBackground} is being canceled.
     *
     * @see #loadInBackground
     */
    public boolean isLoadInBackgroundCanceled() {
        return mCancellingTask != null;
    }

    /**
     * Returns the {@link Executor} to use for this {@link Loader}'s {@link AsyncTask}s.
     * By default {@link AsyncTask#THREAD_POOL_EXECUTOR} will be used.
     *
     * Override this method to return a custom executor. Note that this method will only be called
     * once before this {@link Loader}'s first {@link AsyncTask} is run. It is up to the
     * {@link Loader} to shut down the {@link Executor} at the appropriate place
     * (e.g. in {@link #onAbandon()}) if necessary.
     *
     * @return the {@link Executor} to use for this {@link Loader}'s {@link AsyncTask}s.
     */
    @NonNull
    protected Executor getExecutor() {
        return AsyncTask.THREAD_POOL_EXECUTOR;
    }

    /**
     * @deprecated Consider using {@link LoaderManager#enableDebugLogging(boolean)} to understand
     * the series of operations performed by LoaderManager.
     */
    @SuppressWarnings("deprecation")
    @Override
    @Deprecated
    public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
        super.dump(prefix, fd, writer, args);
        if (mTask != null) {
            writer.print(prefix); writer.print("mTask="); writer.print(mTask);
            writer.print(" waiting="); writer.println(mTask.waiting);
        }
        if (mCancellingTask != null) {
            writer.print(prefix); writer.print("mCancellingTask="); writer.print(mCancellingTask);
            writer.print(" waiting="); writer.println(mCancellingTask.waiting);
        }
        if (mUpdateThrottle != 0) {
            writer.print(prefix); writer.print("mUpdateThrottle=");
            writer.print(DateUtils.formatElapsedTime(
                    TimeUnit.MILLISECONDS.toSeconds(mUpdateThrottle)));
            writer.print(" mLastLoadCompleteTime=");
            writer.print(mLastLoadCompleteTime == -10000
                    ? "--"
                    : "-" + DateUtils.formatElapsedTime(TimeUnit.MILLISECONDS.toSeconds(
                                    SystemClock.uptimeMillis() - mLastLoadCompleteTime)));
            writer.println();
        }
    }
}