Android Guts: Intro to Loopers and Handlers

One of the reasons I love Android API is because it contains so many useful little things. Many of them are not even specific to the platform and I really miss them in Java SE. Today I’d like to give a short introduction to two nice classes – Looper and Handler. They are used in Android UI internally, and available to us developers so we can do some cool things with their help.

So what can you do with Loopers and Handlers? Basically, they implement a common concurrency pattern that I call the Pipeline Thread. Here’s how it works:

  • The Pipeline Thread holds a queue of tasks which are just some units of work that can be executed or processed.
  • Other threads can safely push new tasks into the Pipeline Thread’s queue at any time.
  • The Pipeline Thread processes the queued tasks one after another. If there are no tasks queued, it blocks until a task appears in the queue.
  • Sometimes tasks can called messages and other names.

This architecture has some valuable traits and is widely used in frameworks and applications on different platforms.

In this post, we are going to build a simple app that emulates a queue of background downloads while showing its status in the UI. It will be based on a Pipeline Thread that we will build using Looper and Handler. As usual, the complete source is available at the bottom of the article.

But before we start, let’s discuss the Pipeline Thread for some more time and see how Looper and Handler implement it.

Usages of the Pipeline Thread

The most prominent usage of Pipeline Thread is found within almost any UI framework, including Swing (remember the event-dispatching thread?), SWT, Adobe Flex and, of course, Android Activities. The Pipeline Thread pattern is used to process UI events (such as button clicks, mouse movement, orientation changes, screen redraw and so on). This allows you to change a button caption without having to worry that the user will click the button at the same moment.

On the other hand, this forces you to only do quick operations in the UI thread – any developer knows what happens if you try to download a file right there in a button onClick handler. In our app we will overcome that by adding another Pipeline Thread and pushing long-lasting operations (in our case, the downloads) to it so that the UI thread can run free.

Other common usages of the Pipeline Thread pattern:

  • Executing requests to a remote service (usually you want to do them one by one and sequentially)
  • Uploading images to an HTTP service
  • Resizing and processing images (I once developed a picture uploader :))
  • Downloading stuff, just like we’re going to do in our app

In general, using a Pipeline Thread rather than launching another thread for each background operation allows you to control the load and order (almost like law and order) of your background jobs. Also, you can have multiple Pipeline Threads and use them as a pool so you will have both the ability to execute multiple operations at once and keep the load under control.

In our example, we only want to allow one download at a time and we want downloads to execute in the order they were scheduled. Exactly the case to use a Pipeline Thread.

Looping and Handling

Looper is a class that turns a thread into a Pipeline Thread and Handler gives you a mechanism to push tasks into it from any other threads.

The Looper is named so because it implements the loop – takes the next task, executes it, then takes the next one and so on. The Handler is called a handler because someone could not invent a better name.

Here’s what you should put into a Thread‘s run() method to turn it into a Pipeline Thread and to create a Handler so that other threads can assign tasks to it:

?


01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

@Override

public void run() {

  try {

    // preparing a looper on current thread    

    // the current thread is being detected implicitly

    Looper.prepare();

    // now, the handler will automatically bind to the

    // Looper that is attached to the current thread

    // You don‘t need to specify the Looper explicitly

    handler = new Handler();

    

    // After the following line the thread will start

    // running the message loop and will not normally

    // exit the loop unless a problem happens or you

    // quit() the looper (see below)

    Looper.loop();

  } catch (Throwable t) {

    Log.e(TAG, "halted due to an error", t);

  }

}

After that, you can just pass the handler to any other thread. It has a thread-safe interface that includes many operations, but the most straightforward ones arepostMessage() and its relatives.

Note: The Handler interface has many more cool operations, especially related to message passing, that I will not cover in this example.

For example, imagine another thread has a reference to the handler that was created in our Pipeline Thread. Here’s how that other thread can schedule an operation to be executed in the Pipeline Thread:

?


1

2

3

4

5

6

handler.post(new Runnable() {

  @Override

  public void run() {      

    // this will be done in the Pipeline Thread      

  }

});

In our case, we will use this idiom to schedule download tasks to be performed (on the download pipeline thread) when the user clicks a button (which is handled on the UI thread). We will also use it the other way around – when the download thread notifies the activity that a download has completed (on the download thread), the activity will use a Handler that is attached to the UI thread to make sure that we update the UI on the UI thread only (which is a requirement of Android UI).

By the way, the UI thread has a Looper created for it implicitly, so you can just create a Handler in activity’s onCreate() and it will work fine:

?


1

2

3

4

5

6

7

8

9

@Override

public void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.main);

    

    // Create the Handler. It will implicitly bind to the Looper

    // that is internally created for this thread (since it is the UI thread)

    handler = new Handler();

}

Implementation Details

If you understood the idea of Looper and Handler, the rest is details. Basically, I created a DownloadTask class that emulates a download task of a random duration. I did not want to do actual downloads because I didn’t want to eat your data plans, but it’s straightforward to change this into real work. Then, theDownloadThread has an interface that allows to enqueue DownloadTask instances and also to request it to exit gracefully. There is also theDownloadThreadListener interface that allows the download thread to notify some other entity about status updates. In our case, that interface will be implemented by the DownloadQueueActivity because we want to reflect the download progress in the UI.

At this point, however, we need to use another Handler – the one that is attached to the UI thread. The DownloadThreadListener is notified by theDownloadThread from the download thread itself, but the action we want to take (updating the progressbar and the textview) needs to be done from the UI thread. Therefore we use the UI handler like this:

?


01

02

03

04

05

06

07

08

09

10

11

12

// note! this might be called from another thread

@Override

