Действительно ли AsyncTask ошибочен концептуально или я просто что-то упускаю?

Я исследовал эту проблему в течение нескольких месяцев, придумывал разные решения для нее, что меня не устраивает, поскольку все они представляют собой массовые взломы. Я до сих пор не могу поверить, что класс с ошибками в дизайне вошел во фреймворк, и никто об этом не говорит, так что я полагаю, что я просто что-то упускаю.

Проблема с AsyncTask. Согласно документации это

«позволяет выполнять фоновые операции и публиковать результаты в потоке пользовательского интерфейса без необходимости манипулировать потоками и / или обработчиками».

Затем пример продолжает показывать, как некоторый примерный метод showDialog() вызывается в onPostExecute(). Однако мне это кажется полностью надуманным, потому что для отображения диалогового окна всегда нужна ссылка на действительный Context, а AsyncTask никогда не должен содержать сильную ссылку на объект контекста .

Причина очевидна: что, если будет уничтожено действие, которое запустило задачу? Это может происходить постоянно, например потому что вы перевернули экран. Если задача будет содержать ссылку на контекст, который ее создал, вы не только удерживаете бесполезный объект контекста (окно будет уничтожено, и любое взаимодействие с пользовательским интерфейсом завершится ошибкой с исключением! ), вы даже рискуете создать утечку памяти.

Если моя логика здесь не ошибочна, это означает: onPostExecute() совершенно бесполезен, потому что какой смысл в том, что этот метод запускается в потоке пользовательского интерфейса, если у вас нет доступа к какому-либо контексту? Вы не можете здесь сделать ничего значимого.

Одним из способов обходного решения было бы передавать не экземпляры контекста в AsyncTask, а экземпляр Handler. Это работает: поскольку обработчик слабо связывает контекст и задачу, вы можете обмениваться сообщениями между ними, не рискуя утечкой (верно?). Но это означало бы, что предпосылка AsyncTask, а именно то, что вам не нужно возиться с обработчиками, неверна. Это также похоже на злоупотребление обработчиком, поскольку вы отправляете и получаете сообщения в одном потоке (вы создаете его в потоке пользовательского интерфейса и отправляете через него в onPostExecute (), который также выполняется в потоке пользовательского интерфейса).

В довершение ко всему, даже с этим обходным путем у вас все еще есть проблема, заключающаяся в том, что когда контекст разрушается, у вас нет записей о задачах, которые он запускал. Это означает, что вам придется перезапускать любые задачи при воссоздании контекста, например после смены ориентации экрана. Это медленно и расточительно.

Мое решение (как реализовано в библиотеке Droid-Fu) состоит в том, чтобы поддерживать отображение WeakReferences от имен компонентов до их текущих экземпляров в уникальном объекте приложения. Всякий раз, когда запускается AsyncTask, он записывает контекст вызова в этой карте и при каждом обратном вызове извлекает текущий экземпляр контекста из этого сопоставления. Это гарантирует, что вы никогда не будете ссылаться на устаревший экземпляр контекста и у вас всегда будет доступ к действительному контексту в обратных вызовах, чтобы вы могли выполнять значимую работу пользовательского интерфейса там. Он также не протекает, потому что ссылки слабые и очищаются, когда больше не существует экземпляра данного компонента.

Тем не менее, это сложный обходной путь, требующий подкласса некоторых классов библиотеки Droid-Fu, что делает этот подход довольно навязчивым.

Теперь я просто хочу знать: я просто что-то сильно упускаю или AsyncTask действительно полностью ошибочен? Как ваш опыт работы с этим? Как вы решили эту проблему?

Спасибо за ваш вклад.


