В этом уроке:
- работаем с Handler и Runnable
Кроме обработки сообщений, мы можем попросить Handler выполнить кусок кода – Runnable. В прошлых уроках мы работали с сообщениями, которые содержали атрибуты. Мы их обрабатывали в Handler и в зависимости от значений атрибутов выполняли те или иные действия. Runnable же – это кусок кода, который мы пошлем вместо атрибутов сообщения, и он будет выполнен в потоке, с которым работает Handler. Нам уже ничего не надо обрабатывать.
Для отправки кода в работу используется метод post. Как и сообщения, Runnable может быть выполнен с задержкой (postDelayed), и может быть удален из очереди (removeCallbacks). Напишем приложение, которое продемонстрирует все эти возможности.
Создадим проект:
Project name: P0841_HandlerRunnable
Build Target: Android 2.3.3
Application name: HandlerRunnable
Package name: ru.startandroid.develop.p0841handlerrunnable
Create Activity: MainActivity
strings.xml:
<?xml version="1.0" encoding="utf-8"?> <resources> <string name="app_name">HandlerRunnable</string> <string name="info">Подробно</string> </resources>
main.xml:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical"> <ProgressBar android:id="@+id/pbCount" style="?android:attr/progressBarStyleHorizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="20dp"> </ProgressBar> <CheckBox android:id="@+id/chbInfo" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/info"> </CheckBox> <TextView android:id="@+id/tvInfo" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="" android:visibility="gone"> </TextView> </LinearLayout>
ProgressBar, отображающий текущий прогресс. CheckBox, который будет включать отображение доп.информации в TextView.
MainActivity.java:
package ru.startandroid.develop.p0841handlerrunnable; import java.util.concurrent.TimeUnit; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.util.Log; import android.view.View; import android.widget.CheckBox; import android.widget.CompoundButton; import android.widget.CompoundButton.OnCheckedChangeListener; import android.widget.ProgressBar; import android.widget.TextView; public class MainActivity extends Activity { ProgressBar pbCount; TextView tvInfo; CheckBox chbInfo; int cnt; final String LOG_TAG = "myLogs"; final int max = 100; Handler h; /** Called when the activity is first created. */ public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); h = new Handler(); pbCount = (ProgressBar) findViewById(R.id.pbCount); pbCount.setMax(max); pbCount.setProgress(0); tvInfo = (TextView) findViewById(R.id.tvInfo); chbInfo = (CheckBox) findViewById(R.id.chbInfo); chbInfo.setOnCheckedChangeListener(new OnCheckedChangeListener() { public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if (isChecked) { tvInfo.setVisibility(View.VISIBLE); // показываем информацию h.post(showInfo); } else { tvInfo.setVisibility(View.GONE); // отменяем показ информации h.removeCallbacks(showInfo); } } }); Thread t = new Thread(new Runnable() { public void run() { try { for (cnt = 1; cnt < max; cnt++) { TimeUnit.MILLISECONDS.sleep(100); // обновляем ProgressBar h.post(updateProgress); } } catch (InterruptedException e) { e.printStackTrace(); } } }); t.start(); } // обновление ProgressBar Runnable updateProgress = new Runnable() { public void run() { pbCount.setProgress(cnt); } }; // показ информации Runnable showInfo = new Runnable() { public void run() { Log.d(LOG_TAG, "showInfo"); tvInfo.setText("Count = " + cnt); // планирует сам себя через 1000 мсек h.postDelayed(showInfo, 1000); } }; }
В onCreate мы прописываем обработчик для CheckBox. При включении галки отображается TextView и в работу отправляется задание showInfo. При выключении галки – задание showInfo удаляется из очереди.
Далее в новом потоке эмулируем какое-либо действие - запускаем счетчик с паузами. В каждой итерации цикла отправляем в работу задание updateProgress, которое обновляет ProgressBar.
updateProgress – код, который обновляет значение ProgressBar.
showInfo – код, который обновляет TextView и сам себя планирует на выполнение через 1000 мсек. Т.е мы включаем CheckBox, showInfo срабатывает первый раз и само себя планирует на следующий раз. Т.е. этот код лежит в очереди сообщений, обрабатывается и снова кладет себя туда. Так продолжается, пока мы явно его не удалим из очереди (removeCallbacks), выключив CheckBox.
Будем выводить что-нибудь в лог из showInfo, чтобы увидеть, когда он работает, а когда нет.
Все сохраним и запустим приложение. Побежал ProgressBar.
Включим CheckBox.
Появился TextView, который отображает текущее значение счетчика.
В логи при этом добавляется раз в секунду запись:
showInfo
Выключим CheckBox. Текст исчез.
И логи перестали идти. Значит, задание showInfo успешно удалилось из очереди и больше не работает.
Если снова включим CheckBox – оно снова начнет срабатывать и само себя помещать в очередь с задержкой исполнения. Выключаем CheckBox – удаляем его из очереди.
На следующем уроке:
- рассмотрим еще пару способов запуска Runnbale в UI-потоке
Присоединяйтесь к нам в Telegram:
- в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.
- в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Compose, Kotlin, RxJava, Dagger, Тестирование, Performance
- ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня