Android handler from thread

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.

Читайте также:  Андроид авто не видит 2гис почему

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:

Источник

Android UI thread

Большая часть кода Android приложения работает в контексте компонент, таких как Activity, Service, ContentProvider или BroadcastReceiver. Рассмотрим, как в системе Android организованно взаимодействие этих компонент с потоками.

При запуске приложения система выполняет ряд операций: создаёт процесс ОС с именем, совпадающим с наименованием пакета приложения, присваивает созданному процессу уникальный идентификатор пользователя, который по сути является именем пользователя в ОС Linux. Затем система запускает Dalvik VM где создаётся главный поток приложения, называемый также «поток пользовательского интерфейса (UI thread)». В этом потоке выполняются все четыре компонента Android приложения: Activity, Service, ContentProvider, BroadcastReceiver. Выполнение кода в потоке пользовательского интерфейса организованно посредством «цикла обработки событий» и очереди сообщений.

Читайте также:  История yandex browser android

Рассмотрим взаимодействие системы Android с компонентами приложения.

Activity. Когда пользователь выбирает пункт меню или нажимает на экранную кнопку, система оформит это действие как сообщение (Message) и поместит его в очередь потока пользовательского интерфейса (UI thread).

Service. Исходя из наименования, многие ошибочно полагают, что служба (Service) работает в отдельном потоке (Thread). На самом деле, служба работает так же, как Activity в потоке пользовательского интерфейса. При запуске локальной службы командой startService, новое сообщение помещается в очередь основного потока, который выпонит код сервиса.

BroadcastReceiver. При создании широковещательного сообщения система помещает его в очередь главного потока приложения. Главный поток позднее загрузит код BroadcastReceiver который зарегистрирован для данного типа сообщения, и начнёт его выполнение.

ContentProvider. Вызов локального ContentProvider немного отличается. ContentProvider также выполняется в основном потоке но его вызов является синхронным и для запуска кода ContentProvider не использует очередь сообщений.

Исходя из вышесказанного можно заметить, что если главный поток в данный момент обрабатывает пользовательский ввод или выполняет иное действие, выполнение кода, полученного в новом сообщении, начнётся только после завершения текущей операции. Если какая либо операция в одном из компонентов потребует значительного времени выполнения, пользователь столкнётся или с анимацией с рывками, или с неотзывчивыми элементами интерфейса или с сообщением системы «Приложение не отвечает» (ANR).

Для решения данной проблемы используется парадигма параллельного программирования. В Java для её реализации используется понятие потока выполнения (Thread).

Thread: поток, поток выполнения, иногда ещё упоминается как нить, можно рассматривать как отдельную задачу, в которой выполняется независимый набор инструкций. Если в вашей системе только один процессор то потоки выполняются поочередно (но быстрое переключение системы между ними создает впечатление параллельной или одновременной работы). На диаграмме показано приложение, которое имеет три потока выполнения:

Но, к сожалению, для взаимодействия с пользователем, от потока мало пользы. На самом деле, если вы внимательно посмотрите на диаграмму выше, вы поймёте, что как только поток выполнить все входящие в него инструкции он останавливается и перестаёт отслеживать действия пользователя. Чтобы избежать этого, нужно в наборе инструкций реализовать бесконечный цикл. Но возникает проблема как выполнить некое действие, например отобразить что-то на экране из другого потока, иными словами как вклиниться в бесконечный цикл. Для этого в Android можно использовать Android Message System. Она состоит из следующих частей:

Looper: который ещё иногда ещё называют «цикл обработки событий» используется для реализации бесконечного цикла который может получать задания используется. Класс Looper позволяет подготовить Thread для обработки повторяющихся действий. Такой Thread, как показано на рисунке ниже, часто называют Looper Thread. Главный поток Android на самом деле Looper Thread. Looper уникальны для каждого потока, это реализованно в виде шаблона проектирования TLS или Thread Local Storage (любопытные могут посмотреть на класс ThreadLocal в Java документации или Android).

Message: сообщение представляет собой контейнер для набора инструкций которые будут выполнены в другом потоке.

Handler: данный класс обеспечивает взаимодействие с Looper Thread. Именно с помощью Handler можно будет отправить Message с реализованным Runnable в Looper, которая будет выполнена (сразу или в заданное время) потоком с которым связан Handler. Код ниже иллюстрирует использование Handler. Этот код создаёт Activity которая завершиться через определённый период времени.

HandlerThread: написание кода потока реализующего Looper может оказаться не простой задачей, чтобы не повторять одни и те же ошибки система Android включает в себя класс HandlerThread. Вопреки названию этот класс не занимается связью Handler и Looper.

Практическую реализацию данного подхода можно изучить на примере кода класса IntentService, данный класс хорошо подходит для выполнения асинхронных сетевых или иных запросов, так как он может принимать задания одно за другим, не дожидаясь полной обработки текущего, и завершает свою работу самостоятельно.

Выполнение операций в отдельном потоке, не означает, что вы можете делать все что угодно, не влияя на производительность системы. Никогда не забывайте, что написанный вами код работает на машинах, как правило, не очень мощных. Поэтому всегда стоит использовать возможности предоставляемые системой для оптимизации.

Подготовлено на основе материалов AndroidDevBlog

Источник

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