public void handleDownloadThreadUpdate() {

  // we want to modify the progress bar so we need to do it from the UI thread

  // how can we make sure the code runs in the UI thread? use the handler!

  handler.post(new Runnable() {

    @Override

    public void run() {

      // update the UI etc.

    }

  });

}

Note: In case of doubt, it’s also OK to call a handler from its own thread.

Of course, you should read the code and the comments to understand things clearly.

Also, it’s better to do things like background downloads in a service, as described in my previous article.

Conclusion

Loopers and Handlers allow you to do cool things. However, they are related to concurrency which is often a slippery area. Be sure to ask your questions and give feedback in the comments – and to stay tuned for updates!

Attachment: the complete project source

Tags: activityandroidappsarchitectureconcurrencydevelopmentgutshandlerlooperqueuetutorial

In General. You can leave a response, or trackback from your site.

时间: 2024-11-03 22:23:03

Android Guts: Intro to Loopers and Handlers的相关文章

android中的多线程机制

Google参考了Windows的消息处理机制,在Android系统中实现了一套类似的消息处理机制.学习Android的消息处理机制,有几个概念(类)必须了解: 1.       Message 消息,理解为线程间通讯的数据单元.例如后台线程在处理数据完毕后需要更新UI,则可发送一条包含更新信息的Message给UI线程. 2.       Message Queue 消息队列,用来存放通过Handler发布的消息,按照先进先出执行. 3.       Handler Handler是Messa

【转】android的消息处理机制(图+源码分析)——Looper,Handler,Message

原文地址:http://www.cnblogs.com/codingmyworld/archive/2011/09/12/2174255.html#!comments 作为一个大三的预备程序员,我学习android的一大乐趣是可以通过源码学习google大牛们的设计思想.android源码中包含了大量的设计模式,除此以外,android sdk还精心为我们设计了各种helper类,对于和我一样渴望水平得到进阶的人来说,都太值得一读了.这不,前几天为了了解android的消息处理机制,我看了Loo

android 的消息处理机制

android的消息处理机制(图+源码分析)——Looper,Handler,Message android的消息处理有三个核心类:Looper,Handler和Message.其实还有一个Message Queue(消息队列),但是MQ被封装到Looper里面了,我们不会直接与MQ打交道,因此我没将其作为核心类.下面一一介绍: 线程的魔法师 Looper Looper的字面意思是“循环者”,它被设计用来使一个普通线程变成Looper线程.所谓Looper线程就是循环工作的线程.在程序开发中(尤

Android消息处理机制

文章出处:http://www.cnblogs.com/qingblog/archive/2012/06/27/2566021.html Google参考了Windows的消息处理机制,在Android系统中实现了一套类似的消息处理机制.学习Android的消息处理机制,有几个概念(类)必须了解: 1.       Message 消息,理解为线程间通讯的数据单元.例如后台线程在处理数据完毕后需要更新UI,则可发送一条包含更新信息的Message给UI线程. 2.       Message Q

安卓处理机制

android的消息处理有三个核心类:Looper,Handler和Message.其实还有一个Message Queue(消息队列),但是MQ被封装到Looper里面了,我们不会直接与MQ打交道,因此我没将其作为核心类.下面一一介绍: 线程的魔法师 Looper Looper的字面意思是“循环者”,它被设计用来使一个普通线程变成Looper线程.所谓Looper线程就是循环工作的线程.在程序开发中(尤其是GUI开发中),我们经常会需要一个线程不断循环,一旦有新任务则执行,执行完继续等待下一个任

Handler机制中的五大对象

Handler机制中的五大对象 android的消息处理有三个核心类:Looper,Handler和Message.其实还有一个Message Queue(消息队列),但是MQ被封装到Looper里面了,我们不会直接与MQ打交道,因此我没将其作为核心类. 另外, 还有一个类: HandlerThread. Message: 消息 MessageQueue: 消息队列 Handler: 消息处理器, 发送, 处理消息 Looper: 循环器 HandlerThread: 消息处理线程 ------

Android Bottom Sheet详解

最近android更新了support library, 版本到了23.2, 从官方blog中我们还是可以看到几个令人心动的特性的,例如夜间模式的支持,BottomSheet.今天我们就来介绍一下这个Bottom Sheet,这可能会给我们开发中需要的一些效果带来便利. 虽然这里我们准备用整整一篇博客的时间来介绍它,不过这东西使用起来太简单了也太方便了,这还要感谢Behavior机制的引入,我记得在博客源码看CoordinatorLayout.Behavior原理中说过,Behavior其实是C

通过Spannable对象设置textview的样式

通过Spannable对象我们可以设置textview的各种样式,其功能十分强大.通过SpannableString和它的setSpan(Object what, int start, int end, int flags)便可以对textview设置想要的效果了,这里的what就是效果名,start和end是设置这个样式针对的是textview的字符位置. 1. 简单用法 创建一个spannableString,textview可以通过setText设置这个对象,效果就是textview显示了

Android附近基站+Wifi+IP+GPS多渠道定位方案

前言: 在移动客户端的开发中,地理位置定位是一个非常重要的环节,有些时候用户可能会限制web app或者Android app的一些权限,或者由于信号不佳的原因无法获得准确的GPS位置,甚至为了省电,用户可能对开启GPS开关可能会有抵触情绪.但是不能因为GPS的种种限制就放弃了对用户位置的追踪.要通过一切能发送出信号的物体尽可能准确的获取到用户的位置,有时可以牺牲一些精度,对于大数据和用户地区分布分析来说,有一个大体的位置已经够分析人员使用,而且绕开GPS的重重壁垒,为数据的完整性提供可靠方案