All handler apps android

Класс Handler

Класс android.os.Handler является дальнейшим развитием потоков, упрощающий код. Handler может использоваться для планирования выполнения кода в некоторый момент в будущем. Также класс может использоваться для передачи кода, который должен выполняться в другом программном потоке.

Рассмотрим максимально простой пример для знакомства

Запустите пример на эмуляторе и через некоторое время закройте его через кнопку Назад или Домой. При этом смотрите на логи на вкладе Android Monitor. Вы увидите, что приложение по-прежнему печатает текст типа после секундной задержки после запуска.

Разберёмся, что происходит и как следует читать код.

Мы объявили новый объект Handler и булевую переменную, которая по умолчанию имеет значение false — по ней будем отслеживать состояние приложения. В третьей переменной мы сохраним текущее время в момент запуска.

Приложение запустилось, сразу же запоминаем время через currentTimeMillis() — это опорная точка отсчёта.

Основная логика заключена в условии if — тут пишется то, что мы хотим поместить в новый поток. Не страшно, если не понятно, как это всё работает. Просто уясните принцип. Мы иницилизируем объект mHandler и сразу начинаем его настраивать. Переопредляем его главный метод handleMessage() и вызываем метод суперкласса. Это ничем не отличается от вызова onCreate() активности, мы не раз такое делали.

Блок if позволяет управлять кодом для потока. Сам запуск мы сделаем позже. А в самом блоке мы снова получаем текущее время и сравниваем с самой первым временем, полученным во время запуска. Для удобства вычисления идут в секундах. Результат выводится в лог. Метод sendEmptyMessageDelayed() сообщает системе, что мы хотим повторять код в handleMessage() раз в секунду.

После инициализации и настройки mHandler мы присваиваем значение true переменной gameOn, показывая готовность к запуску кода из блока if.

Последняя строка sendEmptyMessage() запускает поток.

Можно использовать более сложные приёмы запуска потока, но пока этого достаточно для понимания.

Периодическое выполнение задачи

При сложных вычислениях может понадобиться очередь Runnable-объектов. Помещая объект в очередь, вы можете задать время его запуска. Для демонстрации использования обработчика потока напишем программу, запускающую фоновый процесс, который будет каждые 200 миллисекунд получать текущее время и обновлять текст. Нам понадобится кнопка Пуск и две текстовые метки, в которых будет отображаться время и количество нажатий кнопки:

На экране будет отображаться время и одновременно мы можем нажимать на кнопку. Эти действия не мешают друг другу, так как работают в разных потоках.

Кроме метода postDelayed() вы можете использовать метод postAtTime():

В этом случае объект r добавляется в очередь сообщений, запуск объекта производится во время, заданное вторым параметром.

Самый простой способ помещения объекта в очередь — метод post(), когда указывается только помещаемый объект без указания времени выполнения объекта:

Пример с индикатором прогресса

Всё, что вам нужно — создать экземпляр класса Handler в классе активности. Поток будет работать с объектом Handler, который в свою очередь, будет обновлять шкалу индикатора в основном потоке активности.

Чтобы послать сообщение в объект Handler, сначала необходимо вызвать метод obtainMessage(), чтобы извлечь объект Message из глобального пула сообщений.

Для вставки сообщения в очередь сообщений объекта Handler существует несколько методов:

  • sendMessage() — помещает сообщение в очередь немедленно (в конец очереди)
  • sendMessageAtFrontofQueue() — помещает сообщение в очередь немедленно и, кроме того, помещает это сообщение впереди очереди (по умолчанию оно ставится в конец очереди), таким образом ваше сообщение берет приоритет над всеми другими
  • sendMessageAtTime() — помещает сообщение в очередь в установленное время в миллисекундах
  • sendMessageDeiayed() — помещает сообщение в очередь после задержки, выраженной в миллисекундах

Чтобы обрабатывать эти сообщения, для объекта Handler необходимо реализовать метод обратного вызова handleMessage(), который будет вызываться каждым сообщением из очереди сообщения.

Для примера создадим приложение с ProgressBar, который будет отображать ход выполнения длительной задачи (это будет простой цикл с приостановкой потока на 1 секунду в каждой итерации цикла) и обновлять степень завершения этой задачи через объект Handler в классе активности.

