【问题标题】:How to interrupt AsyncTaskLoader's background thread?如何中断 AsyncTaskLoader 后台线程?
【发布时间】:2014-02-15 10:08:49
【问题描述】:

当我调用cancelLoad() 时,有什么方法可以中断AsyncTaskLoaderloadInBackground() 线程吗?我相信AsyncTask.cancel() 会这样做,但任务变量是私有的,无法访问。

【问题讨论】:

    标签: android multithreading android-asynctask android-loader asynctaskloader


    【解决方案1】:

    为此使用stopLoading()abandon()reset()

    文章:https://plus.google.com/117981280628062796190/posts/8b9RmQvxudb

    【讨论】:

    • 它并没有说它会中断线程。
    • @androiddeveloper 看一下图表,X - 它通常用于标记线程的“停止/中断/关闭”。
    • 似乎我已经检查了所有可用的方法(根据我很久以前写的):stackoverflow.com/a/28794783/878126。您能否使用您所说的任何功能来展示一个示例,证明线程被中断并且不会继续它正在做的事情?
    • @androiddeveloper 证明?!它的开源 - 只需导航到代码并检查
    • 我不明白,但是我已经尝试了你提到的所有功能,再次,它们仍然没有中断加载器的线程,就像我 4 年前写的那样.他们不应该这样做,甚至没有写在您显示描述的文档上。我已经更新了我的答案,以包括如何做到这一点的工作示例。
    【解决方案2】:

    添加该功能相对简单,这是它的Gist

    public abstract class InterruptibleAsyncTaskLoader<D> extends AsyncTaskLoader<D> {
        private volatile Thread thread;
    
        public InterruptibleAsyncTaskLoader(Context context) {
            super(context);
        }
    
        public boolean isLoadInBackgroundRunning() {
            return thread != null;
        }
    
        @Override
        public void cancelLoadInBackground() {
            Thread t = thread;
            if (t != null) t.interrupt();
        }
    
        @Override
        public final D loadInBackground() {
            try {
                thread = Thread.currentThread();
                return doLoadInBackground();
            } catch (InterruptedException e) {
                OperationCanceledException oce = new OperationCanceledException(e.toString());
                oce.initCause(e);
                throw oce;
            } finally {
                Thread.interrupted();
                thread = null;
            }
        }
    
        public abstract D doLoadInBackground() throws InterruptedException;
    }
    

    【讨论】:

      【解决方案3】:

      我检查了可用的方法,似乎没有一个真正中断在后台运行的线程。

      查看代码,似乎在后台使用了 AsyncTask。我试图找到一个可以用来中断 asyncTask 的“洞”,但我找不到(除非你对反射很好)。 我认为您可以制作具有此功能的自己的加载程序。

      我认为您可以根据需要修改代码。也许添加“forceCancel”,它将检查 asyncTask 的值,如果它不为 null,则在其上调用“cancel(true)”。这是我看过的代码:

      AsyncTaskLoader.java

      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<D> extends Loader<D> {
          static final String TAG = "AsyncTaskLoader";
          static final boolean DEBUG = false;
      
          final class LoadTask extends ModernAsyncTask<Void, Void, D> 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.
           *
           * <p>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 <tt>false</tt> if the task could not be cancelled,
           *         typically because it has already completed normally, or
           *         because {@link #startLoading()} hasn't been called; returns
           *         <tt>true</tt> 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!");
                  rollbackContentChanged();
                  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 {
                      commitContentChanged();
                      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.
           * <p>
           * Use for testing only.  <b>Never</b> 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();
              }
          }
      }
      

      编辑:3 年后,我决定为此发布我的解决方案:

      /**
       * makes it a bit easier to use AsyncTaskLoader. based on https://github.com/alexjlockwood/AppListLoader
       */
      abstract class AsyncTaskLoaderEx<T>(context: Context) : AsyncTaskLoader<T>(context) {
          @JvmField
          var hasResult = false
          @Suppress("MemberVisibilityCanBePrivate")
          @JvmField
          var isCanceled = false
          var result: T? = null
              private set
          private var _currentThread: Thread? = null
      
          init {
              onContentChanged()
          }
      
          override fun onStartLoading() {
              if (takeContentChanged())
                  forceLoad()
          }
      
          override fun deliverResult(data: T?) {
              result = data
              hasResult = true
              super.deliverResult(data)
          }
      
          override fun onLoadInBackground(): T? {
              _currentThread = Thread.currentThread()
              return super.onLoadInBackground()
          }
      
          open fun interrupt() {
              isCanceled = true
              _currentThread?.interrupt()
          }
      
          override fun onReset() {
              super.onReset()
              onStopLoading()
              if (hasResult) {
                  onReleaseResources(result)
                  result = null
                  hasResult = false
              }
          }
      
          protected open fun onReleaseResources(data: T?) {
              //nothing to do.
          }
      
          companion object {
              private val sCurrentUniqueId = AtomicInteger(0)
              @JvmStatic
              val newUniqueLoaderId: Int
                  get() = sCurrentUniqueId.getAndIncrement()
          }
      }
      

      以及使用示例:

      class MainActivity : AppCompatActivity() {
          companion object {
              val TASK_LOADER_ID = AsyncTaskLoaderEx.newUniqueLoaderId
          }
      
          override fun onCreate(savedInstanceState: Bundle?) {
              super.onCreate(savedInstanceState)
              setContentView(R.layout.activity_main)
              val loaderManager = LoaderManager.getInstance(this)
              loaderManager.initLoader(TASK_LOADER_ID, null, object : LoaderManager.LoaderCallbacks<Boolean?> {
                  override fun onCreateLoader(id: Int, args: Bundle?): Loader<Boolean?> {
                      return ImageLoadingTask(this@MainActivity)
                  }
      
                  override fun onLoadFinished(loader: Loader<Boolean?>, result: Boolean?) {
                      Log.d("AppLog", "finished without being interrupted?:$result")
                      if (result == null)
                          return
                      //TODO use result
      
                  }
      
                  override fun onLoaderReset(loader: Loader<Boolean?>) {
                  }
              })
              val runnable = Runnable {
                  Log.d("AppLog", "trying to stop loader")
                  (loaderManager.getLoader<Loader<Boolean?>>(TASK_LOADER_ID) as AsyncTaskLoaderEx?)?.interrupt()
              }
              val handler = Handler()
              handler.postDelayed(runnable, 2000L)
              Log.d("AppLog", "will try to interrupt in 2 seconds")
              lifecycle.addObserver(object : LifecycleObserver {
                  @Suppress("unused")
                  @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
                  fun onDestroy() {
                      handler.removeCallbacks(runnable)
                  }
              })
          }
      
          private class ImageLoadingTask(context: Context) : AsyncTaskLoaderEx<Boolean?>(context) {
      
              override fun loadInBackground(): Boolean? {
                  try {
                      for (i in 0..10) {
                          Log.d("AppLog", "loadInBackground: $i")
                          Thread.sleep(1000L)
                      }
                      return true
                  } catch (e: InterruptedException) {
                      Log.d("AppLog", "INTERRUPTED!!!")
                  }
                  return false
              }
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2013-10-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-04-25
        • 1970-01-01
        相关资源
        最近更新 更多