Handler浅析——学习笔记

来源:互联网 发布:sql中union的用法 编辑:程序博客网 时间:2024/06/07 07:14

1、概述

  • handler是android给我们提供用来更新UI的一套机制,也是一套消息处理的机制,我们可以发送消息,也可以通过它处理消息。

2、常见异常

  • 在子线程中进行更新UI操作会抛出
    android.view.ViewRootImpi$CalledFromWrongThreadException
    解决办法:子线程发消息给主线程handler去修改UI

  • 该异常发生在子线程创建Handler时,因为handler需要绑定一个Looper,主线程默认创建了一个Looper所以直接创建handler不会报错,子线程不会默认创建Looper,所以要手动创建出Looper
    Can’t create handler inside thread that has not called Looper.prepare()
    解决办法:子线程创建Handler之前调用Looper.prepare(),之后调用Looper.loop()

3、常见用法

post();postDelayed();sendMessage();sendMessageDelayed();Handler handler = new Handler(new Callback(){    @Override    public boolean handlerMessage(Message msg){        // 这个方法可以截获消息对象,返回值为true,表示截获        return false;    }}){    @Override    public void handlerMessage(Message msg){        // 消息如果没有被截获,这个方法正常处理消息    }}

4、原理

  • android使用handler最根本的目的就是解决多线程并发问题.
    假设如果在一个Activity当中,有多个线程去更新UI,并且都没有加锁机制。就会产生更新界面错乱
    如果对更新UI的操作都进行加锁处理,又会产生性能下降
  • Handler封装了消息的发送(主要包括消息发送给谁)
    Looper
    1.内部包含一个消息队列也就是MessageQueue,所有的Handler发送的消息都走向这个消息队列
    2.Looper.Looper方法,就是一个死循环,不断的从MessageQueue取消息,如果有消息就处理消息,没有就阻塞。
  • MessageQueue,就是一个消息队列,可以添加消息,并处理消息
  • Handler也很简单,内部会跟Looper进行关联,也就是说在Handler的内部可以找到Looper,找到了Looper也就找到了MessageQueue,在Handler中发送消息,其实就是向MessageQueue队列中发送消息。
    handler负责发送消息,Looper负责接收Handler发送的消息,并直接把消息回传给handler自己,MessageQueue就是一个存储消息的容器

5、handler源码主要逻辑

  • 发送消息
handler.sendMessage(msg);
  • 插入消息到MessageQueue中
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {        MessageQueue queue = mQueue;        if (queue == null) {            RuntimeException e = new RuntimeException(                    this + " sendMessageAtTime() called with no mQueue");            Log.w("Looper", e.getMessage(), e);            return false;        }        // 这一步是插入消息到MessageQueue中        return enqueueMessage(queue, msg, uptimeMillis);    }
  • 在插入到队列过程中,设置target为当前handler
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {        msg.target = this;        if (mAsynchronous) {            msg.setAsynchronous(true);        }        return queue.enqueueMessage(msg, uptimeMillis);    }
  • Looper轮询,在Looper.loop()方法中,死循环,从MessageQueue取出消息
public static void loop() {        final Looper me = myLooper();        if (me == null) {            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");        }        final MessageQueue queue = me.mQueue;        Binder.clearCallingIdentity();        final long ident = Binder.clearCallingIdentity();        for (;;) {            Message msg = queue.next(); // might block            if (msg == null) {                return;            }            try {                // target就是发消息的handler                msg.target.dispatchMessage(msg);            } finally {                if (traceTag != 0) {                    Trace.traceEnd(traceTag);                }            }        }    }
  • 跳转到Handler的dispatchMessage方法
public void dispatchMessage(Message msg) {        if (msg.callback != null) {            handleCallback(msg);        } else {            if (mCallback != null) {                if (mCallback.handleMessage(msg)) {                    return;                }            }            // 回调到handlerMessage            handleMessage(msg);        }    }

总的来说

1、首先Looper.prepare()在本线程中保存一个Looper实例,然后该实例中保存一个MessageQueue对象;因为Looper.prepare()在一个线程中只能调用一次,所以MessageQueue在一个线程中只会存在一个。
2、Looper.loop()会让当前线程进入一个无限循环,不端从MessageQueue的实例中读取消息,然后回调msg.target.dispatchMessage(msg)方法。
3、Handler的构造方法,会首先得到当前线程中保存的Looper实例,进而与Looper实例中的MessageQueue想关联。
4、Handler的sendMessage方法,会给msg的target赋值为handler自身,然后加入MessageQueue中。
5、在构造Handler实例时,我们会重写handleMessage方法,也就是msg.target.dispatchMessage(msg)最终调用的方法。

在子线程中创建一个handler

class MyThread extends Thread{        public Handler handler;        @Override        public void run() {            Looper.prepare();            handler = new Handler(){                @Override                public void handleMessage(Message msg) {                    System.out.println("currentThread:" + Thread.currentThread());                }            };            Looper.loop();        }    };