person Matthias    schedule 28.07.2010    source источник
comment
Если вам интересно, мы недавно добавили класс в базовую библиотеку ignition под названием IgnitedAsyncTask, который добавляет поддержку типобезопасного доступа к контексту во всех обратных вызовах с использованием шаблона подключения / отключения, описанного Дайанн ниже. Он также позволяет генерировать исключения и обрабатывать их в отдельном обратном вызове. См. github.com / kaeppler / ignition-core / blob / master / src / com / github /   -  person Matthias    schedule 13.10.2011
comment
взгляните на это: gist.github.com/1393552   -  person Matthias    schedule 25.11.2011
comment
Этот вопрос также связан с этим.   -  person Alex Lockwood    schedule 15.01.2014
comment
Я добавляю асинхронные задачи в массив и обязательно закрываю их все в определенный момент.   -  person NightSkyCode    schedule 15.10.2014


Ответы (12)


Как насчет чего-то вроде этого:

class MyActivity extends Activity {
    Worker mWorker;

    static class Worker extends AsyncTask<URL, Integer, Long> {
        MyActivity mActivity;

        Worker(MyActivity activity) {
            mActivity = activity;
        }

        @Override
        protected Long doInBackground(URL... urls) {
            int count = urls.length;
            long totalSize = 0;
            for (int i = 0; i < count; i++) {
                totalSize += Downloader.downloadFile(urls[i]);
                publishProgress((int) ((i / (float) count) * 100));
            }
            return totalSize;
        }

        @Override
        protected void onProgressUpdate(Integer... progress) {
            if (mActivity != null) {
                mActivity.setProgressPercent(progress[0]);
            }
        }

        @Override
        protected void onPostExecute(Long result) {
            if (mActivity != null) {
                mActivity.showDialog("Downloaded " + result + " bytes");
            }
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mWorker = (Worker)getLastNonConfigurationInstance();
        if (mWorker != null) {
            mWorker.mActivity = this;
        }

        ...
    }

    @Override
    public Object onRetainNonConfigurationInstance() {
        return mWorker;
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mWorker != null) {
            mWorker.mActivity = null;
        }
    }

