Android Practice
В Android приложении есть ситуации когда нужно выполнить метод после определенной задержки. Особенно полезно когда пользователь ввел данные и нужно с небольшой задержкой выполнить метод поиска. Теперь давайте рассмотрим как можно вызвать метод задав для него задержку.
Класс Handler и метод postDelayed(Runnable r, long delayMillis позволяют выполнить Runnable в котором будет наш код с определенной задержкой. Сама задержка задается в миллисекундах.
Handler handler = new Handler(); handler.postDelayed(new Runnable() < @Override public void run() < // код выполняющийся после 5 секунд >>, 5000);
Для более удобной записи задержки в пять секунд лучше использовать класс TimeUnit, который позволяет выбрать единицы измерения времени и сделать простым преобразование единиц времени, например преобразовать секунды в миллисекунды. В следующем примере мы рассмотрим как лучше задавать задержку.
Handler handler = new Handler(); handler.postDelayed(new Runnable() < @Override public void run() < // код выполняющийся после 5 секунд >>, TimeUnit.SECONDS.toMillis(5));
Такая запись задержки будет выглядит больше, но гораздо лучше с точки зрения понимания. Можно определить константу если это улучшит читаемость и понимание кода.
Если вы хотите чтобы событие всегда приходило в главной поток Android приложения, то инициализируйте Handler передавая Looper главного потока с помощью статического метода Looper.getMainLooper() в конструктор и тем самым явно задав отправку всех сообщений в главный поток.
Handler handler = new Handler(Looper.getMainLooper());
Теперь все сообщения будут приходит с задержкой только в главный поток.
Обратите внимание в каком потоке создается Handler
По умолчанию сообщения отправляются в поток в котором был создан экземпляр класса Handler. Поэтому нужно следить в каком потоке вы создаете Handler или явно передавать Looper главного потока в конструктор.
Мы рассмотрели способ как можно вызвать метод с задержкой, а также как задать отправление сообщений только в главный поток и почему важно знать в каком потоке создается Handler .
Класс Handler
Класс android.os.Handler является дальнейшим развитием потоков, упрощающий код. Handler может использоваться для планирования выполнения кода в некоторый момент в будущем. Также класс может использоваться для передачи кода, который должен выполняться в другом программном потоке.

Рассмотрим максимально простой пример для знакомства
private Handler mHandler; boolean gameOn; long startTime; @Override protected void onCreate(Bundle savedInstanceState) < super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); startTime = System.currentTimeMillis(); mHandler = new Handler()< public void handleMessage(Message msg)< super.handleMessage(msg); if(gameOn) < long seconds = ((System.currentTimeMillis() - startTime)) / 1000; Log.i("info", "seconds text-warning">Периодическое выполнение задачи При сложных вычислениях может понадобиться очередь Runnable-объектов. Помещая объект в очередь, вы можете задать время его запуска. Для демонстрации использования обработчика потока напишем программу, запускающую фоновый процесс, который будет каждые 200 миллисекунд получать текущее время и обновлять текст. Нам понадобится кнопка Пуск и две текстовые метки, в которых будет отображаться время и количество нажатий кнопки:
package ru.alexanderklimov.handler; import android.os.Bundle; import android.os.Handler; import android.os.SystemClock; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.TextView; public class MainActivity extends AppCompatActivity < // считаем нажатия кнопки private int mButtonPressed = 0; // счетчик времени private long mTime = 0L; private TextView mCounterTextView; private TextView mTimeTextView; // обработчик потока - обновляет сведения о времени // Создаётся в основном UI-потоке private Handler mHandler = new Handler(); @Override protected void onCreate(Bundle savedInstanceState) < super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mTimeTextView = (TextView) findViewById(R.id.textViewTime); mCounterTextView = (TextView) findViewById(R.id.textViewCounter); if (mTime == 0L) < mTime = SystemClock.uptimeMillis(); mHandler.removeCallbacks(timeUpdaterRunnable); // Добавляем Runnable-объект timeUpdaterRunnable в очередь // сообщений, объект должен быть запущен после задержки в 100 мс mHandler.postDelayed(timeUpdaterRunnable, 100); >> // Описание Runnable-объекта private Runnable timeUpdaterRunnable = new Runnable() < public void run() < // вычисляем время final long start = mTime; long millis = SystemClock.uptimeMillis() - start; int second = (int) (millis / 1000); int min = second / 60; second = second % 60; // выводим время mTimeTextView.setText("" + min + ":" + String.format("%02d", second)); // повторяем через каждые 200 миллисекунд mHandler.postDelayed(this, 200); >>; @Override protected void onPause() < // Удаляем Runnable-объект для прекращения задачи mHandler.removeCallbacks(timeUpdaterRunnable); super.onPause(); >@Override protected void onResume() < super.onResume(); // Добавляем Runnable-объект mHandler.postDelayed(timeUpdaterRunnable, 100); >public void onClick(View v) < mCounterTextView.setText("" + ++mButtonPressed); >>
На экране будет отображаться время и одновременно мы можем нажимать на кнопку. Эти действия не мешают друг другу, так как работают в разных потоках.

Кроме метода postDelayed() вы можете использовать метод postAtTime():
postAtTime(Runnable r, long uptimeMillis)
В этом случае объект r добавляется в очередь сообщений, запуск объекта производится во время, заданное вторым параметром.
Самый простой способ помещения объекта в очередь - метод post(), когда указывается только помещаемый объект без указания времени выполнения объекта:
post(Runnable r)
Пример с индикатором прогресса
Всё, что вам нужно - создать экземпляр класса Handler в классе активности. Поток будет работать с объектом Handler, который в свою очередь, будет обновлять шкалу индикатора в основном потоке активности.
Чтобы послать сообщение в объект Handler, сначала необходимо вызвать метод obtainMessage(), чтобы извлечь объект Message из глобального пула сообщений.
Для вставки сообщения в очередь сообщений объекта Handler существует несколько методов:
- sendMessage() — помещает сообщение в очередь немедленно (в конец очереди)
- sendMessageAtFrontofQueue() — помещает сообщение в очередь немедленно и, кроме того, помещает это сообщение впереди очереди (по умолчанию оно ставится в конец очереди), таким образом ваше сообщение берет приоритет над всеми другими
- sendMessageAtTime() — помещает сообщение в очередь в установленное время в миллисекундах
- sendMessageDeiayed() — помещает сообщение в очередь после задержки, выраженной в миллисекундах
Чтобы обрабатывать эти сообщения, для объекта Handler необходимо реализовать метод обратного вызова handleMessage(), который будет вызываться каждым сообщением из очереди сообщения.
Для примера создадим приложение с ProgressBar, который будет отображать ход выполнения длительной задачи (это будет простой цикл с приостановкой потока на 1 секунду в каждой итерации цикла) и обновлять степень завершения этой задачи через объект Handler в классе активности.
package ru.alexanderklimov.progressbar; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.widget.ProgressBar; public class ProgressBarDemoActivity extends Activity < ProgressBar mProgressBar; int mProgress = 0; @Override public void onCreate(Bundle savedInstanceState) < super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mProgressBar = (ProgressBar) findViewById(R.id.progressBarHorizontal); new Thread(myThread).start(); >private Runnable myThread = new Runnable() < @Override public void run() < while (mProgress < 100) < try < myHandler.sendMessage(myHandler.obtainMessage()); Thread.sleep(1000); >catch (Throwable t) < >> > Handler myHandler = new Handler() < @Override public void handleMessage(Message msg) < mProgress++; mProgressBar.setProgress(mProgress); >>; >; >

Splash-screen
Очень часто программисты используют Handler для реализации окна приветствия, которое автоматически закрывается и следом запускается основная активность игры или приложения.
public classSplashActivity extends Activity < @Override protected voidonCreate(Bundle savedInstanceState) < super.onCreate(savedInstanceState); setContentView(R.layout.activity_splash); newHandler().postDelayed(new Runnable() < @Override public void run() < startActivity(newIntent( SplashActivity.this, MainMenuActivity.class)); finish(); >>, 2000); > >
Как сделать не мгновенный переход\перезагрузку Activity?
Я хочу что бы после правильного ответа кнопка светилось зеленым, и переходил к следующему вопросу.
Но у меня это происходит мгновенно и нечего таково не видно, как сделать задержку перехода так чтоб цвет кнопки успел сменится, типа переход через 500мс.
Остановка потока не работает ведь все зависает на Nмс, тестировал эти два способа.
Thread.sleep(500);
и
// Handler из android.os.Handler; Handler handler = new Handler(); handler.postDelayed(new Runnable() < public void run() < /* Code */ >>, 500);
Эти два способа не работают у меня, просто зависает.
- Вопрос задан более двух лет назад
- 94 просмотра
2 комментария
Простой 2 комментария
Как сделать 10-секундную задержку?
Здравствуйте. Необходимо реализовать задержку подобно, как на аукционах, то есть человек загрузил фото, у второго оно загрузилось на смарте, и он нажимает кнопку - проходит 10 секунд, и если из всех, кто нажал под этой фоткой, он первый - ему об этом сообщается. Хочется понимания от более опытных будущих коллег, новичок_
Отслеживать
11 1 1 золотой знак 2 2 серебряных знака 8 8 бронзовых знаков
задан 19 янв 2015 в 4:02
33 1 1 золотой знак 1 1 серебряный знак 3 3 бронзовых знака
А причем тут Android? Это надо на сервере делать.
19 янв 2015 в 5:36
3 ответа 3
Сортировка: Сброс на вариант по умолчанию
Хочу дополнить тему еще одним способов сделать задержку.
Принятый вариант будет вызывать блокировку текущего потока, в нашем случае UI, чтобы этого избежать, используйте этот вариант, он позволяет выполнять требуемое в другом потоке.
Java
Handler handler = new Handler(); handler.postDelayed(new Runnable() < public void run() < /*code*/ >>, 2000); //specify the number of milliseconds
Kotlin
val handler = android.os.Handler() handler.postDelayed(< /*code*/ >, 2000) //specify the number of milliseconds //Или android.os.Handler().postDelayed(< /*code*/ >, 2000)
Coroutine
CoroutineScope(Dispatchers.IO).launch
Можно использовать функцию расширение
object Coroutines < fun io(work: suspend (() ->Unit)): Job = CoroutineScope(Dispatchers.IO).launch < work() >fun ioThenMain(work: suspend (() -> T?), callback: ((T?) -> Unit)): Job = CoroutineScope(Dispatchers.Main).launch < val data = CoroutineScope(Dispatchers.IO).async rt@< return@rt work() >.await() callback(data) > >
Coroutines.ioThenMain(< //выполнение идет в отдельном потоке >) < //Результат приходит в ui поток >