Что представляет собой «взаимодействие с пользовательским интерфейсом» для AsyncTask? - PullRequest
1 голос
/ 14 октября 2019

AsyncTask - это стандартный способ асинхронного выполнения длительных операций в фоновом потоке без удержания потока пользовательского интерфейса. Не следует выполнять какие-либо взаимодействия с пользовательским интерфейсом из метода doInBackground ().

Мой вопрос: Какие примеры взаимодействия с пользовательским интерфейсом запрещены? Будет ли это любое из следующего:

  • LayoutInflater.inflate ()
  • View.findViewById ()
  • TextView.setText ()

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

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

Перед изменением нашего кода в надежде найти этоВ редких случаях я хотел узнать, есть ли у кого-нибудь мысли о том, почему наш код «работает» как есть.

Единственный другой полезный кусочек информации - это то, что наш метод doInBackground имеет следующий шаблон кода. :

    protected Boolean doInBackground(Void... voids) {
        if (null == Looper.myLooper()) {
            Looper.prepare();
        }

        publishProgress(0.0);

        // Perform ui/non-ui logic here

        Looper myLooper = Looper.myLooper();
        if (null != myLooper && Looper.getMainLooper() != myLooper) {
            myLooper.quit();
        }
        return true;
    }

Looper необходим для некоторого кода генерации отчетов (опущен), который использует new Handler () для генерации данных. Я не уверен, что создание Looper каким-то образом делает наши пользовательские взаимодействия легальными.

(У меня есть трассировка стека, которая четко показывает, как наша пользовательская активность вызывается из doInBackground, на случай, если вы подумали, что мы можем отключитьсянесколько отдельных тем для обновления нашего интерфейса)

1 Ответ

1 голос
/ 15 октября 2019

AsyncTask не предназначен для действительно длительной работы, должен завершиться в течение нескольких секунд. Это полностью управляемый контекст потока, который не должен иметь свой собственный Looper. Это на самом деле нарушит базовую функциональность AsyncTask и лишит вас других будущих операций AsyncTask, которые вы можете начать. Если у вас есть что-то, что требует Looper, вы должны использовать свой Thread или ThreadPool вместо AsyncTask. Вам также нужно убедиться, что вы сохранили ссылку на ваш AsyncTask, чтобы его можно было соответствующим образом отменить - это источник многих утечек памяти и / или исключений из-за недопустимого состояния при вызове onPostExecute().

Цель метода publishProgress() состоит в том, чтобы дать вашему приложению возможность получать обновления, которые оно может отразить на UX. Вы правы, setText() и т. Д. Не должны выполняться в обратном вызове doInBackground(). Этот обратный вызов выполняется в произвольном контексте потока, в котором вы не контролируете и не можете обновлять пользовательский интерфейс.

Возможно, вы сможете использовать inflateLayout() и findViewById(), но не рекомендуется делать это вне инициализации, поскольку это потенциально дорогостоящие операции. Инфляция должна анализировать двоичный макет и создавать объекты на лету. Поиск по идентификатору обходит всю иерархию представлений, чтобы найти нужный вам компонент. Лучше было бы кэшировать их при создании (для Activity или Fragment) или при создании представления как части адаптера (например, ViewHolder в RecyclerView.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...