Android:关于 Handler 消息传递机制

news/2024/7/7 5:31:17 标签: android, handler, 消息传递

文章目录

  • 写在前面
  • 用法
    • 第一种方法
    • 第二种方法
    • 第三种方法
    • 用法说明
      • 为什么第一种方法和第二种方法是一样的?
      • 第三种是标准写法
  • 消息传递机制
  • Handler
    • 关于 post 和 send
  • Looper
  • MessageQueue
  • ThreadLocal
  • 小插曲
      • 那么 Runnable 究竟是什么?
  • 参考

写在前面

这一篇主要是对Android 的消息机制做一个总结。

消息传递机制里,Android提供了 Handler 来作为对线程里的消息队列里信息进行传递和处理的方法,所以并不是说 Handler 就是用来更新主线程里的 UI,只是我们大多时候是用来在这么做。

而且在 Handler 的介绍里,并没有说作用就是用来更新主线程 UI:

A Handler allows you to send and process {@link Message} and Runnable objects associated with a thread’s {@link MessageQueue}.

它主要的工作就是分发Message 和 Runnable (实际还是封装成 Message) 到消息队列里,然后当它们从消息队列里出来的时候执行它们。

一说到这个就离不开三个关键词:Handler,MessageQueue 和 Looper。关于这三者的内容和关系不少,但我们先从用法开始。


用法

常见的用法有几种:

第一种方法

Handler handler = new Handler(new Handler.Callback() {
                @Override
                public boolean handleMessage(Message msg) {

                    tv.setText(msg.what + "");
                    return false;
                }
            });

private Thread thread = new Thread(new Runnable() {
        @Override
        public void run() {
            Message message = new Message();
            message.what = 1;           
            handler.sendMessage(message);
        }
    });   

第二种方法

    private Thread thread = new Thread(new Runnable() {
        @Override
        public void run() {
           Handler handler = new Handler(Looper.getMainLooper() , 
           new Handler.Callback() {
                @Override
                public boolean handleMessage(Message msg) {

                    tv.setText(msg.what + "");
                    return false;
                }
            });

            Message message = new Message();
            message.what = 1;
            handler.sendMessage(message);
        }
    });

第三种方法


 private Thread thread = new Thread(new Runnable() {
        @Override
        public void run() {
            Looper.prepare();
            Handler handler = new Handler( 
            new Handler.Callback() {
                @Override
                public boolean handleMessage(Message msg) {

                    //做操作
                    return false;
                }
            });

            Message message = new Message();
            message.what = 1;
            handler.sendMessage(message);
            
            Looper.loop();
        }
    });

用法说明

为什么第一种方法和第二种方法是一样的?

第一种和第二种方法是一样的,主要的区别是在于 Handler 初始化时的位置不同而导致的不同。

因为 Handler 的构造方法里会需要指定一个 Looper,默认是当前线程里的 Looper:

public Handler(Callback callback, boolean async) {
      //省略其它代码
      //关注这里
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
      //省略其它代码
    }

    /**
     * Return the Looper object associated with the current thread.  
     * Returns null if the calling thread is not associated with a Looper.
     */
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

在第一种方法里,像有时我们在 Activity 里,之所以不用指定 Looper,是因为我们这样写就表示是默认使用主线程ActivityThread的 Looper (在这里被称为 MainLooper)了,这个可以在 ActivityThread 的源码里看到:

//ActivityThread.java

 public static void main(String[] args) {
        //省略其它代码...

        Looper.prepareMainLooper();

        //省略其它代码...
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

而在第二种方法里,你不写Looper.getMainLooper()的话,就默认是当前子线程,那么运行起来你其实是更新不了 UI 的。

第三种是标准写法

第三种是最原本的标准用法,要在用法上完整的使用 Handler,那么就需要

  1. 有一个线程
  2. 准备Looper.prepare()
  3. 初始化 Handler,(顺便把对消息回调的方法也实现了)
  4. Looper.loop();
  5. Handler 发送消息

第3点就是前面说的,Handler 是用来针对线程里的消息队列的,在初始化的时候需要有 Looper 参数,而这个 Looper 参数初始化的时候是获取当前线程,也就是说要想知道 Handler 是针对哪个线程的,就看 Looper 是哪里的。


消息传递机制

Handler 会投递(send or post方法 )信息(Messasge or Runnable)到消息队列(MessageQueue)里,Looper 会从消息队列里取出消息,然后再将消息发给 Handler的回调(Callback)或是执行 Runnable 的 run 方法。


Handler

关于 post 和 send

这里有个地方我们要注意,就是 Handler 的 send 和 post 方法,通过源码的分析,我们可以知道,post 一个 Runnable 和 send一个 Message最后调用的方法都是一样的,即sendMessageAtTime(Message , long)

当使用 post 方法时,它会把 Runnable 封装进 Message 里,因为 Message 里有个 Callback 的成员变量,就是 Runnable 类型,所以最后都可以当做 Message 来看待。

 public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

那么 send 一个 Message 的时候,我们知道有这样的回调Handler.Callback:

 Handler handler = new Handler( 
            new Handler.Callback() {
                @Override
                public boolean handleMessage(Message msg) {

                    //做操作
                    return false;
                }
            });

而 post 一个 Runnable 的时候,就是执行 Runnable里 的 run 方法了,所以它是先 post进队列,然后等队列里出来后,才 run,相当于上面的回调。

//这个相当于在子线程里进行个计时器的操作
handler.postDelayed(new Runnable() {
                @Override
                public void run() {

                }
            } , 100);

从最后Handler 对消息的分发方法dispatchMessage(Message)也可以看出来。

 public void dispatchMessage(Message msg) {
        //使用 post 方法时,就执行 Runnable 里的 run
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            //使用 send 方法,就回调
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

private static void handleCallback(Message message) {
        message.callback.run();
    }

另外由于 post 和 send 两个方法的返回值是 boolean 类型,所以也可以让我们来判断是否成功加入队列。

Looper

Looper 就是对消息队列进行循环,MessageQueue 是它的一个成员变量,所以它可以在自己的loop()方法里,对 MessageQueue 进行遍历操作。

loop()方法是开启循环的操作,然后我们可以调用quit()或是quitSafety()方法来退出Looper。这两个方法最后都是调用了MessageQueue 的quit(boolean safe)方法。

区别在于quit()是直接清空消息队列,而quitSafety()是只清空那些延时的信息(即 postDelay),然后分发那些即时的消息。

我们可以看下最后调用的MessageQueue 的quit(boolean safe)方法:

void quit(boolean safe) {
        if (!mQuitAllowed) {
            throw new IllegalStateException("Main thread not allowed to quit.");
        }

        synchronized (this) {
            if (mQuitting) {
                return;
            }
            mQuitting = true;

            if (safe) {
                //关注这个
                removeAllFutureMessagesLocked();
            } else {
                //关注这个
                removeAllMessagesLocked();
            }

            // We can assume mPtr != 0 because mQuitting was previously false.
            nativeWake(mPtr);
        }
    }

 private void removeAllMessagesLocked() {
        Message p = mMessages;
        while (p != null) {
            Message n = p.next;
            p.recycleUnchecked();
            p = n;
        }
        mMessages = null;
    }

    private void removeAllFutureMessagesLocked() {
        final long now = SystemClock.uptimeMillis();
        Message p = mMessages;
        if (p != null) {
            //如果超过了当前时间,即表示是要延时发送的消息,那就都清掉
            if (p.when > now) {
                removeAllMessagesLocked();
            } else {
                Message n;
                for (;;) {
                    n = p.next;
                    if (n == null) {
                        return;
                    }
                    if (n.when > now) {
                        break;
                    }
                    p = n;
                }
                p.next = null;
                do {
                    p = n;
                    n = p.next;
                    p.recycleUnchecked();
                } while (n != null);
            }
        }
    }

MessageQueue

一个用链表实现的消息队列,主要包含插入(enqueueMessage)和读取(next)两个操作,在读取的时候,还包含将该消息进行删除。

ThreadLocal

ThreadLocal 是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储以后,只有在指定线程中可以获取到存储的数据,对于其它线程来说则无法获取到数据。

在新的版本里,ThreadLocal 的 get 和 set 方法从原先的数组索引里取值,变成了从一个自定义的 hashmap ——ThreadLocalMap里取值。

ThreadLocal 的 set 方法会获取当前线程的 value 值,没有的话则对其初始化。然后会使用 ThreadLocalMap (一个自定义的 hashmap)来保存(在以前的版本里是用了一个数组,然后 i 的值为 key,i+1的值为 value。现在改成了用 Entry 类型的数组来保存,即一个 Entry 包含了 key 和 value)。

小插曲

经常看到 Thread 和 Runnable 在一起的操作,但现在我们在 Handler,Message 也看到Runnable 的出现。

那么 Runnable 究竟是什么?

Runnable 是个接口,按照规定,当一个类要想实现这个接口,就代表着它的实例是想在线程里被执行,所以 Runnable 的接口里会有个 run 方法。

它被设计于用来说当这个对象想在活跃状态时运行自己那部分的代码,例如Thread 这个类就是这样,活跃状态的意思就是已经开始但还没结束。但其实它不是仅限于在 Thread 里的。

参考

《Android 开发艺术探索》


http://www.niftyadmin.cn/n/1149491.html

相关文章

关于幂等

最近看了个词觉得有点意思,叫幂等。 概念 幂等(idempotent、idempotence)是一个数学与计算机学概念,常见于抽象代数中。 在编程中一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。 幂等函数&#xff…

ClassLoader 类加载

2019独角兽企业重金招聘Python工程师标准>>> 类加载 参考链接 Java ClassLoader 转载于:https://my.oschina.net/u/3421984/blog/1611449

Android:Android系统启动(笔记)

init 进程启动 init 进程是 Android 系统中用户控件的第一个进程,进程号为 1,是 Android 系统系统流程中一个关键的步骤,作为第一个进程,它被赋予了很多极其重要的工作职责,比如创建 Zygote(孵化器&#x…

第6章 循环

6.1 range 函数用来创建一个数字列表,它的范围是从起始数字开始到结束数字之前 1 >>> for x in range(0,5): 2 print(Hello %s % x) 3 4 Hello 0 5 Hello 1 6 Hello 2 7 Hello 3 8 Hello 4 1 >>> print(list(range(10,20))) 2 [10, 11, 12, …

一些方案的处理

列表请求分页重复 问题:请求了第一页的数据,在请求第二页的时候,可能出现已经有新数据,导致第二页的数据与第一页有重复。 方案:给item项加一个id,然后请求第二页时携带第一页最后一个item项的id&#xf…

Flutter里的一些小问题

GridView,ListView在全面屏里可能会出现顶部有空白,在它们里面的padding属性里设置top为0GridView里要自己设置item的大小,需要设置其AspectRatio属性GestureDector里的点击行为添加 behavior: HitTestBehavior.opaque,可以使其透…

SCRUM的四大支柱

转自:http://www.scrumcn.com/agile/scrum-knowledge-library/scrum.html#tab-id-9 迭代开发 在Scrum的开发模式下,我们将开发周期分成多个1-4周的迭代,每个迭代都交付一些增量的可工作的功能。迭代的长度是固定的,如果我们选择了…

Flutter : 一个简单的九宫格抽奖实现

写在前面 用Flutter实现一个简单的九宫格抽奖 步骤 实现一个九宫格的页面实现一个控制器用于控制抽奖开始实现抽奖的动画 过程 定义数据类型 class SimpleLotteryValue {SimpleLotteryValue({this.target 0, this.isFinish false, this.isPlaying false});/// 中奖目…