X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?p=android_pandora.git;a=blobdiff_plain;f=apps%2FAndroidSupportV2%2Fsrc%2Fandroid%2Fsupport%2Fv2%2Fcontent%2FAsyncTaskLoader.java;fp=apps%2FAndroidSupportV2%2Fsrc%2Fandroid%2Fsupport%2Fv2%2Fcontent%2FAsyncTaskLoader.java;h=e2a3330262dcbacc2e999f03012831e5ef89856d;hp=0000000000000000000000000000000000000000;hb=811a5a4a3091f65fef340acafe62d6355b13c44f;hpb=4401ca4aa1b3938939c6c371dfda57aa0652696f diff --git a/apps/AndroidSupportV2/src/android/support/v2/content/AsyncTaskLoader.java b/apps/AndroidSupportV2/src/android/support/v2/content/AsyncTaskLoader.java new file mode 100644 index 0000000..e2a3330 --- /dev/null +++ b/apps/AndroidSupportV2/src/android/support/v2/content/AsyncTaskLoader.java @@ -0,0 +1,287 @@ +/* + * 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.v2.content; + +import android.content.Context; +import android.os.AsyncTask; +import android.os.Handler; +import android.os.SystemClock; +import android.support.v2.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 AsyncTask 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 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.execute((Void[]) null); + // XXX TO DO: use reflection to call this version. + //mTask.executeOnExecutor(AsyncTask.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(); + } + } +}