Правила потоков AsyncTask - PullRequest
0 голосов
/ 16 сентября 2018

AsyncTask имеет 5 правил потоков:

Для правильной работы этого класса необходимо соблюдать несколько правил потоков:

  1. Класс AsyncTask должен быть загружен в поток пользовательского интерфейса.Это делается автоматически с Build.VERSION_CODES.JELLY_BEAN.

  2. Экземпляр задачи должен быть создан в потоке пользовательского интерфейса.

  3. execute (Params...) должен вызываться в потоке пользовательского интерфейса.

  4. Не вызывать onPreExecute (), onPostExecute (Result), doInBackground (Params ...), onProgressUpdate (Progress ...) вручную.

  5. Задача может быть выполнена только один раз (исключение будет сгенерировано при попытке второго выполнения.)

Однако я не очень хорошо понял правила 2 и 3.Я попробовал их на следующем коде:

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

    log(Thread.currentThread().getName());

    new Thread(new Runnable() {
        @Override
        public void run() {
            log(Thread.currentThread().getName());
            Task task = new Task();
            task.execute();
        }
    }).start();
}

public class Task extends AsyncTask<Void, Void, Void> {

    @Override
    protected Void doInBackground(Void... voids) {
        return null;
    }
}

И вот результат:

09-15 21:27:10.179 3310-3310/com.xxx.test D/com.xxx.test.MainActivity: main
09-15 21:27:10.179 3310-3329/com.xxx.test D/com.xxx.test.MainActivity: Thread-264

У меня вопрос: почему я могу создать экземпляр задачи и вызвать execute() метод в другой теме (Тема-264), кроме темы пользовательского интерфейса (основной)?

Я прочитал этот пост , но это не объясняет, почему.Большое спасибо!

Ответы [ 2 ]

0 голосов
/ 16 сентября 2018

Существует 3 метода защиты, которые пользовательский код может ожидать, что они будут запущены из пользовательского интерфейса

  1. onPreExecute()
  2. onProgressUpdate(Progress...)
  3. onPostExecute(Result)

Хотя onPreExecute() будет выполняться в любом потоке, вызывающем execute(), остальные 2 метода будут запускаться Handler.

Класс Handler будет связан ссоздающий его поток, он позволяет коду пользователя публиковать Runable для запуска в этом конкретном потоке.До прихода AsyncTask пользовательский код, который хочет обновить пользовательский интерфейс (который должен обновляться в потоке пользовательского интерфейса), должен сначала создать Handler в потоке пользовательского интерфейса, а затем отправить Runable в этот Handler для выполнения своей задачи в потоке пользовательского интерфейса..

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

Даже если вы можете использовать AsyncTask (кроме onPreExecute()) в рабочем потоке, я рекомендую вам следовать документации и создавать / запускать AsyncTask в потоке пользовательского интерфейса.

0 голосов
/ 16 сентября 2018

С Официальный сайт Android

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

Есть некоторые моменты, которые нам необходимо уточнить.

  1. Наши вычисления будут выполняться в фоновом потоке
  2. Результат вычисления будет опубликован в потоке пользовательского интерфейса
  3. Они не мешают разработчикам создавать или вызывать AsyncTask из потоков, не относящихся к пользовательскому интерфейсу

Шаг 1: Когда вы вызываете

Task task = new Task();

Взгляните на исходный код AsyncTask .

public AsyncTask(@Nullable Looper callbackLooper) {
    mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
        ? getMainHandler()
        : new Handler(callbackLooper);

    mWorker = new WorkerRunnable<Params, Result>() {
        public Result call() throws Exception {
            mTaskInvoked.set(true);
            Result result = null;
            try {
                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //noinspection unchecked
                result = doInBackground(mParams);
                Binder.flushPendingCommands();
            } catch (Throwable tr) {
                mCancelled.set(true);
                throw tr;
            } finally {
                postResult(result);
            }
            return result;
        }
    };

    mFuture = new FutureTask<Result>(mWorker) {
        @Override
        protected void done() {
            try {
                postResultIfNotInvoked(get());
            } catch (InterruptedException e) {
                android.util.Log.w(LOG_TAG, e);
            } catch (ExecutionException e) {
                throw new RuntimeException("An error occurred while executing doInBackground()",
                        e.getCause());
            } catch (CancellationException e) {
                postResultIfNotInvoked(null);
            }
        }
    };
}

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

Шаг 2: Затем вы вызываете

task.execute();

Взгляните на Исходный код AsyncTask .

public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
            Params... params) {
    if (mStatus != Status.PENDING) {
        switch (mStatus) {
            case RUNNING:
                throw new IllegalStateException("Cannot execute task:"
                        + " the task is already running.");
            case FINISHED:
                throw new IllegalStateException("Cannot execute task:"
                        + " the task has already been executed "
                        + "(a task can be executed only once)");
        }
    }

    mStatus = Status.RUNNING;

    onPreExecute();

    mWorker.mParams = params;

    exec.execute(mFuture);

    return this;
}

onPreExecute() будет вызываться в вызывающем потоке, который вызывает AsyncTask (в данном случае ваш анонимный поток).Затем он выполняет Future в своем исполнителе.

После завершения вычисления он вызовет метод postResult.

private Result postResult(Result result) {
    @SuppressWarnings("unchecked")
    Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
            new AsyncTaskResult<Result>(this, result));
    message.sendToTarget();
    return result;
}

private static class InternalHandler extends Handler {
    public InternalHandler(Looper looper) {
        super(looper);
    }

    @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
    @Override
    public void handleMessage(Message msg) {
        AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
        switch (msg.what) {
            case MESSAGE_POST_RESULT:
                // There is only one result
                result.mTask.finish(result.mData[0]);
                break;
            case MESSAGE_POST_PROGRESS:
                result.mTask.onProgressUpdate(result.mData);
                break;
        }
    }
}

private void finish(Result result) {
    if (isCancelled()) {
        onCancelled(result);
    } else {
        onPostExecute(result);
    }
    mStatus = Status.FINISHED;
}

getHandler в этом случае см. Обработчик пользовательского интерфейсапоток, поэтому onPostExecute всегда будет вызываться в потоке пользовательского интерфейса.

Вывод:

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

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