Handler method in 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() — помещает сообщение в очередь после задержки, выраженной в миллисекундах
Читайте также:  Скинуть андроид через usb

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

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

Splash-screen

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

Источник

Mastering Android Handler

What is Handler, internals and Best practices. Back to Basics.

Preface

If you are an Android developer, I can say with surety you have used Handler directly or indirectly. The question arises do you understand it in depth? Let’s go back to basics and learn in and out of Handler. Basic understanding of Java threads and generics is expected. For naive to experienced developers, this article aims to cover something for everyone.

W hat is Handler?

First thing first, Handler is not related to UI. Read last line again. Before we jump into Handler business, let us set the base.

Most of UI based operating systems are single threaded. Means, only one thread is permitted to touch User interface -> drawing on the canvas. This choice is made to keep things simple and avoid complicacy. Few attempts have been made in past to make UI multi-threaded but failed because of confusing response to users, race condition, deadlock, and a few other problems.

UI Thread

Android is a single threaded UI framework. We all know there is one UI thread also known as Main thread responsible for drawing. If you touch UI in any other thread, you meet CalledFromWrongThreadException ; Therefore some mechanism is needed to perform UI work in UI thread. Most of the developers have used Handler to accomplish that. Handler and View offers post utility:

Are both ways identical? For now, let’s say view.post is also using Handler inside. You will understand more by the end of this article.

What is post doing here? How does it work internally? Why is it even needed? What are the tips and tricks? Let’s begin the ride.

Thread Communication:

Suppose you have two threads, A and B. How will you facilitate communication between them? Consider classical producer-consumer example. A consumer can only consume after the producer has produced. All other times, it has to wait. The point is it is the basis for Handler.

First definition of Handler: It is the Android way of Thread communication. It is not related to UI. You use Handler to order threads to perform the desired action. Any thread can be used with Handler way with a small ceremony.

But how Handler is related to a Thread and how does we can use it to order thread to do something?

Best way to understand something is make our own hands dirty. Suppose you do not have Handler and you are writing a library to make threading easy with following requirements:

  1. A developer must be able to run any piece of code in any thread
  2. A developer must be able to run any piece of code in a thread of their choice
  3. A developer must be able to run any piece of code in a thread of their choice in any class they need (don’t worry you will understand soon)
  4. Make point 2/3 as easy as possible

Run any piece of code in any thread

If not Handler, I will give a shot using ThreadPool and Runnable/Callable. I think this should suffice and does not deserve explanation here. If you want to share a better way, feel free to leave a note OR comment.

Читайте также:  Как написать вирус для андроида

Run any piece of code in a thread of their choice

One solution is we can have our own ThreadFactory and spawn a thread as needed. We can have BlockingQueue attached to each Thread spawned and we can post our custom Command object every-time we need to do something in that particular thread.

Let’s say we spawned a thread named NetworkThread which accepts only NetworkCommand. Consider this shitty piece of code:

Similarly, you can spawn as many Threads you need and point 2 is taken care. We can run any piece of code using our NetworkCommand which can have Runnable/Callable or any other interface.

Run any piece of code in a thread of their choice in any class they need which is readable

In point 2 solution, developers can call queue their Command from any class and they will get callback in NetworkThread. Point 3 is partially taken care as well.

There are a few small problems here:

  1. It is not readable when many Runnable are queued in callers class and it reads ugly
  2. Ugly
  3. Ugly
  4. What if you want to pass anything other than heavy Runnable, for e.g an int representing data Loaded signal
  5. How do you identify by just reading which Runnable is called in which thread? You might need to invent your own way, Annotation is one solution and comment is other. // Many devs: Sorry what is a comment?
  6. Ugly
  7. Many other issues you will encounter OR requirements you need to fulfill

How about we create 3 classes

  1. A class which takes care of Thread and Looping through Blocking Queue
  2. Generic Command class with the capability of carrying a different type of data [You create these objects and consume]
  3. A class you can attach your command to as destination [Each command has a target where the command will be received]. You send your commands to this class and it adds these commands to blocking queue under the hood. This allows the possibility of queuing commands with delay and other cool functionalities.
  4. First class loops and calls Target class (3) of command
  5. You can create multiple objects of Target class(3) and it appears they are part of Worker Thread

