Не може да се отмени изпълнението на AsyncTask в AsyncTaskLoader

Искам да отменя изпълнението на AsyncTask (в AsyncTaskLoader), когато потребителят щракне върху началния бутон. Ето какво сътворих досега:

package cz.davidliska.android.loaders;

import java.text.SimpleDateFormat;
import java.util.Date;

import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.AsyncTaskLoader;
import android.support.v4.content.Loader;
import android.util.Log;

public class MainActivity extends FragmentActivity implements LoaderManager.LoaderCallbacks<Date> {
    private static final String TAG = "loader";
    private Date date;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        LoaderManager.enableDebugLogging(true);
        getSupportLoaderManager().initLoader(0, savedInstanceState, this);
    }

    private static class DateLoader extends AsyncTaskLoader<Date> {
        public static final String STATE_DATE_LOADER = "dateloader.state";
        private Date mDate;

        public DateLoader(Context context, Bundle savedInstanceState) {
            super(context);
            if (savedInstanceState != null) {
                long timeStamp = savedInstanceState.getLong(STATE_DATE_LOADER);
                if (timeStamp != 0L) mDate = new Date(timeStamp);
            }
        }

        @Override
        protected void onStartLoading() {
            Log.d(TAG, "Loader.onStartLoading()");
            if (mDate != null) {
                deliverResult(mDate);
            } else {
                forceLoad();
            }
        }

        @Override
        public void deliverResult(Date data) {
            super.deliverResult(data);
            Log.d(TAG, "Loader.deliverResult()");
            mDate = data;
        }

        @Override
        protected void onStopLoading() {
            super.onStopLoading();
            cancelLoad(); // try to cancel AsyncTask
            Log.d(TAG, "Loader.onStopLoading()");
        }


        @Override
        protected void onForceLoad() { // overridden in AsyncTaskLoader
            super.onForceLoad();
            Log.d(TAG, "Loader.onForceLoad()");
        }

        @Override
        protected void onReset() {
            super.onReset();
            mDate = null;
            Log.d(TAG, "Loader.onForceLoad()");
        }

        @Override
        public void onContentChanged() {
            super.onContentChanged();
            Log.d(TAG, "Loader.onContentChanged()");
        }

        @Override
        protected void onAbandon() {
            super.onAbandon();
            Log.d(TAG, "Loader.onContentChanged()");
        }

        @Override
        public Date loadInBackground() { // AsyncTaskLoader only
            Log.d(TAG, "Loader.loadInBackground()");
            try {
                // there will be some HttpClient.execute()
                while(true) {
                    Thread.sleep(100);
                    Log.d(TAG, "Loader.loadInBackground() still running");
                }

            } catch (InterruptedException e) {
                Log.d(TAG, "Loader.loadInBackground() interupted");
            }
            Log.d(TAG, "Loader.loadInBackground() finished");
            return new Date();
        }

        @Override
        public void onCanceled(Date data) { // AsyncTaskLoader only
            super.onCanceled(data);
            Log.d(TAG, "Loader.onCanceled()");
        }

        @Override
        protected Date onLoadInBackground() { // AsyncTaskLoader only
            Log.d(TAG, "Loader.onContentChanged()");
            return super.onLoadInBackground();
        }

        @Override
        public void abandon() {
            Log.d(TAG, "Loader.abandon()");
            super.abandon();
        }

        @Override
        public boolean cancelLoad() {
            Log.d(TAG, "Loader.cancelLoad()");
            return super.cancelLoad();
        }

        @Override
        public void forceLoad() {
            Log.d(TAG, "Loader.forceLoad()");
            super.forceLoad();
        }

        @Override
        public boolean isAbandoned() {
            Log.d(TAG, "Loader.isAbandoned()");
            return super.isAbandoned();
        }

        @Override
        public boolean isReset() {
            Log.d(TAG, "Loader.isReset()");
            return super.isReset();
        }

        @Override
        public boolean isStarted() {
            Log.d(TAG, "Loader.isStarted()");
            return super.isStarted();
        }

        @Override
        public void reset() {
            Log.d(TAG, "Loader.reset()");
            super.reset();
        }

        @Override
        public void stopLoading() {
            Log.d(TAG, "Loader.stopLoading()");
            super.stopLoading();
        }

        @Override
        public boolean takeContentChanged() {
            Log.d(TAG, "Loader.takeContentChanged()");
            return super.takeContentChanged();
        }

    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        if (date != null)
            outState.putLong(DateLoader.STATE_DATE_LOADER, date.getTime());
    }

    public Loader<Date> onCreateLoader(int id, Bundle args) {
        Log.d(TAG, "LoaderCallback.onCreateLoader()");
        if (args != null) Log.d(TAG, "bundle: " + args.getLong(DateLoader.STATE_DATE_LOADER));
        return new DateLoader(this, args);
    }

    public void onLoadFinished(Loader<Date> loader, Date data) {
        Log.d(TAG, "LoaderCallback.onLoadFinished(): " + new SimpleDateFormat("dd/MM/yyyy").format(data));
        date = data;
    }

    public void onLoaderReset(Loader<Date> loader) {
        Log.d(TAG, "LoaderCallback.onLoaderReset()");
    }
}

