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

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

Go to download

The Support Package includes static "support libraries" that you can add to your Android application in order to use APIs that are either not available for older platform versions or that offer "utility" APIs that aren't a part of the framework APIs.

There is a newer version: r7
Show 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 - 2024 Weber Informatics LLC | Privacy Policy