    void startWork() {
        mWorker = new Worker(this);
        mWorker.execute(...);
    }
}
person hackbod    schedule 29.07.2010
comment
Нет - mActivity всегда будет! = Null, поскольку у вас есть сильная ссылка на него, поэтому он не будет иметь права на сборку мусора. Но даже если он не равен нулю, он не обязательно должен быть действительным: если окно было уничтожено, которое было привязано к этому контексту, то этот объект бесполезен, поскольку вы пытаетесь выполнять операции пользовательского интерфейса в окне, которое не работает. больше не существует. - person Matthias; 29.07.2010
comment
Кроме того, именно так вы создаете утечку памяти, если ваша задача не завершается. - person Matthias; 29.07.2010
comment
Да, mActivity будет! = Null, но если нет ссылок на ваш экземпляр Worker, то любые ссылки на этот экземпляр также будут удалены. Если ваша задача выполняется бесконечно, значит, у вас все равно есть утечка памяти (ваша задача), не говоря уже о том, что вы разряжаете аккумулятор телефона. Кроме того, как упоминалось в другом месте, вы можете установить для mActivity значение null в onDestroy. - person EboMike; 29.07.2010
comment
Метод onDestroy () устанавливает для mActivity значение null. Неважно, у кого была ссылка на действие до этого, потому что оно все еще выполняется. И окно активности всегда будет доступно до вызова onDestroy (). Установив здесь значение null, асинхронная задача будет знать, что действие больше недействительно. (И когда конфигурация изменяется, вызывается предыдущее действие onDestroy (), а следующее onCreate () запускается без каких-либо сообщений в основном цикле, обрабатываемых между ними, поэтому AsyncTask никогда не увидит несогласованное состояние.) - person hackbod; 30.07.2010
comment
правда, но это все еще не решает последнюю проблему, о которой я упоминал: представьте, что задача загружает что-то из Интернета. Используя этот подход, если вы перевернете экран 3 раза во время выполнения задачи, он будет перезапускаться при каждом повороте экрана, и каждая задача, кроме последней, отбрасывает свой результат, потому что его ссылка на активность равна нулю. - person Matthias; 30.07.2010
comment
еще один вопрос: документация API для onReatainNonConfigurationInstance () read Эта функция вызывается исключительно для оптимизации, и вы не должны полагаться на ее вызов. Но ваш код зависит от его вызова. Это проблема? В каких случаях этот метод не будет вызван? - person Matthias; 03.08.2010
comment
Нет, он не будет перезапущен. Текущая AsyncTask распространяется на новый экземпляр Activity через onRetainNonConfigurationInstance (). - person hackbod; 04.08.2010
comment
Ваш код не полагается на его вызов - то есть, если приложение полностью перезапущено (процесс завершился), вы запускаетесь без асинхронной задачи. Вы используете его как оптимизацию, чтобы одна и та же асинхронная задача выполнялась во всех экземплярах активности. - person hackbod; 04.08.2010
comment
Ясно спасибо. Что, если мне нужно получить доступ к контексту в doInBackground ()? Должен ли я затем объявить ссылку на активность как изменчивую, поскольку doInBackground () будет вызываться в рабочем потоке? - person Matthias; 04.08.2010
comment
Чтобы получить доступ в фоновом режиме, вам нужно либо установить соответствующую синхронизацию вокруг mActivity и, чтобы работать во время, когда он равен нулю, либо фоновый поток просто возьмет Context.getApplicationContext (), который является единый глобальный экземпляр для приложения. Контекст приложения ограничен в том, что вы можете делать (например, без пользовательского интерфейса, такого как Dialog) и требует некоторой осторожности (зарегистрированные получатели и привязки служб останутся навсегда, если вы не очистите их), но обычно подходит для кода, который не привязаны к контексту конкретного компонента. - person hackbod; 05.08.2010
comment
Это было невероятно полезно, спасибо Дайанн! Я бы хотел, чтобы документация была такой же хорошей. - person Matthias; 05.08.2010
comment
Вы не можете сохранить mWorker в действии, которое будет уничтожено, но вы можете сохранить его в каком-то постоянном хранилище, таком как подкласс Application или какой-то синглтон. Это все еще не идеально, потому что эти решения для постоянного хранения на самом деле не гарантируются. Кроме того, нет гарантии, что onDestroy () будет вызван. - person Edward Falk; 13.11.2012
comment
@EdwardFalk: Да, на самом деле это зависит от характера задачи. Некоторые действия просто не следует реализовывать как AsyncTasks. Вместо этого они должны быть Services. - person Vit Khudenko; 13.11.2012
comment
Я думаю, что более простой подход - просто обернуть задачу внутри фрагмента, а затем вызвать setRetainInstace(true) в методе onCreate этого фрагмента. Это объясняется в следующей статье блог Алекса Локвуда. - person izaban; 23.12.2014
comment
Я обнаружил, что проверка mActivity! = Null здесь избыточна, поскольку ссылка не объявлена ​​WeakReference, поэтому активность здесь не будет GC'ed, пока жив объект Worker. - person AlexVPerl; 09.05.2015
comment
@AlexVPerl: Вы совершенно правы, говоря, что активность здесь не будет собираться, пока рабочий поток жив. Но мы устанавливаем ссылку на действие как null, когда действие уничтожается. После этого, если мы попытаемся отправить прогресс внутри метода onProgressUpdate(), это приведет к вызову операции пользовательского интерфейса для активности, которая была уничтожена. Но я все еще считаю это ошибочным. Как это работает, если действие происходит в фоновом режиме (например, пользователь нажимает кнопку «Домой»)? - person Swapnil; 10.01.2017
comment
У меня вопрос относительно сохранения ссылки Activity внутри AsyncTask. Что делать, если я не храню ссылку, а использую только MyActivity.this внутри AsyncTask, например, MyActivity.this.setProgressPercent(...). Может ли это быть решением против наличия (слабой) ссылки внутри AsyncTask? - person Swapnil; 10.01.2017

Причина очевидна: что, если будет уничтожено действие, которое запустило задачу?

Вручную отключите действие от AsyncTask в onDestroy(). Вручную повторно свяжите новое действие с AsyncTask в onCreate(). Для этого требуется либо статический внутренний класс, либо стандартный класс Java, а также, возможно, 10 строк кода.