Така че в onStopLoading() извиквам cancelLoad(), което според мен трябва да отмени текущата задача, но AsyncTask в AsyncTaskLoader все още се изпълнява (докато цикълът в loadInBackground() все още е в ход).

Проблемът може би е в метода cancelLoad() в "java.android.support.v4.content.AsyncTaskLoader.java", където mTask.cancel(boolean) се извиква с "false" в аргументите:

public boolean cancelLoad() {
    ...
    boolean cancelled = mTask.cancel(false);

Има ли някакъв шанс да отмените изпълнението на AsyncTask в AsyncTaskLoader?


person D-Fox    schedule 23.07.2012    source източник
comment
опитайте boolean canceled = mTask.cancel(true);   -  person rajpara    schedule 23.07.2012
comment
за съжаление mTask не се вижда в AsyncTaskLoader   -  person D-Fox    schedule 23.07.2012


Отговори (2)


Ако имате предвид „Има ли някакъв шанс да отмените изпълнението на AsyncTask в android.content.AsyncTaskLoader?“ отговорът е да: просто трябва да добавите някои „точки за анулиране“ във вашия метод loadInBackground и да проверите дали е издадена заявка за анулиране (isLoadInBackgroundCanceled() == true), след което или да върнете, или да хвърлите OperationCanceledException).

Версията на библиотеката за поддръжка на AsyncTaskLoader, която използвате обаче, изглежда не прилага напълно анулирането в момента (поне не в средата на полета и бегло сравнение на рамката и версията за поддръжка на Изглежда Loader подсказва, че анулирането може изобщо да не се поддържа...).

http://developer.android.com/reference/android/content/Loader.html http://developer.android.com/reference/android/support/v4/content/Loader.html

Два начина за облекчаване на проблема ми идват на ум:

  • създайте две реализации на вашия Loader (една, използваща рамката за API ниво 11 и по-висока, и една без функцията за отмяна за по-стари устройства)
  • създайте подклас android.support.v4.content.Loader и сами обработвайте всяка AsyncTask и заявка за анулиране
person Rick77    schedule 16.03.2013

person    schedule
comment
Това ще работи за цикъла while, но не и за дълготрайни операции, като например изпълнение на HTTP заявки. - person D-Fox; 23.07.2012
comment
един ред над цикъла while е коментар //ще има малко HttpClient.execute() - person D-Fox; 23.07.2012
comment
Вероятно искате да направите анулираната си променлива непостоянна или да използвате AtomicBoolean, за да сте сигурни, че няма да се натъкнете на проблеми с видимостта между нишките - person nbarraille; 17.12.2014