Splash-screen

Очень часто программисты используют Handler для реализации окна приветствия, которое автоматически закрывается и следом запускается основная активность игры или приложения.

Источник

HandlerThreads and why you should be using them in your Android apps

I first came across HandlerThread while peering through some code for Android. I got curious as to what it did and looked up the code. It is an extension of Thread, which works with a Looper. Meaning, it is meant to handle multiple jobs on the background thread.

Nov 24, 2015 · 5 min read

Читайте также:  Kiosk mode для android

This was my first and last encounter with them for a while. It seemed like a very low level API and something that may not necessarily be meant to be used by app developers. I had rarely (almost never) seen it used in any code sample for how to do perform asynchronous tasks in Android. I did mentioned them as a way to do asynchronous processing in my article 8 ways to do asynchronous processing in Android and counting, even there, I deferred to just regular Theads and ignored how HandlerThreads are used.

It wasn ‘ t till a recent conversation with Colt McAnlis at the BABBQ15 that I heard the mentions of HandlerThreads again. The difference this time was that they were in context of how they process commands from a queue and how this makes for situations where you have to perform a lot of asynchronous tasks. This sounded like just about every application I have written, so why weren’t these mentioned more readily in Android Development literature?

Since the BABBQ, I’ve looked some into HandlerThreads and from what I figure, there are 2 main reasons why developers/educators may be inclined to not mention HandlerThreads.

  1. HandlerThreads run outside of your activity’s lifecycle, so they need to be cleaned up properly or else you will have thread leaks. This is especially dangerous if you are using them for long running processes. I recall Colt mentioning (I may be wrong) that you can have HandlerThreads with a timeout, so if they haven’t processed anything from the queue in a while, they should terminate themselves. I haven’t been able to find any way of doing this, short of extending HandlerThreads and adding the functionality myself. Update : HandlerThread doesn’t have a time out, a ThreadpoolExecutor has a timeout that can be used to wind down unused threads.
  2. There is no convenient mechanism for posting results back to the main thread. This means more boilerplate code.

What is the problem with AsyncTask

AsyncTasks have a single thread dedicated to them, so if you spin off more than one AsyncTask, they aren’t truly asynchronous. This is not necessarily a problem, but it is restrictive if you do want multiple threads. With each AsyncTask that is spun off, a thread is created and destroyed which, if you have a lot of AsyncTasks is a performance issue.

What is wrong with Service

Services are executed on the main thread, so they aren’t really asynchronous. You have to create a separate thread for them to use if you want them to be asynchronous. With both of these you still have a new thread being created and destroyed.

Why not use IntentService

As Ian Lake points out (link to discussion on Google+), IntentService does use HandlerThread internally (link to source) and this does make them a great option to use. He also points out that there is ever only one instance of a Service in existence at any given time and it has only one HandlerThread. This means that if you need more than one thing to happen at the same time, IntentServices may not be a good option. However, looking at the source for the IntentService, it does illustrate that the techniques discussed below are in fact used within the Android SDK itself and as such it is a design pattern you should be familiar with.

How do I use HandlerThreads

There are 2 main ways that I found to use HandlerThreads.

  1. Create a new HandlerThread, create a new Handler using this HandlerThread and post your tasks on this handler.
  2. Extend HandlerThread inside your CustomHandlerThread, create a Handler to process your task. You would take this approach if you know the task you want to perform and just need to pass in parameters. An example would be creating a HandlerThread to download images or perform networking operations.

Implementation of HandlerThread

The implementation is very straight forward.

As I mentioned earlier, we create a new HandlerThread and give it any name. We then start the HandlerThread, get the Looper for the HandlerThread and pass it to a handler.

Then, in order to use it, you simply have to post Runnable to the Handler thread.

If you have a few different tasks you would like to perform on the background thread, it may be a better idea to extend handler and adding handling for different messages. Note, there is still one background thread here. If you want more threads, you’ll have to create more Handlers and HandlerThreads.

IMPORTANT: Remember to call handlerThread.quit() when you are done with the background thread or on your activities onDestroy() method.