In a nutshell, we just discussed the architecture of Handler.

Let’s just say Android framework devs realized very early they will be using one Thread at a lot of places and they need to make spaghetti code calling everything from everywhere. They came up with a solution for Point 3 & Point 4 and architected it in Handler, Looper and Message.

Summary of Part 1

To maintain the requirement of passing work from different threads and ensuring it feels natural and easy to understand, Android framework devised this mechanism of passing messages around and used it like no tomorrow. They created a class which runs on any thread of your choice and loops infinite unless you say otherwise. Now you understand why it is called Looper. It is similar to blocking queue looping. Every message it receives has a target attached to it which handles the command, they named it Handler. Every command is called Message, I am sure the author does not remember why the name.

Simple requirement is Handler cannot be instantiated without Looper. After all, it needs that queue. If the Thread you want to use has its Looper, you can create Handler with

If you run this code in UI thread, it will execute properly and magically UI thread looper (queue) will be attached to myHandler. But if you run this in another thread, you will face error.

May of us have copied the following code:

It worked like magic but I always had some itch what the heck it is!

( Bitch about Android) I must admit, this is one of the rare scenarios when I admire Android(while most devs hate this implementation). Usually, I say Android is developed based on what Youtube and Gmail apps demand where it should be vice-versa. Handler is a really clever mechanism of thread communication they have created. They allow thread to switch task and it does not seem like heavy thread work. It appears natural(to me OR I have just accepted the reality of life). How does it happen? Let me try to shed some light.

Читайте также:  Задачи по андроид программированию

In any thread, Handler internally calls

which uses ThreadLocal [One object per Thread]

and if it does not find (which your thread does not have), you meet RuntimeException

It very very simple once you grasp it.

So your normal thread does not have Looper associated with it. Simple, attach a looper using utility prepare:

Once prepared you can create a new Handler Object and it will not crash with Runtime exception. Following code in your run method:

Now you attached a looper (queue) and created Handler as well. What is missing? It is like politicians promising in the political memorandum and never achieving any results. We forgot the main part, execution.

We attached the looper and assigned a Handler which can receive callbacks but we forgot to loop through the messages which is fairly simple with loop method

Loop method says:

It has an infinite loop which simply waits for messages (remember blocking queue) and calls the message target which is Handler.

You can attach as many Handlers as you want to your thread which has Looper. I leave that as an exercise for you.

HandlerThread

The above thread we saw which has looper and handler can be called HandlerThread. One day Android framework devs realized this small ceremony can be offered as Utility and they created HandlerThread (literally the name).

If you need a worker thread which should have looper attached to it, you can use this utility. It does not save you much code but it is a decent wrapper above looper API. You need to call:

And you have another worker thread ready which is capable of queuing messages and running in background aligned to Android way.

In my all apps, I have one worker thread which I create using HandlerThread. I use it everywhere UI thread can’t be used for small small tasks.
Note: All requests to this are queued as per Handler looper contract, so I choose it explicitly for tasks which can be queued.

Message

Is there anything special about Message? Android framework uses it extensively inside. However, it has a few more use cases like IPC which we will cover sometime later. Important to know in the context of this article is Message has a cached queue inside. You should not use a plain constructor.

I find it a weird API (did I just define whole Android?). if something is Public, do not write preferred way. It would have been better if they had made it non-public(IMO). Life is good when binary. When we are in May be dilemma, we never enjoy anything.

Obtain method maintains a pool and reuses objects when possible.

There is a small catch here, Message object has a limited life. Once you receive it in your Handler and execution returns from there, Looper will mark message unused and recycle.

Once the message is recycled, it can be reused again when next time you call Obtain. So if you cache your message and do some long running task before you process your message, there is a possibility that it is recycled and data is cleared. Dare to debug? Welcome to threading world!

Solution Whenever you want to consume message with some delay(not instant), use copy:

Источник

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