解析Android的消息传递机制Handler

  • 1. 什么是Handler:

    Handler 网络释义“操纵者,管理者的”意思,在Android里面用于管理多线程对UI的操作;

    2. 为什么会出现Handler:

    Android的设计机制里面,只允许主线程(一个程序第一次启动时所移动的线程,因为此线程主要是完成对UI相关事件的处理,所以也称UI线程)

    对UI进行修改等操作,这是一种规则的简化,之所以这样简化是因为Android的UI操作时线程不安全的,为了避免多个线程同时操作UI造成线程安全

    问题,才出现了这个简化的规则。

    由此以来,问题就出现了,因为只允许主线程修改UI,那么如果新线程的操作需要修改原来的UI该如何进行的?举个常见的例子就是:如果新线程的操作是更新UI中TextView

    的值,那么该如何操作?

    这时候就需要Handler在新线程和主线程(UI线程)之间传递休息;

    3. Handler的功能:

    主要就是两个:

    1)在新启动的线程中发送消息;

    2)在主线程中获取,处理消息;

    看似简单,但是如何处理同步问题却是一个难题,即如何把握新线程发送消息的时机和主线程处理消息的时机。这个问题的解决方案是:

    在主线程和新线程之间使用一个叫做MessageQueue的队列,新启动的线程发送消息时将消息先发送到与之关联的MessageQueue,然后主线程的Handler方法会被调用

    从MessageQueue中去取相应的消息进行处理。

    4. Handler的实现机制

    Handler的实现主要是依靠下面的几个方法:

    读取消息使用到的方法是;

    void handleMessage(Message msg) ,进程通过重写这个方法来处理消息。

    final boolean hasMessage(int what), 检查消息队列中是否包含what属性为指定值的消息。

    final boolean hasMessage(int what,Object object),减产队列中是否有指定值和指定对象的消息。

    Message obtainMessage(): 获取消息,课被多种方式重载。

    发送消息用到的方法有:

    sendEmptyMessage(int what): 发送空消息;

    final boolean sendEmptyMessageDelayed(int what, long delayMillis):指定多少毫秒之后发送空消息

    final boolean sendMessage(Message msg):立即发送消息

    final boolean sendMessageDelayed(Message msg, long delayMillis)指定多少毫秒之后发送空消息

    01. public class HandlerTest extends Activity
    02. {
    03. ImageView show;
    04. // 代表从网络下载得到的图片
    05. Bitmap bitmap;
    06. Handler handler = new Handler()
    07. {
    08. @Override
    09. <span style="color:#ff0000;">public void handleMessage(Message msg)</span>
    10. {
    11. if(msg.what == 0x123)  //如果该消息是本程序发的
    12. {
    13. // 使用ImageView显示该图片
    14. show.setImageBitmap(bitmap);
    15. }
    16. }
    17. };
    18. @Override
    19. public void onCreate(Bundle savedInstanceState)
    20. {
    21. super.onCreate(savedInstanceState);
    22. setContentView(R.layout.main);
    23. show = (ImageView) findViewById(R.id.show);
    24. <span style="color:#ff0000;">new Thread()</span>
    25. {
    26. public void run()
    27. {
    28. try
    29. {
    30. // 定义一个URL对象
    31. URL url = new URL("http://img001.21cnimg.com/photos"+
    32. <span style="white-space:pre">                    </span>"/album/20140626/o/C164BDB0B24F59929C2113C0A9910636.jpeg");
    33. // 打开该URL对应的资源的输入流
    34. InputStream is = url.openStream();
    35. // 从InputStream中解析出图片
    36. bitmap = BitmapFactory.decodeStream(is);
    37. // 发送消息、通知UI组件显示该图片
    38. <span style="color:#ff0000;">handler.sendEmptyMessage(0x123);</span>
    39. is.close();
    40. }
    41. catch (Exception e)
    42. {
    43. e.printStackTrace();
    44. }
    45. }
    46. }.start();
    47. }
    48. }

    新的进程在将图片从网上解析下来之后向主进程发送空消息,之后主线程中handleMessage()的方法会被自动调用,更新UI。

    5. 深入理解Handler的工作机制

    上面我们看到了一个简单的Handler的工作过程,其中Handler是在主线程中定义的。如果Handler是在子线程中定义的那么可以更深入的理解他的工作原理。

    因为有的时候我们需要将主线程中的消息传递给子线程,让子线程去处理一些计算量比较大的任务,因为应用程序应尽量避免在UI线程中执行耗时操作,否则会

    导致ANR异常(Application Not Responding)。这样的话,我们上面所讲的 主线程和新线程的角色就发生了颠倒,主线程需要向新线程发送消息,然后新线程

    进行消息的处理。在这种情况下,Handler需要定义在新线程中,在这种情况下需要做一些额外的工作。

    我们先来解释一下配合Handler的其它组件:

    Looper: 每个线程对应一个looper,它负责管理MessageQueue,将消息从队列中取出交给Handler进行处理;

    MessageQueue:负责管理Message,接收Handler发送过来的message;

    下图是整个工作的流程:


    下面我们具体阐述一下工作的过程:

    在创建一个Handler之前需要先创建Looper,创建的方式是Looper.prepare();

    在创建Looper的同时会自动创建MessageQueue;

    下面 创建一个Handler的对象

    然后调用Looper的loop()方法

    下面的这段代码是一个实例,摘自 疯狂android讲义;主进程将上限发送给子线程计算2-上限之间的素数

    01. public class CalPrime extends Activity
    02. {
    03. static final String UPPER_NUM = "upper";
    04. EditText etNum;
    05. CalThread calThread;
    06. // 定义一个线程类
    07. class CalThread extends Thread
    08. {
    09. <span style="color:#cc0000;">public Handler mHandler;</span>
    10.  
    11. public void run()
    12. {
    13. <span style="color:#ff0000;">Looper.prepare();</span>
    14. <span style="color:#ff0000;">mHandler = new Handler()</span>
    15. {
    16. // 定义处理消息的方法
    17. @Override
    18. public void handleMessage(Message msg)
    19. {
    20. if(msg.what == 0x123)
    21. {
    22. int upper = msg.getData().getInt(UPPER_NUM);
    23. List<Integer> nums = new ArrayList<Integer>();
    24. // 计算从2开始、到upper的所有质数
    25. outer:
    26. for (int i = 2 ; i <= upper ; i++)
    27. {
    28. // 用i处于从2开始、到i的平方根的所有数
    29. for (int j = 2 ; j <= Math.sqrt(i) ; j++)
    30. {
    31. // 如果可以整除,表明这个数不是质数
    32. if(i != 2 && i % j == 0)
    33. {
    34. continue outer;
    35. }
    36. }
    37. nums.add(i);
    38. }
    39. // 使用Toast显示统计出来的所有质数
    40. Toast.makeText(CalPrime.this , nums.toString()
    41. , Toast.LENGTH_LONG).show();
    42. }
    43. }
    44. };
    45. <span style="color:#ff0000;">Looper.loop();</span>
    46. }
    47. }
    48. @Override
    49. public void onCreate(Bundle savedInstanceState)
    50. {
    51. super.onCreate(savedInstanceState);
    52. setContentView(R.layout.main);
    53. etNum = (EditText)findViewById(R.id.etNum);
    54. <span style="color:#ff0000;">calThread = new CalThread();</span>
    55. // 启动新线程
    56. <span style="color:#ff0000;">calThread.start();</span>
    57. }
    58. // 为按钮的点击事件提供事件处理函数
    59. public void cal(View source)
    60. {
    61. // 创建消息
    62. <span style="color:#ff0000;">Message msg = new Message();</span>
    63. msg.what = 0x123;
    64. Bundle bundle = new Bundle();
    65. bundle.putInt(UPPER_NUM ,
    66. Integer.parseInt(etNum.getText().toString()));
    67. <span style="color:#ff0000;">msg.setData(bundle);</span>
    68. // 向新线程中的Handler发送消息
    69. <span style="color:#ff0000;">calThread.mHandler.sendMessage(msg);</span>
    70. }
    71. }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值