В прошлых статьях был описан общий подход, который применяется сейчас для запуска в приложении нового потока и обновления в нем пользовательского интерфейса. Рассмотрим другой подход, который представляет класс 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. Чтобы сигнализировать окончание работы, здесь выводится на экран всплывающее сообщение.
Запустим приложение. Запустим задачу, нажав на кнопку:
При этом пока выполняется задача, мы можем параллельно нажимать на вторую кнопку и увеличивать число кликов, либо выполнять какую-то другую работу в приложении.