person CommonsWare    schedule 29.07.2010
comment
Будьте осторожны со статическими ссылками - я видел, как объекты собирались сборщиком мусора, даже если на них были статические сильные ссылки. Возможно, это побочный эффект загрузчика классов Android или даже ошибка, но статические ссылки не являются безопасным способом обмена состоянием в жизненном цикле активности. Однако объект приложения есть, поэтому я его и использую. - person Matthias; 29.07.2010
comment
@Matthias: я не говорил использовать статические ссылки. Я сказал использовать статический внутренний класс. Существует существенная разница, несмотря на то, что оба имеют статические имена. - person CommonsWare; 29.07.2010
comment
Я бы хотел увидеть это в коде. Может быть, кто-нибудь сможет воплотить идею в код. - person Pentium10; 29.07.2010
comment
@ Pentium10: github.com/commonsguy/cw-android/tree/ master / Rotation / - person CommonsWare; 29.07.2010
comment
Я вижу - ключевым здесь является getLastNonConfigurationInstance (), а не статический внутренний класс. Статический внутренний класс не хранит неявных ссылок на свой внешний класс, поэтому он семантически эквивалентен простому общедоступному классу. Просто предупреждение: onRetainNonConfigurationInstance () НЕ гарантированно будет вызываться, когда действие прерывается (прерывание также может быть телефонным звонком), поэтому вам также придется разделить свою задачу в onSaveInstanceState () для действительно надежного решение. Но все же, хорошая идея. - person Matthias; 29.07.2010
comment
Гм ... onRetainNonConfigurationInstance () всегда вызывается, когда действие находится в процессе уничтожения и воссоздания. В другое время звонить нет смысла. Если происходит переключение на другое действие, текущее действие приостанавливается / останавливается, но не уничтожается, поэтому задача async может продолжать работать и использовать тот же экземпляр действия. Если он завершает работу и, скажем, отображает диалоговое окно, диалоговое окно будет правильно отображаться как часть этого действия и, таким образом, не будет отображаться для пользователя, пока он не вернется к действию. Вы не можете поместить AsyncTask в Bundle. - person hackbod; 30.07.2010
comment
@Matthias: насколько я понимаю, Android может перезапустить процесс, что приведет к сбросу всех статических сильных ссылок. - person 18446744073709551615; 30.01.2013
comment
@CommonsWare Я бы хотел увидеть версию этого действия RotationAsync, которая делает то же самое с CoroutineScope. то есть почти минимальный код для выполнения самых простых задач без необходимости создавать еще один слой для фрагментов или моделей представления. Я собрал здесь вопрос: stackoverflow.com/questions/59881467/ - person Tenfour04; 23.01.2020

Похоже, что AsyncTask немного больше, чем просто концептуально ошибочно. Он также непригоден для использования из-за проблем с совместимостью. Документы Android гласят:

Когда впервые были представлены, AsyncTasks выполнялись последовательно в одном фоновом потоке. Начиная с DONUT, это было изменено на пул потоков, позволяющий нескольким задачам работать параллельно. При запуске HONEYCOMB задачи снова выполняются в одном потоке, чтобы избежать распространенных ошибок приложений, вызванных параллельным выполнением. Если вы действительно хотите параллельное выполнение, вы можете использовать executeOnExecutor(Executor, Params...) версия этого метода с THREAD_POOL_EXECUTOR; однако предупреждения о его использовании см. в комментариях.

И executeOnExecutor(), и THREAD_POOL_EXECUTOR добавлены в API уровня 11 (Android 3.0.x, HONEYCOMB).

Это означает, что если вы создадите два AsyncTask для загрузки двух файлов, вторая загрузка не начнется, пока не завершится первая. Если вы общаетесь через два сервера, и первый сервер не работает, вы не сможете подключиться ко второму, пока не истечет время ожидания подключения к первому. (Если, конечно, вы не используете новые функции API11, но это сделает ваш код несовместимым с 2.x).

И если вы хотите настроить таргетинг как на 2.x, так и на 3.0+, все становится действительно сложно.

Кроме того, в документах говорится:

Внимание! Другая проблема, с которой вы можете столкнуться при использовании рабочего потока, - это неожиданные перезапуски в вашей деятельности из-за изменения конфигурации среды выполнения (например, когда пользователь меняет ориентацию экрана), что может разрушить ваш рабочий поток . Чтобы узнать, как сохранить задачу во время одного из этих перезапусков и как правильно отменить задачу при уничтожении действия, см. Исходный код примера приложения Shelves.

person 18446744073709551615    schedule 30.01.2013

Вероятно, все мы, включая Google, неправильно используем AsyncTask с точки зрения MVC.

Действие - это Контроллер, и контроллер не должен запускать операции, которые могут пережить Представление. То есть AsyncTasks следует использовать из Model, из класса, который не привязан к жизненному циклу Activity - помните, что Activity уничтожаются при ротации. (Что касается View, вы обычно не программируете классы, производные, например, от android.widget.Button, но вы можете. Обычно это единственное, что вы делаете с View это xml.)

Другими словами, неправильно размещать производные от AsyncTask в методах Activity. OTOH, если мы не должны использовать AsyncTasks в Activity, AsyncTask теряет свою привлекательность: раньше его рекламировали как быстрое и простое решение.

person 18446744073709551615    schedule 30.01.2013

Я не уверен, что вы рискуете утечкой памяти со ссылкой на контекст из AsyncTask.

Обычный способ их реализации - создать новый экземпляр AsyncTask в рамках одного из методов Activity. Итак, если действие уничтожено, то после завершения AsyncTask оно не станет недоступным, а затем будет иметь право на сборку мусора? Таким образом, ссылка на действие не будет иметь значения, потому что сама AsyncTask не будет зависать.

person oli    schedule 28.07.2010
comment
правда - но что, если задача блокируется на неопределенный срок? Задачи предназначены для выполнения операций блокировки, возможно, даже тех, которые никогда не завершаются. Вот и утечка памяти. - person Matthias; 29.07.2010
comment
Любой рабочий, который выполняет что-то в бесконечном цикле или что-то, что просто блокируется, например при операции ввода-вывода. - person Matthias; 29.07.2010

Было бы более надежно вести WeekReference о вашей деятельности:

public class WeakReferenceAsyncTaskTestActivity extends Activity {
    private static final int MAX_COUNT = 100;

    private ProgressBar progressBar;

    private AsyncTaskCounter mWorker;

