ответ

0

Я проверил доступные методы, и, кажется, никто на самом деле не прерывает поток, который работает в фоновом режиме.

Рассматривая код, кажется, что AsyncTask используется под капотом. Я пробовал найти «дыру», которую я мог бы использовать для прерывания asyncTask, но я не мог найти (если только вы не прекрасны с отражением). Я думаю, вы могли бы создать свой собственный загрузчик, который будет иметь эту функциональность.

Я думаю, вы можете изменить код по своему усмотрению. Может быть, добавьте «forceCancel», который будет проверять значение asyncTask, и если он не является нулевым, назовите «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(); 
     } 
    } 
} 
0

Это относительно просто добавить эту функциональность, это 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; 
} 
Смежные вопросы