Android为什么要提供Handler

Android建议我们不要在UI线程中执行耗时操作,因为这很容易导致ANR异常(在Android源码中我们可以看到,UI如果对用户的操作超过5秒无响应,就会报ANR异常)。因此,一些耗时操作都会在子线程中完成。当我们在子线程中获取了数据,要将其显示到UI中,如果没有Handler,这将很难完成。因此,Android之所以提供Handler,就是为了解决子线程访问UI的问题。
为什么Android不允许在子线程中访问UI呢?显然这样做不安全,多线程访问UI是不安全的(学过操作系统的盆友应该都了解线程互斥,这里我就不详细介绍了)。有人就会说了,可以通过设置信号量来解决啊。这中方法不是不可以,因为这种方法会使访问UI的逻辑变得复杂;其次这会降低UI的访问效率。而使用Handler就比较简单高效。Handler是同个Message来通讯的。

Handler的用法

使用Handler时,需要重写handleMessage方法,在handleMessage中接受新线程发来的Message,并做相应的处理。在新线程中则是通过Message来传递消息,Message中往往也携带着需要传递的数据以及消息的类型。还要强调一点,如果当前线程有Looper就不需要执行Looper.prepare(),如果没有,就需要在新线程内执行Looper.prepare(),否则会报错。具体使用代码如下:

  1. public class MainActivity extends AppCompatActivity {
  2. private Handler mHandler=new Handler(){
  3. @Override
  4. public void handleMessage(Message msg) {
  5. switch (msg.what)
  6. {
  7. case 1:
  8. //执行需要修改的UI操作
  9. break;
  10. default:
  11. break;
  12. }
  13. }
  14. };
  15.  
  16. @Override
  17. protected void onCreate(Bundle savedInstanceState) {
  18. super.onCreate(savedInstanceState);
  19. setContentView(R.layout.activity_main);
  20.  
  21. new Thread(new Runnable() {
  22. @Override
  23. public void run() {//在新线程中执行耗时操作
  24.  
  25. //如果当前线程有Looper就不需要执行Looper.prepare();
  26. Looper.prepare();
  27. try {
  28. Thread.sleep(1000);//睡眠1秒
  29. } catch (InterruptedException e) {
  30. e.printStackTrace();
  31. }
  32.  
  33. //操作完成之后通过发送Message,来通知Handler进行UI操作
  34.  
  35. Message msg=new Message();
  36. msg.what=1;
  37.  
  38. /*这部分是伪代码,value 是想通过Message传递的值
  39. Bundle data=new Bundle();
  40. data.putSerializable("key",value);
  41. msg.setData(data);
  42.  
  43. */
  44.  
  45. //设置好数据后,发送消息
  46. mHandler.sendMessage(msg);
  47. }
  48. }).start();
  49. }
  50.  
  51. }

当然,handler也可以在子线程中创建,代码如下:

  1. private TextView tv_test;
  2.  
  3. @Override
  4. protected void onCreate(Bundle savedInstanceState) {
  5. super.onCreate(savedInstanceState);
  6. setContentView(R.layout.handler_test_layout);
  7.  
  8. tv_test= (TextView) findViewById(R.id.tv_test);
  9.  
  10. }
  11.  
  12. //button点击的函数
  13. public void click(View v)
  14. {
  15. new Thread(new Runnable() {
  16. @Override
  17. public void run() {
  18. Looper.prepare();
  19. Handler handler=new Handler(Looper.getMainLooper()){
  20. @Override
  21. public void handleMessage(Message msg) {
  22. switch (msg.what)
  23. {
  24. case 1:
  25. tv_test.setText("receive msg");
  26. }
  27. }
  28. };
  29. Message msg=new Message();
  30. msg.what=1;
  31. handler.sendMessage(msg);
  32. }
  33. }).start();
  34. }

上面的代码是,当点击按钮后,就会创建一个新的线程,在新线程中创建handler,并发送消息、接受消息。这里需要注意的是,在新线程中创建handler需要使用Handler handler=new Handler(Looper.getMainLooper())这样的写法,Looper.getMainLooper()将主线程中的Looper传过去,这样handler才能访问UI。运行效果如下:

Handler的内部机制

Handler创建时会采用Looper来建立消息循环。所以,当前线程必须要有Looper。当Handler创建完成后,其内部的Looper以及MessageQueue既可以和Handler一起协同工作了。Handler通过sendMessage将消息发送给内部的MessageQueue,而MessageQueue会调用queue.enqueueMessage(msg, uptimeMillis)方法,它的源码如下:

  1. boolean enqueueMessage(Message msg, long when) {
  2. if (msg.target == null) {
  3. throw new IllegalArgumentException("Message must have a target.");
  4. }
  5. if (msg.isInUse()) {
  6. throw new IllegalStateException(msg + " This message is already in use.");
  7. }
  8.  
  9. synchronized (this) {
  10. if (mQuitting) {
  11. IllegalStateException e = new IllegalStateException(
  12. msg.target + " sending message to a Handler on a dead thread");
  13. Log.w(TAG, e.getMessage(), e);
  14. msg.recycle();
  15. return false;
  16. }
  17.  
  18. msg.markInUse();
  19. msg.when = when;
  20. Message p = mMessages;
  21. boolean needWake;
  22. if (p == null || when == 0 || when < p.when) {
  23. // New head, wake up the event queue if blocked.
  24. msg.next = p;
  25. mMessages = msg;
  26. needWake = mBlocked;
  27. } else {
  28. // Inserted within the middle of the queue. Usually we don't have to wake
  29. // up the event queue unless there is a barrier at the head of the queue
  30. // and the message is the earliest asynchronous message in the queue.
  31. needWake = mBlocked && p.target == null && msg.isAsynchronous();
  32. Message prev;
  33. for (;;) {
  34. prev = p;
  35. p = p.next;
  36. if (p == null || when < p.when) {
  37. break;
  38. }
  39. if (needWake && p.isAsynchronous()) {
  40. needWake = false;
  41. }
  42. }
  43. msg.next = p; // invariant: p == prev.next
  44. prev.next = msg;
  45. }
  46.  
  47. // We can assume mPtr != 0 because mQuitting is false.
  48. if (needWake) {
  49. nativeWake(mPtr);
  50. }
  51. }
  52. return true;
  53. }

通过源码,我们发现,queue.enqueueMessage(msg, uptimeMillis)将消息放入了MessageQueue里。Looper则会一直处理MessageQueue中的消息。

  1.  
  1.  

Android消息机制之Handler的更多相关文章

  1. Android 消息机制 (Handler、Message、Looper)

    综合:http://blog.csdn.net/dadoneo/article/details/7667726 与 http://android.tgbus.com/Android/androidne ...

  2. Android开发之漫漫长途 ⅥI——Android消息机制(Looper Handler MessageQueue Message)

    该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...

  3. Android开发之漫漫长途 Ⅶ——Android消息机制(Looper Handler MessageQueue Message)

    该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...

  4. Android消息机制探索(Handler,Looper,Message,MessageQueue)

    概览 Android消息机制是Android操作系统中比较重要的一块.具体使用方法在这里不再阐述,可以参考Android的官方开发文档. 消息机制的主要用途有两方面: 1.线程之间的通信.比如在子线程 ...

  5. android 消息机制,handler机制,messageQueue,looper

    韩梦飞沙  韩亚飞  313134555@qq.com  yue31313  han_meng_fei_sha handler  就是 处理器 .  用来处理消息, 发送消息. handler   就 ...

  6. Android消息机制:Looper,MessageQueue,Message与handler

    Android消息机制好多人都讲过,但是自己去翻源码的时候才能明白. 今天试着讲一下,因为目标是讲清楚整体逻辑,所以不追究细节. Message是消息机制的核心,所以从Message讲起. 1.Mes ...

  7. android 进程间通信 messenger 是什么 binder 跟 aidl 区别 intent 进程间 通讯? android 消息机制 进程间 android 进程间 可以用 handler么 messenger 与 handler 机制 messenger 机制 是不是 就是 handler 机制 或 , 是不是就是 消息机制 android messenge

    韩梦飞沙  韩亚飞  313134555@qq.com  yue31313  han_meng_fei_sha messenger 是什么 binder 跟 aidl 区别 intent 进程间 通讯 ...

  8. Android 进阶14:源码解读 Android 消息机制( Message MessageQueue Handler Looper)

    不要心急,一点一点的进步才是最靠谱的. 读完本文你将了解: 前言 Message 如何获取一个消息 Messageobtain 消息的回收利用 MessageQueue MessageQueue 的属 ...

  9. Android进阶——Android消息机制之Looper、Handler、MessageQueen

    Android消息机制可以说是我们Android工程师面试题中的必考题,弄懂它的原理是我们避不开的任务,所以长痛不如短痛,花点时间干掉他,废话不多说,开车啦 在安卓开发中,常常会遇到获取数据后更新UI ...

随机推荐

  1. SQL Server Service Borker 1

    1.消息类型定义: 消息类型,是信息交换的模板.create message type message_type_name validattion = well_formed_xml; 2.约定定义: ...

  2. 解决Oracle 11gR2 空闲连接过多,导致连接数满的问题

    今天又遇到了11gR2连接数满的问题,以前也遇到过,因为应用那边没有深入检查,没有找到具体原因,暂且认为是这个版本Oracle的BUG吧. 上次的处理办法是用Shell脚本定时在系统中kill  v$ ...

  3. 优盘(U 盘) 采用TLC, MLC, SLC芯片 的区别 与使用寿命

    最近一直在看大家在讨论sandisk,pny,金士顿等大厂都开始用tlc的芯片问题,让大家基本都不敢用U盘存数据了按照之前的擦写参数TLC        1000次MLC       10000次SL ...

  4. Unix/Linux环境C编程入门教程(3) Oracle Linux 环境搭建

    Unix/Linux版本众多,我们推荐Unix/Linux初学者选用几款典型的Unix/Linux操作系统进行学习. 2010年9月,Oracle Enterprise Linux发布新版内核--Un ...

  5. CSliderCtrl鼠标点击精确定位

    实现CSliderCtrl的子类CXXCtrl 响应左键按下消息 ON_WM_LBUTTONDOWN() void CXXCtrl::OnLButtonDown(UINT nFlags, CPoint ...

  6. display的table和cell外加table-layout:fixed等分布局,外加换行,word-wrap:break-word

    <!doctype html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  7. HDU 4937 Lucky Number 规律题_(:зゝ∠)_

    把全部合法的进制打出来会发现合法的进制都是在 n/3 n/4 n/5的边上 然后暴力边上的进制数.. #include <cstdio> #include <set> type ...

  8. xcode -饼状进度条

    界面搭建 创建一个画饼状的类  eatView 集成UIView #import "eatView.h" @implementation eatView // Only overr ...

  9. sql获取表字段名、描述和类型

    SELECT TableName = OBJECT_NAME(c.object_id), ColumnsName = c.name, Description = ex.value, ColumnTyp ...

  10. <转>ASP.NET学习笔记之理解MVC底层运行机制

    ASP.NET MVC架构与实战系列之一:理解MVC底层运行机制 今天,我将开启一个崭新的话题:ASP.NET MVC框架的探讨.首先,我们回顾一下ASP.NET Web Form技术与ASP.NET ...