    @SuppressWarnings("deprecation")
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_async_task_test);

        mWorker = (AsyncTaskCounter) getLastNonConfigurationInstance();
        if (mWorker != null) {
            mWorker.mActivity = new WeakReference<WeakReferenceAsyncTaskTestActivity>(this);
        }

        progressBar = (ProgressBar) findViewById(R.id.progressBar1);
        progressBar.setMax(MAX_COUNT);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_async_task_test, menu);
        return true;
    }

    public void onStartButtonClick(View v) {
        startWork();
    }

    @Override
    public Object onRetainNonConfigurationInstance() {
        return mWorker;
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mWorker != null) {
            mWorker.mActivity = null;
        }
    }

    void startWork() {
        mWorker = new AsyncTaskCounter(this);
        mWorker.execute();
    }

    static class AsyncTaskCounter extends AsyncTask<Void, Integer, Void> {
        WeakReference<WeakReferenceAsyncTaskTestActivity> mActivity;

        AsyncTaskCounter(WeakReferenceAsyncTaskTestActivity activity) {
            mActivity = new WeakReference<WeakReferenceAsyncTaskTestActivity>(activity);
        }

        private static final int SLEEP_TIME = 200;

        @Override
        protected Void doInBackground(Void... params) {
            for (int i = 0; i < MAX_COUNT; i++) {
                try {
                    Thread.sleep(SLEEP_TIME);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Log.d(getClass().getSimpleName(), "Progress value is " + i);
                Log.d(getClass().getSimpleName(), "getActivity is " + mActivity);
                Log.d(getClass().getSimpleName(), "this is " + this);

                publishProgress(i);
            }
            return null;
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
            if (mActivity != null) {
                mActivity.get().progressBar.setProgress(values[0]);
            }
        }
    }

}
person Snicolas    schedule 20.09.2012
comment
Это похоже на то, что мы впервые сделали с Droid-Fu. Мы будем хранить карту слабых ссылок на объекты контекста и выполнять поиск в обратных вызовах задач, чтобы получить самую последнюю ссылку (если она доступна) для запуска обратного вызова. Наш подход, однако, означал, что это сопоставление поддерживалось одним объектом, тогда как ваш подход - нет, так что это действительно лучше. - person Matthias; 21.09.2012
comment
Вы смотрели на RoboSpice? github.com/octo-online/robospice. Я считаю, что эта система даже лучше. - person Snicolas; 21.09.2012
comment
Пример кода на первой странице выглядит так, как будто он пропускает контекстную ссылку (внутренний класс хранит неявную ссылку на внешний класс). Не уверен !! - person Matthias; 24.09.2012
comment
@Matthias, вы правы, поэтому я предлагаю статический внутренний класс, который будет содержать WeakReference в Activity. - person Snicolas; 24.09.2012
comment
@Matthias, вы говорили о первой странице RoboSpice? Где утечка? - person Snicolas; 01.10.2012
comment
ВНУТРЕННИЙ КЛАСС активности, уведомляемый в потоке пользовательского интерфейса, если и только если ваша активность активна. Затем код продолжает определять нестатический внутренний класс, который ссылается на $ this. Предполагая, что этому слушателю придется пережить перезапуски активности, он приведет к утечке этой ссылки на внешнюю активность. - person Matthias; 01.10.2012
comment
Спасибо @Matthias, но вам действительно стоит взглянуть на API. Фактически, этот экземпляр внутреннего класса не будет создавать утечки памяти, поскольку он будет собирать мусор, если действие будет уничтожено (действительно, во время onPause). Наша структура обрабатывает все это, и вы можете безопасно использовать внутренний класс, который намного удобнее изменять вашу активность, когда запрос возвращает какой-то результат. Мы действительно справились со всем этим в рамках. - person Snicolas; 01.10.2012
comment
О, значит, слушатель тоже уничтожается, когда уничтожается активность? Понятно, мое плохое - у меня создалось впечатление, что слушатель - это кусок, который переживает перезапуск активности. - person Matthias; 02.10.2012
comment
@Matthias, ничего страшного, я понял, что это не очень понятно в документации, но мы подумали, что это будет довольно сложная тема. Так что мы виноваты в этом :) Но, пожалуйста, Матиас, если вам нравится эта тема, мы будем рады услышать от вас, если вы хотите попробовать RoboSpice. - person Snicolas; 02.10.2012
comment
это, конечно, выглядит интересно, хотя мне было интересно, будет ли лучше в наши дни пройти через классы Loader (например, AsyncTaskLoader), которые уже справляются с этими проблемами. Я еще не работал с ними в производственном приложении, но я считаю, что их предпосылка состоит в том, чтобы избавиться от необходимости самостоятельно обрабатывать присоединение / отсоединение контекста. Однако у них все еще есть другие проблемы, такие как невозможность вызывать исключения в задаче (что является ограничением AsyncTask, а не Loader). - person Matthias; 02.10.2012
comment
@Matthias, я думаю, это уже не по теме. Но загрузчики не обеспечивают кеширование из коробки, как это делаем мы, более того, загрузчики, как правило, более подробны, чем наша библиотека. На самом деле они довольно хорошо обрабатывают курсоры, но для работы в сети лучше подходит другой подход, основанный на кешировании и сервисе. См. neilgoodman.net/2011/12/26/ часть 1 и 2 - person Snicolas; 03.10.2012

Почему бы просто не переопределить метод onPause() в действии-владельце и оттуда отменить AsyncTask?

