All Downloads are FREE. Search and download functionalities are using the official Maven repository.

android.support.v4.content.AsyncTaskLoader Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2011 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 android.support.v4.content;

import android.content.Context;
import android.os.Handler;
import android.os.SystemClock;
import android.support.v4.util.TimeUtils;
import android.util.Log;

import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.concurrent.CountDownLatch;

/**
 * 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 extends Loader {
    static final String TAG = "AsyncTaskLoader";
    static final boolean DEBUG = false;

    final class LoadTask extends ModernAsyncTask implements Runnable {

        D result;
        boolean waiting;

        private CountDownLatch done = new CountDownLatch(1);

        /* Runs on a worker thread */
        @Override
        protected D doInBackground(Void... params) {
            if (DEBUG) Log.v(TAG, this + " >>> doInBackground");
            result = AsyncTaskLoader.this.onLoadInBackground();
            if (DEBUG) Log.v(TAG, this + "  <<< doInBackground");
            return result;
        }

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

        @Override
        protected void onCancelled() {
            if (DEBUG) Log.v(TAG, this + " onCancelled");
            try {
                AsyncTaskLoader.this.dispatchOnCancelled(this, result);
            } finally {
                done.countDown();
            }
        }

        @Override
        public void run() {
            waiting = false;
            AsyncTaskLoader.this.executePendingTask();
        }
    }

    volatile LoadTask mTask;
    volatile LoadTask mCancellingTask;

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

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

    /**
     * Set amount to throttle updates by.  This is the minimum time from
     * when the last {@link #onLoadInBackground()} 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();
    }

    /**
     * Attempt to cancel the current load task. See {@link android.os.AsyncTask#cancel(boolean)}
     * for more info.  Must be called on the main thread of the process.
     *
     * 

Cancelling is not an immediate operation, since the load is performed * in a background thread. If there is currently a load in progress, this * method requests that the load be cancelled, and notes this is the case; * once the background thread has completed its work its remaining state * will be cleared. If another load request comes in during this time, * it will be held until the cancelled load is complete. * * @return Returns false if the task could not be cancelled, * typically because it has already completed normally, or * because {@link #startLoading()} hasn't been called; returns * true otherwise. */ public boolean cancelLoad() { if (DEBUG) Log.v(TAG, "cancelLoad: mTask=" + mTask); if (mTask != null) { 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; } mTask = null; return cancelled; } } return false; } /** * Called if the task was canceled before it was completed. Gives the class a chance * to properly dispose of the result. */ public void onCanceled(D data) { } 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); mTask.executeOnExecutor(ModernAsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null); } } void dispatchOnCancelled(LoadTask task, D data) { onCanceled(data); if (mCancellingTask == task) { if (DEBUG) Log.v(TAG, "Cancelled task is now canceled!"); mLastLoadCompleteTime = SystemClock.uptimeMillis(); mCancellingTask = null; executePendingTask(); } } 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 { mLastLoadCompleteTime = SystemClock.uptimeMillis(); mTask = null; if (DEBUG) Log.v(TAG, "Delivering result"); deliverResult(data); } } } /** */ public abstract D loadInBackground(); /** * Called on a worker thread to perform the actual load. 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. * * @return Implementations must return the result of their load operation. */ protected D onLoadInBackground() { return loadInBackground(); } /** * Locks the current thread until the loader completes the current load * operation. Returns immediately if there is no load operation running. * Should not be called from the UI thread: calling it from the UI * thread would cause a deadlock. *

* Use for testing only. Never call this from a UI thread. * * @hide */ public void waitForLoader() { LoadTask task = mTask; if (task != null) { try { task.done.await(); } catch (InterruptedException e) { // Ignore } } } @Override 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="); TimeUtils.formatDuration(mUpdateThrottle, writer); writer.print(" mLastLoadCompleteTime="); TimeUtils.formatDuration(mLastLoadCompleteTime, SystemClock.uptimeMillis(), writer); writer.println(); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy