В прошлых статьях был описан общий подход, который применяется сейчас для запуска в приложении нового потока и обновления в нем пользовательского интерфейса. Рассмотрим другой подход, который представляет класс AsyncTask. Хотя примение AsyncTask в современных приложениях Android устарел. Тем не менее, поскольку он по прежнему широко применяется, также рассмотрим его.
Чтобы использовать AsyncTask, нам надо:
Создать класс, производный от AsyncTask (как правило, для этого создается внутренний класс в activity или во фрагменте)
Переопределить один или несколько методов AsyncTask для выполнения некоторой работы в фоновом режиме
При необходимости создать объект AsyncTask и вызывать его метод execute()
для начала работы
Итак, создадим простейшее приложение с использованием AsyncTask. Определим следующую разметку интерфейса в файле activity_main.xml:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="16dp" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="22sp" android:id="@+id/clicksView" android:text="Clicks: 0"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/clicksBtn" android:text="Click" /> </LinearLayout> <Button android:id="@+id/progressBtn" android:text="Запуск" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:id="@+id/statusView" android:text="Статус" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <ProgressBar android:id="@+id/indicator" style="@android:style/Widget.ProgressBar.Horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:max="100" android:progress="0" /> </LinearLayout>
Здесь определена кнопка для запуска фонового потока, а также текстовое поле и прогрессбар для индикации выполнения задачи. Кроме того, здесь определены дополнительная кнопка, которая увеличивает числов кликов, и текстовое поле, оторое выводит число кликов.
Далее определим в классе MainActivity следующий код:
import android.os.AsyncTask; import android.os.SystemClock; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; public class MainActivity extends AppCompatActivity { int[] integers=null; int clicks = 0; ProgressBar indicatorBar; TextView statusView; TextView clicksView; Button progressBtn; Button clicksBtn; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); integers = new int[100]; for(int i=0;i<100;i++) { integers[i] = i + 1; } indicatorBar = (ProgressBar) findViewById(R.id.indicator); statusView = findViewById(R.id.statusView); progressBtn = findViewById(R.id.progressBtn); progressBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { new ProgressTask().execute(); } }); clicksView = findViewById(R.id.clicksView); clicksBtn = findViewById(R.id.clicksBtn); clicksBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { clicks++; clicksView.setText("Clicks: " + clicks); } }); } class ProgressTask extends AsyncTask<Void, Integer, Void> { @Override protected Void doInBackground(Void... unused) { for (int i = 0; i<integers.length;i++) { publishProgress(i); SystemClock.sleep(400); } return(null); } @Override protected void onProgressUpdate(Integer... items) { indicatorBar.setProgress(items[0]+1); statusView.setText("Статус: " + String.valueOf(items[0]+1)); } @Override protected void onPostExecute(Void unused) { Toast.makeText(getApplicationContext(), "Задача завершена", Toast.LENGTH_SHORT) .show(); } } }
Класс задачи ProgressTask определен как внутренний класс. Он наследуется не просто от AsyncTask, а от его типизированной версии AsyncTask<Void, Integer, Void>
.
Она типизируется тремя типами:
Класс для хранения информации, которая нужна для обработки задачи
Тип объектов, которые используются для индикации процесса выполнения задачи
Тип результата задачи
Эти типы могут быть представлены разными классами. В данном случае сущность задачи будет состоять в переборе массива integers, представляющего набор элементов Integer. И здесь нам не надо передавать в задачу никакой объект, поэтому первый тип идет как Void.
Для индикации перебора используются целые числа, которые показывают, какой объект из массива мы в данный момент перебираем. Поэтому в качестве второго типа используется Integer.
В качестве третьего типа используется опять Void, поскольку в данном случае не надо ничего возвращать из задачи.
AsyncTask содержит четыре метода, которые можно переопределить:
Метод doInBackground(): выполняется в фоновом потоке, должен возвращать определенный результат
Метод onPreExecute(): вызывается из главного потока перед запуском метода doInBackground()
Метод onPostExecute(): выполняется из главного потока после завершения работы метода doInBackground()
Метод onProgressUpdate(): позволяет сигнализировать пользователю о выполнении фонового потока
Так как метод doInBackground() не принимает ничего и не возвращает ничего, то в качестве его параметра используется Void...
- массив Void,
и в качестве возвращаемого типа - тоже Void. Эти типы соответствуют первому и третьему типам в AsyncTask<Void, Integer, Void>
.
Метод doInBackground() перебирает массив и при каждой итерации уведомляет систему с помощью метода publishProgress(item). Так
как в нашем случае для индикации используются целые числа, то параметр item
должен представлять целое число.
Метод onProgressUpdate(Integer... items)
получает переданное выше число и применяет его для настройки текстового поля и прогрессбара.
Метод onPostExecute()
выполняется после завершения задачи и в качестве параметра принимает объект, возвращаемый методом doInBackground() - то есть
в данном случае объект типа Void. Чтобы сигнализировать окончание работы, здесь выводится на экран всплывающее сообщение.
Запустим приложение. Запустим задачу, нажав на кнопку:
При этом пока выполняется задача, мы можем параллельно нажимать на вторую кнопку и увеличивать число кликов, либо выполнять какую-то другую работу в приложении.