深入理解之 Android Handler

 

一,相关概念

在Android中如果通过用户界面(如button)来来启动线程,然后再线程中的执行代码将状态信息输出到用户界面(如文本框),这时候就会抛出以下的异常信息:

5-12 13:33:04.393: ERROR/JavaBinder(1029):android.view.ViewRoot$CalledFromWrongThreadException:Onlythe original thread that created a view hierarchy can touch its views.

该异常的意思是,只有最初创建视图层次结构的线程才能接触该结构中的视图,也就是说,不是最初创建界面的线程是不能接触界面元素的。那么,在不是创建界面的线程中,如何把内容输出到界面元素中呢?

对了,我们可以通过线程消息分发机制来实现。就应用程序而言,Android系统中Java的应用程序和其他系统上一样,都是依赖于消息驱动完成那些比较有难度的工作,它们的工作原理大致是分别有一个消息队列负责接收各种消息,一个消息循环可以不断从中取出消息然后做处理。先了解一下几个消息机制的常见名词概念:

  • Handler

消息的封装者和处理者,handler负责将需要传递的信息封装成Message,通过调用handler对象的obtainMessage()来实现;将消息传递给Looper,这是通过handler对象的sendMessage()来实现的。继而由Looper将Message放入MessageQueue中。当Looper对象看到MessageQueue中含有Message,就将其广播出去。该handler对象收到该消息后,调用相应的handler对象的handleMessage()方法对其进行处理。

  • Message

消息对象,Message Queue中的存放的对象。一个Message Queue中包含多个Message。Message实例对象的取得,通常使用Message类里的静态方法obtain(),该方法有多个重载版本可供选择;它的创建并不一定是直接创建一个新的实例,而是先从Message Pool(消息池)中看有没有可用的Message实例,存在则直接取出返回这个实例。如果Message Pool中没有可用的Message实例,则才用给定的参数创建一个Message对象。调用removeMessages()时,将Message从Message Queue中删除,同时放入到Message Pool中。除了上面这种方式,也可以通过Handler对象的obtainMessage()获取一个Message实例。

  • MessageQueue

是一种数据结构,见名知义,就是一个消息队列,存放消息的地方。每一个线程最多只可以拥有一个MessageQueue数据结构。创建一个线程的时候,并不会自动创建其MessageQueue。通常使用一个Looper对象对该线程的MessageQueue进行管理。主线程创建时,会创建一个默认的Looper对象,而Looper对象的创建,将自动创建一个Message Queue。其他非主线程,不会自动创建Looper,要需要的时候,通过调用prepare函数来实现。

  • Looper

是MessageQueue的管理者。每一个MessageQueue都不能脱离Looper而存在,Looper对象的创建是通过prepare函数来实现的。同时每一个Looper对象和一个线程关联。通过调用Looper.myLooper()可以获得当前线程的Looper对象创建一个Looper对象时,会同时创建一个MessageQueue对象。除了主线程有默认的Looper,其他线程默认是没有MessageQueue对象的,所以,不能接受Message。如需要接受,自己定义一个Looper对象(通过prepare函数),这样该线程就有了自己的Looper对象和MessageQueue数据结构了。Looper从MessageQueue中取出Message然后,交由Handler的handleMessage进行处理。处理完成后,调用Message.recycle()将其放入Message Pool中。

二,Looper类分析

当需要向其他线程发消息时我们需要实例化Looper对象会有以下两步:

1,Looper.prepare()

我们可以看下源码:

  1. // sThreadLocal.get() will return null unless you've called prepare().
  2. static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
  3. public static void prepare() {
  4. if (sThreadLocal.get() != null) {
  5. throw new RuntimeException("Only one Looper may be created per thread");
  6. }
  7. // 构造一个Looper对象,保存到调用线程的局部变量中。
  8. sThreadLocal.set(new Looper());
  9. }

可以看出来,Looper.prepare()会在调用线程的局部变量中设置一个Looper对象。这个调用线程就是LooperThread的run线程。F3看一下Looper的构造方法:

  1. private Looper() {
  2. //构造一个消息队列
  3. mQueue = new MessageQueue();
  4. mRun = true;
  5. //获取当前线程的Thread对象
  6. mThread = Thread.currentThread();
  7. }

所以Looper.prepare()的作用是设置一个Looper对象保存在sThreadLocal里,而Looper自己内部封装了一个消息队列。就这样把Looper和调用线程关联在一起了。到底要干嘛,我们看Looper的第二步棋

2,Looper.loop()

  1. public static void loop() {
  2. //myLooper()将会返回保存在sThreadLocal中的Looper对象。
  3. Looper me = myLooper();
  4. if (me == null) {
  5. throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
  6. }
  7. //取出该Looper中的消息队列
  8. MessageQueue queue = me.mQueue;
  9. while (true) {
  10. //处理消息,Message对象中有一个target,它是Handle类型
  11. Message msg = queue.next(); // might block
  12. if (msg != null) {
  13. if (msg.target == null) {
  14. // No target is a magic identifier for the quit message.
  15. return;
  16. }
  17. msg.target.dispatchMessage(msg);
  18. msg.recycle();
  19. }
  20. }
  21. }
  22. /**
  23. * Return the Looper object associated with the current thread.  Returns
  24. * null if the calling thread is not associated with a Looper.
  25. */
  26. public static Looper myLooper() {
  27. return sThreadLocal.get();
  28. }

综上可以知道Looper类的作用是:

封装了一个消息队列;

Looper的prepare方法把这个Looper和调用prepare的线程绑定在一起;

处理线程调用loop方法,处理来自该消息队列的消息。

三,Handler类分析

1,好多构造函数

一样看去,我们可以看到几个眼熟的家伙:MessageQueue,Looper,Callback,一个不少,他们是干嘛的?往下看发现有4个形态不一的构造函数:

  1. /**
  2. * 构造函数一,铁板一块,不过也完成了调用线程的Looper,并获得它的消息队列。
  3. */
  4. public Handler() {
  5. mLooper = Looper.myLooper();
  6. if (mLooper == null) {
  7. throw new RuntimeException(
  8. "Can't create handler inside thread that has not called Looper.prepare()");
  9. }
  10. mQueue = mLooper.mQueue;
  11. mCallback = null;
  12. }
  13. /**
  14. * 构造函数二,多了一个callback
  15. */
  16. public Handler(Callback callback) {
  17. mLooper = Looper.myLooper();
  18. if (mLooper == null) {
  19. throw new RuntimeException(
  20. "Can't create handler inside thread that has not called Looper.prepare()");
  21. }
  22. mQueue = mLooper.mQueue;
  23. mCallback = callback;
  24. }
  25. /**
  26. * 构造函数三,Looper由外部传入,定制。
  27. */
  28. public Handler(Looper looper) {
  29. mLooper = looper;
  30. mQueue = looper.mQueue;
  31. mCallback = null;
  32. }
  33. /**
  34. * 构造函数四,它是2和3的结合体。
  35. */
  36. public Handler(Looper looper, Callback callback) {
  37. mLooper = looper;
  38. mQueue = looper.mQueue;
  39. mCallback = callback;
  40. }

在上面的构造函数中,Handler中的消息队列变量最终都会指向Looper的消息队列,Handler为啥要这么做,我们继续挖。

2,Handler的真正使命

通过分析Handler源代码,可以发现它的作用就是提供一系列函数,方便我们完成消息的创建封装和插入到消息队列:

  1. //查看消息队列中是否有存货
  2. public final boolean hasMessages(int what) {
  3. return mQueue.removeMessages(this, what, null, false);
  4. }
  5. //创建封装一个消息
  6. public final Message obtainMessage(int what)
  7. {
  8. return Message.obtain(this, what);
  9. }
  10. //发送一个消息,并添加到队列尾
  11. public final boolean sendMessage(Message msg)
  12. {
  13. return sendMessageDelayed(msg, 0);
  14. }
  15. //延时发送一个消息,并添加到队列尾
  16. public final boolean sendMessageDelayed(Message msg, long delayMillis)
  17. {
  18. if (delayMillis < 0) {
  19. delayMillis = 0;
  20. }
  21. return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
  22. }

选其一进行分析,就sendMessageDelayed()了,F3进去:

  1. public boolean sendMessageAtTime(Message msg, long uptimeMillis)
  2. {
  3. boolean sent = false;
  4. MessageQueue queue = mQueue;
  5. if (queue != null) {
  6. //把Message的target设置为自己,然后加入消息队列中
  7. msg.target = this;
  8. sent = queue.enqueueMessage(msg, uptimeMillis);
  9. }
  10. else {
  11. RuntimeException e = new RuntimeException(
  12. this + " sendMessageAtTime() called with no mQueue");
  13. Log.w("Looper", e.getMessage(), e);
  14. }
  15. return sent;
  16. }

以上工作是往Looper的消息队列中加入了一个消息,按照Looper类分析可知,它在获取消息后会调用target的dispatchMessage函数,再把这个消息分发给Handle处理,接着继续看Handler.java:

  1. public void dispatchMessage(Message msg) {
  2. // 1,如果Message本身有callback,则直接交给Message的callback处理
  3. if (msg.callback != null) {
  4. handleCallback(msg);
  5. } else {
  6. // 2,如果本Handle设置了mCallback,则交给它处理
  7. if (mCallback != null) {
  8. if (mCallback.handleMessage(msg)) {
  9. return;
  10. }
  11. }
  12. // 3,啥也没有,就交给子类来处理
  13. handleMessage(msg);
  14. }
  15. }

在绝大多数情况下,我们都是走的第三部流程,用一个从Handler派生的子类并重载handleMessage()来处理各种封装的消息。

小结:1,Looper中有一个Message队列,里面储存的是一个个待处理的Message。

2,Message中有一个Handler,这个Handler是用来处理Message的。

四,实战篇

1,主线程给自己发消息

  1. <span style="font-family:'Microsoft YaHei';font-size:12px;">public class handler1 extends Activity {
  2. private Button btnTest;
  3. private EditText textView;
  4. private final String tag="hyj_handler1";
  5. private Handler handler;
  6. /** Called when the activity is first created. */
  7. @Override
  8. public void onCreate(Bundle savedInstanceState) {
  9. super.onCreate(savedInstanceState);
  10. setContentView(R.layout.main);
  11. btnTest = (Button)this.findViewById(R.id.bt1);
  12. textView = (EditText)this.findViewById(R.id.ed1);
  13. btnTest.setOnClickListener(new View.OnClickListener() {
  14. @Override
  15. public void onClick(View arg0) {
  16. Looper looper = Looper.getMainLooper(); //主线程的Looper对象
  17. //这里以主线程的Looper对象创建了handler,
  18. //所以,这个handler发送的Message会被传递给主线程的MessageQueue。
  19. handler = new MyHandler(looper);
  20. handler.removeMessages(0);
  21. //构建Message对象
  22. //第一个参数:是自己指定的message代号,方便在handler选择性地接收
  23. //第二三个参数没有什么意义
  24. //第四个参数需要封装的对象
  25. Message msg = handler.obtainMessage(1,1,1,"主线程发消息了");
  26. handler.sendMessage(msg); //发送消息
  27. Log.i(tag, looper.toString());
  28. Log.i(tag, msg.toString());
  29. Log.i(tag, handler.toString());
  30. }
  31. });
  32. }
  33. class MyHandler extends Handler{
  34. public MyHandler(Looper looper){
  35. super(looper);
  36. }
  37. public void handleMessage(Message msg){
  38. super.handleMessage(msg);
  39. textView.setText("我是主线程的Handler,收到了消息:"+(String)msg.obj);
  40. }
  41. }
  42. }</span>

2,其他线程给主线程发消息

  1. <span style="font-family:'Microsoft YaHei';font-size:12px;">public class handler2 extends Activity{
  2. private Button btnTest;
  3. private EditText textView;
  4. //      private final String tag="hyj_handler2";
  5. private Handler handler;
  6. /** Called when the activity is first created. */
  7. @Override
  8. public void onCreate(Bundle savedInstanceState) {
  9. super.onCreate(savedInstanceState);
  10. setContentView(R.layout.layout2);
  11. btnTest = (Button)this.findViewById(R.id.bt2);
  12. textView = (EditText)this.findViewById(R.id.ed2);
  13. btnTest.setOnClickListener(new View.OnClickListener() {
  14. @Override
  15. public void onClick(View arg0) {
  16. //可以看出这里启动了一个线程来操作消息的封装和发送的工作
  17. //这样原来主线程的发送就变成了其他线程的发送,简单吧?呵呵
  18. new MyThread().start();
  19. }
  20. });
  21. }
  22. class MyHandler extends Handler{
  23. public MyHandler(Looper looper){
  24. super(looper);
  25. }
  26. public void handleMessage(Message msg){
  27. super.handleMessage(msg);
  28. textView.setText("我是主线程的Handler,收到了消息:"+(String)msg.obj);
  29. }
  30. }
  31. class MyThread extends Thread{
  32. public void run(){
  33. Looper looper = Looper.getMainLooper(); //主线程的Looper对象
  34. //这里以主线程的Looper对象创建了handler,
  35. //所以,这个handler发送的Message会被传递给主线程的MessageQueue。
  36. handler = new MyHandler(looper);
  37. Message msg = handler.obtainMessage(1,1,1,"其他线程发消息了");
  38. handler.sendMessage(msg); //发送消息
  39. }
  40. }
  41. }</span>

3,主线程给其他线程发消息

  1. <span style="font-family:'Microsoft YaHei';font-size:12px;">public class handler3 extends Activity{
  2. private Button btnTest;
  3. private EditText textView;
  4. private Handler handler;
  5. @Override
  6. public void onCreate(Bundle savedInstanceState) {
  7. super.onCreate(savedInstanceState);
  8. setContentView(R.layout.main);
  9. btnTest = (Button)this.findViewById(R.id.bt1);
  10. textView = (EditText)this.findViewById(R.id.ed1);
  11. //启动线程
  12. new MyThread().start();
  13. btnTest.setOnClickListener(new View.OnClickListener() {
  14. @Override
  15. public void onClick(View arg0) {
  16. //这里handler的实例化在线程中
  17. //线程启动的时候就已经实例化了
  18. Message msg = handler.obtainMessage(1,1,1,"主线程发送的消息");
  19. handler.sendMessage(msg);
  20. }
  21. });
  22. }
  23. class MyHandler extends Handler{
  24. public MyHandler(Looper looper){
  25. super(looper);
  26. }
  27. public void handleMessage(Message msg){
  28. super.handleMessage(msg);
  29. textView.setText("我是主线程的Handler,收到了消息:"+(String)msg.obj);
  30. }
  31. }
  32. class MyThread extends Thread{
  33. public void run(){
  34. Looper.prepare(); //创建该线程的Looper对象,用于接收消息
  35. //注意了:这里的handler是定义在主线程中的哦,呵呵,
  36. //前面看到直接使用了handler对象,是不是在找,在什么地方实例化的呢?
  37. //现在看到了吧???呵呵,开始的时候实例化不了,因为该线程的Looper对象
  38. //还不存在呢。现在可以实例化了
  39. //这里Looper.myLooper()获得的就是该线程的Looper对象了
  40. handler = new ThreadHandler(Looper.myLooper());
  41. //这个方法,有疑惑吗?
  42. //其实就是一个循环,循环从MessageQueue中取消息。
  43. //不经常去看看,你怎么知道你有新消息呢???
  44. Looper.loop();
  45. }
  46. //定义线程类中的消息处理类
  47. class ThreadHandler extends Handler{
  48. public ThreadHandler(Looper looper){
  49. super(looper);
  50. }
  51. public void handleMessage(Message msg){
  52. //这里对该线程中的MessageQueue中的Message进行处理
  53. //这里我们再返回给主线程一个消息
  54. handler = new MyHandler(Looper.getMainLooper());
  55. Message msg2 = handler.obtainMessage(1,1,1,"子线程收到:"+(String)msg.obj);
  56. handler.sendMessage(msg2);
  57. }
  58. }
  59. }</span>

4,其他线程给其自己发消息

  1. <span style="font-family:'Microsoft YaHei';font-size:12px;">public class handler4 extends Activity{
  2. Button bt1 ;
  3. EditText et1;
  4. Handler handler;
  5. public void onCreate(Bundle savedInstanceState){
  6. super.onCreate(savedInstanceState);
  7. setContentView(R.layout.main);
  8. bt1 = (Button)findViewById(R.id.bt1);
  9. et1 = (EditText)findViewById(R.id.ed1);
  10. bt1.setOnClickListener(new View.OnClickListener() {
  11. @Override
  12. public void onClick(View v) {
  13. new ThreadHandler().start();
  14. }
  15. });
  16. }
  17. class MyHandler extends Handler{
  18. public MyHandler(Looper looper){
  19. super(looper);
  20. }
  21. public void handleMessage(Message msg){
  22. super.handleMessage(msg);
  23. et1.setText("我是主线程的Handler,收到了消息:"+(String)msg.obj);
  24. }
  25. }
  26. public class ThreadHandler extends Thread{
  27. public void run(){
  28. Looper.prepare();
  29. handler = new Handlerthread(Looper.myLooper());
  30. Message msg = handler.obtainMessage(1,1,1,"我自己");
  31. handler.sendMessage(msg);
  32. Looper.loop();
  33. }
  34. //定义线程类中的消息处理类
  35. public class Handlerthread extends Handler{
  36. public Handlerthread(Looper looper){
  37. super(looper);
  38. }
  39. public void handleMessage(Message msg){
  40. //这里对该线程中的MessageQueue中的Message进行处理
  41. //这里我们再返回给主线程一个消息
  42. //加入判断看看是不是该线程自己发的信息
  43. if(msg.what == 1 && msg.obj.equals("我自己")){
  44. handler = new MyHandler(Looper.getMainLooper());
  45. Message msg2 = handler.obtainMessage(1,1,1,"禀告主线程:我收到了自己发给自己的Message");
  46. handler.sendMessage(msg2);
  47. //et1.setText("++++++threadhandler"+(String)msg.obj);
  48. }}
  49. }
  50. }
  51. </span>

深入理解之 Android Handler的更多相关文章

  1. 【转】Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38377229 ,本文出自[张鸿洋的博客] 很多人面试肯定都被问到过,请问Andr ...

  2. [Android]Handler的消息机制

    最经面试中,技术面试中有一个是Handler的消息机制,细细想想,我经常用到的Handler无非是在主线程(或者说Activity)新建一个Handler对象,另外一个Thread是异步加载数据,同时 ...

  3. [转]android Handler使用

    转 http://blog.csdn.net/new_abc/article/details/8184634 不过这个我看不懂 不知道为什么i的值可以接着增长... package com.examp ...

  4. Android Handler 异步消息处理机制的妙用 创建强大的图片加载类(转)

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38476887 ,本文出自[张鸿洋的博客] 最近创建了一个群,方便大家交流,群号: ...

  5. 源码分析Android Handler是如何实现线程间通信的

    源码分析Android Handler是如何实现线程间通信的 Handler作为Android消息通信的基础,它的使用是每一个开发者都必须掌握的.开发者从一开始就被告知必须在主线程中进行UI操作.但H ...

  6. Android Handler 机制总结

    写 Handler 原理的文章很多,就不重复写了,写不出啥新花样.这篇文章的主要是对 handler 原理的总结. 1.Android消息机制是什么? Android消息机制 主要指 Handler ...

  7. Android Handler 异步消息处理机制的妙用 创建强大的图片载入类

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38476887 ,本文出自[张鸿洋的博客] 近期创建了一个群.方便大家交流,群号: ...

  8. Android Handler学习笔记

    已经习惯了挖坑不填,继续任性一下,周一到周五继续挖坑,每周六周日负责填坑. 1.从Android UI线程谈起 出于性能考虑,Android 中的UI操作并不是线程安全的,所以Android中规定只能 ...

  9. Android Handler leak 分析及解决办法

    In Android, Handler classes should be static or leaks might occur, Messages enqueued on the applicat ...

随机推荐

  1. [ Python ] unittest demo

    # -*- coding: utf-8 -*- import unittest class MyUT(unittest.TestCase): def test_1(self): print(" ...

  2. 文件上传中UUID的解读

    UUID简介如下:1.简介UUID含义是通用唯一识别码 (Universally Unique Identifier),这 是一个软件建构的标准,也是被开源软件基金会 (Open Software F ...

  3. Shell 文本处理命令

    命令:cut –d’:’ -f1, 文件名 #切割处文件列的参数. -d切割字符. -f列的第几个参数. -c1-10指定字符串范围行的第一个到第十个. 命令:sort 文件名 #根据第一列第一个字符 ...

  4. scp传输文件,自动填充密码

    一个偷懒的小shell, #!/usr/bin/expect #******************************************************************** ...

  5. SSM-网站后台管理系统制作(1)

    好久没写博客了,忙于考试和项目答辩,今天整理一下想弄的SSM:本人想做的是博客管理平台,和博客园,CSDN,stackoverflow这些类似. 老师先让做的是后台管理系统,先给出来吧. (讲解内容: ...

  6. hdu 5382 GCD?LCM! - 莫比乌斯反演

    题目传送门 传送门I 传送门II 题目大意 设$F(n) = \sum_{i = 1}^{n}\sum_{j = 1}^{n}\left [ [i, j] + (i, j) \geqslant n \ ...

  7. C# winform 选择文件保存路径

    1.winform 点击按钮选择文件保存的路径,效果如下图: 具体代码如下: private void button8_Click(object sender, EventArgs e) { Fold ...

  8. css技巧小计

    今天又学到两招,一招是把大框写相对定位,然后小框写绝对定位,运用top和left,想去哪里去哪里 另一招是边框渐变色,微信小程序,边框写渐变我没成功,然后大佬支招写一个大框,相对定位,然后设背景渐变色 ...

  9. 本地构建:Gulp

    Gulp中文网:https://www.gulpjs.com.cn/ Gulp英文网:https://gulpjs.com/ Gulp:工作流程自动化+强化 (一)安装初始化 (二)基础API及插件 ...

  10. Resnet-34框架

    import torch import torch.nn as nn import torch.nn.functional as F class ResidualBlock(nn.Module): ' ...