As you can see, Option 1 is probably best suited in situations where you don’t have a lot of code. I haven’t covered the scenario of posting back the result to the main thread either.

Posting back the results to the UI thread

There are two approaches that are best used here. You can either make a LocalBroadcast with the result:

The great thing about this approach is that you can simply receive the result on the Main thread without having to do anything extra. The down side is that the method is a lot slower than using a callback.

Читайте также:  Недавно закрытые вкладки яндекс андроид

In order to use a callback, you can explicitly get a Handler on the main thread:

Or, if you are creating the Handler on the main thread, simply calling new Handler() should return a Handler on the main thread.

Implementation of HandlerThreads by creating a custom HandlerThread, is simply a combination of the techniques that I have shown above. There is a great blog post by Nikita that explains how to do this. He also has a great post in which he explain Looper, Handler and HandlerThread and the interaction between them I highly recommend reading this to get a deeper understanding of how Android apps work.

Yay! you made it to the end! We should hang out! feel free to follow me on Medium, LinkedIn, Google+ or Twitter.

Источник

Android- Handlers, Loopers, MessageQueue Basics

May 23, 2020 · 6 min read

Probably the most important APIs of Android suited for Multithreading and offloading the tasks to worker threads- Handlers and Loopers.

The Android architecture has the Main Thread AKA UI thread, which updates the UI after every 16ms frame. Failure to update within this window will reflect as the “lag”, and even worse if it fails for 5secs, then the “ Application not responding” shown to the user- marking the app crash. To avoid these types of issues, please resort to offloading the heavy tasks to worker threads.

To explain: th e Android architecture has one main/UI thread which should ideally only be responsible for updating the UI, and other tasks should be done on separate threads(called worker threads). These worker threads upon completion should send the result back to the main thread and the main thread can then use it to update the UI, etc.
There are some Android APIs to do so like Async tasks, Services(Intent services actually), Executors, etc, etc.
But there are even cooler APIs available and are the core of all the above-mentioned APIs- Handlers and Loopers. Even Async and Intent Services rely heavily on them. Let’s see how they work.

Handlers and Loopers Architecture:

For the thread to receive any message from other threads, the most common pattern we follow is that it should read from the common data structure(usually Queues), to which other threads can write. The same is being followed here.

Handler Threads:

These are the threads that can receive messages from any other thread. Main/UI thread is also a Handler thread.
As explained above, such a thread must need a Message Queue(data structure in which other threads can write to), and a mechanism to read this queue infinitely(until stopped).
Looper is a mechanism which loops through the message Queue infinitely until stopped.

Message Queue

Each HandlerThread has one Message Queue into which other threads write data to(via Handlers), and it has a Looper which keeps on reading from it.

Looper:

A Looper is associated with every handler Thread and a Message Queue.
It keeps on reading the MessageQueue for any new Messages, and once it reads a message it will delegate that to the corresponding Callback(Handler).

Handler:

The handler is the exposed API to work with Loopers/MessageQueues. It basically exposes methods to interact with the Looper architecture, like writing messages to MQ, Setting callbacks to handle the message once Looper reads it, etc, etc.
So Each handler will need a Looper(so that it knows in which MQ to write data to since Looper has the reference to the MQ).

Message:

The Messages which can be sent to the HandlerThread. To avoid the number of Messages initializes, there is a pool maintained. Every time the looper is stopped, or the message is read by Looper, it is recycled(i.e. its fields are cleared off), and added to the pool. Next time we want to create a new Message, we get it from the pool( Message.obtain() method does that). This way the Android prevents us to initialize new Message instances every time.

The message is the LinkedList:

Line 1: Its next points to the next Message.
Line 2: It has long when signifying the timeInMillis of when should message be processed/read.

Please note that the LinkedList of these messages should be processed by their when, so it’s sorted on when.
When adding a new msg, we iterate the LinkedList to find the particular slot to insert this message and then insert it there.

Sending Data to UI thread(which is the Handler Thread too):
Say a worker thread(created at line 1) wants to send data(123) to UI thread:

Line 1: Creates a new worker thread.
Line 4: Get the Looper associated with the UI thread
Line 5:Create the Callback which will be called by UI thread, once it reads the message we are going to send to it.
Line 12: Create Handler with the Main Thread’s Looper, so that we write the message to Main thread’s MQ.
Line 13: Create an empty Message from the pool of messages( to avoid GC overload).
Line 14: Put some value into the Empty message — 123.
Line 15: Setting the above handler(which has callback defined) as the Message Target, which will be called once the message is read by the UI looper.
Line 16: Write the Message to the MQ via Main Looper via Handler.

Читайте также:  Account manager android что это

This is pretty easy to write to Main thread from worker thread, as the Main thread is itself a handler thread which has its Looper/MQ etc defined.
But what if we want to write our own Handler Thread to which other threads can communicate?
In that case we need to initialize Loopers, MQ, etc ourselves.

So make a Thread a Handler thread, we need to do:
1. Looper. prepare() → basically initializes the Looper and creates an MQ for it.

2. Looper. loop() → This will start the looper, which will keep on reading the Message Queue until Stopped.

Line 2: This will get the MQ for that Looper.
Line 8: Read from MQ infinitely until stopped(in that case msg read from Queue will be null).
Line 9: gets blocked until the message has arrived in the MQ. queue.next will be blocked until it sends a msg, it will send null only if Looper is stopped(using Looper.quit() method).
Line 15: Once msg is read, send it to its callback(defined in msg.target and hence in Handler’s callback).
Line 17: Once msg is handled, recycle it to clear its data off, and add it to the pool.

These above 2 methods are sufficient to create a Handler Thread.

Another easy way is to extend the Thread by HandlerThread, which under the hood calls these 2 above methods.

This is the HandlerThread run method: which calls Looper.prepare — then Looper.loop and in between onLooperPrepared to do something in this thread.

How to send data to such a Handler thread?

Simple, instead of passing the MaineLooper to the Handler, pass the Worker Handler Thread’s Looper like:

Line 6 is the only difference, here the looper belongs to the Handler thread we created and not that of the default UI thread.

Let’s explore the working of other Methods too:

MessageQueue::next

Looper.loop depends heavily on Queue.next() method. Below is the stripped-down version of next:

MessageQueue has the LinkedList of Messages(sorted on Message.when). Message.when corresponds to the time this message should be processed.

Line 6: Get the LinkedList of messages written into the Queue.
Line 7 and 8: If the message exists and message read has when before the current time(I.e. it is eligible to be processed), read it.
Line 10 and 11: LinkedList points to the next message now, as this message is read and could be removed from the Linked list(its next is set to null).
line 12: set this message in use, so that no further action can be done on it, like updating it, etc.
line 13: return it to the Looper. This will not happen until any message is read, as there is an infinite loop enclosing this code at line 2.
Line 17 and 18: Returns null only if it is stopped.

Adding Messages to Queue:

This is done via handler.sendMessage() or Handler.post()

2 types of messages can be sent to HandlerThread:
a. Messages: which have when, data, target, etc fields. Upon being read by the Looper, it delegates it to msg.target to handle the message. This will run on the HandlerThread, and not on the worker thread which sends this message.
b. Runnable: Upon read by the Looper, the Runnable.run() is called inside the Handler Thread. Under the hood this Runnable is converted into the empty message, and this runnable is set as its callback. So that this runnable also acts as a Message.

Line 2 wraps this Runnable into the Message, and its the Message which gets written into the MQ.

As shown, this basically calls queue.enqueueMessage to write this message into the mQueue(MessageQueue for this handler thread).

MessageQueue.enqueueMessage(Message )

Line 2: If it’s already being read(next() sets this boolean true), or already added into Queue(line 13), then don’t add the same message again.
Line 7–10: If the Queue/Looper is quitting, then don’t add msgs into it.
Line 13: Set this msg to be in use so that it’s not added into the queue again.
Line 16: If this msg is to be processed before or at the current time, add it at the front.
Line 17–18: LinkedList code to add msg at the head.
Line 21–27: Find the slot to add this msg based on its when, as the LL is sorted on its when. This is a general LinkedList iteration code.

Please note that all the above methods are stripped-down to ease the understanding.

Источник

Оцените статью