person Jeff Axelrod    schedule 08.08.2012
comment
это зависит от того, что делает эта задача. если он просто загружает / читает некоторые данные, тогда все будет в порядке. но если он изменяет состояние некоторых данных на удаленном сервере, мы бы предпочли дать задаче возможность работать до конца. - person Vit Khudenko; 09.08.2012
comment
@Arhimed, и я так понимаю, если вы задержите поток пользовательского интерфейса в onPause, что это так же плохо, как и где-то еще? То есть, вы могли получить ANR? - person Jeff Axelrod; 09.08.2012
comment
точно. мы не можем заблокировать поток пользовательского интерфейса (будь то onPause или что-то еще), потому что мы рискуем получить ANR. - person Vit Khudenko; 09.08.2012

Вы абсолютно правы - именно поэтому переход от использования асинхронных задач / загрузчиков в действиях по извлечению данных набирает обороты. Один из новых способов - использовать структуру Volley, которая по существу обеспечивает обратный вызов после данные готовы - гораздо более совместимы с моделью MVC. Volley был популярен в Google I / O 2013. Не знаю, почему больше людей не знают об этом.

person C0D3LIC1OU5    schedule 15.07.2014
comment
спасибо за это ... я собираюсь изучить это ... моя причина, по которой мне не нравится AsyncTask, заключается в том, что он заставляет меня придерживаться одного набора инструкций onPostExecute ... если я не взламываю его, как с использованием интерфейсов или переопределением его каждый раз Мне это надо. - person carinlynchin; 01.02.2017

Лично я просто расширяю Thread и использую интерфейс обратного вызова для обновления пользовательского интерфейса. Я никогда не смог бы заставить AsyncTask работать правильно без проблем с FC. Я также использую неблокирующую очередь для управления пулом выполнения.

person androidworkz    schedule 29.07.2010
comment
Что ж, ваше принудительное закрытие, вероятно, было из-за проблемы, о которой я упоминал: вы пытались сослаться на контекст, который вышел за пределы области видимости (т.е. его окно было уничтожено), что приведет к исключению фреймворка. - person Matthias; 29.07.2010
comment
Нет ... на самом деле это было потому, что очередь отстойная, встроенная в AsyncTask. Я всегда использую getApplicationContext (). У меня нет проблем с AsyncTask, если это всего несколько операций ... но я пишу медиаплеер, который обновляет обложку альбомов в фоновом режиме ... в моем тесте у меня 120 альбомов без обложки ... так что пока мое приложение не закрылось полностью, asynctask выдавал ошибки ... поэтому вместо этого я создал одноэлементный класс с очередью, которая управляет процессами, и до сих пор он отлично работает. - person androidworkz; 29.07.2010

Я думал, что отмена работает, но это не так.

вот они RTFMing об этом:

«« Если задача уже запущена, то параметр mayInterruptIfRunning определяет, должен ли поток, выполняющий эту задачу, быть прерван при попытке остановить задачу ».

Однако это не означает, что поток можно прервать. Это Java, а не AsyncTask ".

http://groups.google.com/group/android-developers/browse_thread/thread/dcadb1bc7705f1bb/add136eb4949359d?show_docid=add136eb4949359d

person nir    schedule 14.12.2010

Вам было бы лучше думать об AsyncTask как о чем-то, что более тесно связано с Activity, Context, ContextWrapper и т. Д. Это удобнее, когда его область действия полностью понятна.

Убедитесь, что у вас есть политика отмены в вашем жизненном цикле, чтобы она в конечном итоге была собрана мусором и больше не содержала ссылки на вашу деятельность, а также могла быть собрана мусором.

Не отменяя AsyncTask при выходе из контекста, вы столкнетесь с утечками памяти и исключениями NullPointerExceptions, если вам просто нужно предоставить обратную связь, например, в виде простого диалогового окна, то одноэлемент вашего контекста приложения поможет избежать проблемы NPE.

AsyncTask не так уж и плох, но определенно происходит много волшебства, которое может привести к некоторым непредвиденным ловушкам.

person jtuchek    schedule 07.09.2014

Что касается «опыта работы с ним»: это возможно, чтобы убить процесс вместе со всеми AsyncTasks, Android воссоздает стек действий, чтобы пользователь ничего не упоминал.

person 18446744073709551615    schedule 30.01.2013