Handler学习笔记

2018-05-14  本文已影响33人  kirito0424

目录

学习目录

1. Handler的作用

2. 为什么需要Handler?

// Looper.loop轮询方法
public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        // 获取MessageQueue
        final MessageQueue queue = me.mQueue;
        // 省略
        // 具体的轮询逻辑,无限for循环
        for (;;) {
        // 取出Message
            Message msg = queue.next(); // might block
            // 省略
            try {
            // target为发送message的Handler实例
            // Handler处理
                msg.target.dispatchMessage(msg);
            } 
            // 省略
        }
    }

所以说,引入Handler只是为了大家使用方便以及代码的清晰简洁。并没有大家想的那么高深。


3. 具体的使用

3.1 主线程使用Handler刷新UI

Handler handler = new Handler()

实际会调用

 public Handler(Callback callback, boolean async) {
        // 省略
        // 这里也验证了,Handler在哪个线程创建,他就会持有哪个线程的Looper
        // 我们一般在UI线程初始化,Handler就会持有UI线程的Looper和MessageQueue
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        // 这里也验证了,Handler在哪个线程创建,他就会持有哪个线程的MessageQueue
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

在其他线程创建想在主线程处理事情的Handler,可以用以下代码可以达到相应效果

// 传入UI线程的Looper
Handler handler = new Handler(Looper.getMainLooper);

3.2 LooperThread子线程使用(官网的文档)

class LooperThread extends Thread {
       //其他线程可以通过mHandler这个引用给该线程的消息队列添加消息
       public Handler mHandler;
       public void run() {
            Looper.prepare();
            //需要在线程进入死循环之前,创建一个Handler实例供外界线程给自己发消息
            mHandler = new Handler() {
                public void handleMessage(Message msg) {
                    //Handler 对象在这个线程构建,那么handleMessage的方法就在这个线程执行
                }
            };
            // loop方法里会用到Handler实例
            // 所以必须先初始化Handler
            // 如果在loop方法之后初始化Handler,那么loop方法执行中会报错
            Looper.loop();
            // loop之后才初始化Handler,代码是无效的,loop是死循环,正常情况下这行代码就不会执行了
            // mHandler = new Handler()......
        }
    }

需要说明的是,上面写到的Looper.prepare,创建Handler和Looper.loop方法的顺序并不一定不能改。如果你想的话,也完全可以loop执行之后创建Handler,只是创建的流程不能写在loop后面。因为loop里的死循环会导致你的代码不执行,你可以在主线程通过LooperThread.mHander这样的引用,来创建实例,效果也是一样的。

注意,其实UI线程也有类似的代码,如下:

public final class ActivityThread {
    public static final void main(String[] args) {
        ......
        Looper.prepareMainLooper();
        ......
        ActivityThread thread = new ActivityThread();
        thread.attach(false);

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

与上面的例子类似,系统在这里也为我们初始化了一个Handler。我们每次使用Handler mHandler = new Handler();都是额外创建了一个Handler,与原有的不冲突。msg.target.dispatchMessage(msg)这句代码会判断target。


4.Handler发送消息的两种方式

Handler,它直接继承自Object,一个Handler允许发送和处理Runnable或者Message对象,并且会关联到主线程的MessageQueue中。所以Handler把消息压入MessageQueue也有两种方式,post(new Runnable)和sendMessage(Message msg)。

4.1 post

post允许把一个Runnable对象入队到消息队列中。它的方法有:

4.2 sendMessage

sendMessage允许把一个包含消息数据的Message对象压入到消息队列中。它的方法有:

从上面的各种方法可以看出,不管是post还是sendMessage都具有多种方法,它们可以设定Runnable对象和Message对象被入队到消息队列中,是立即执行还是延迟执行。

4.3 post和sendMessage方法的联系和区别

先看源码

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;
}

代码很好理解,post方法其实还是把Runnable对象转化成了Message,区别在于普通的sendMessage不会使用callBack参数,它具体的处理逻辑在Handler的handleMessage里。而post会使用Message的callback,callback就是Runnable对象,所以使用post方法的话,无需再去写具体的handleMessage逻辑。源码如下:

// dispatchMessage方法是在Looper.loop开启循环,开始处理MessageQueue里的每个Message时调用的,可以发现,默认先调用callback,没有callback才会使用handleMessage
public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {                        
            return;
            }
        }
        handleMessage(msg); 
    }
}

5. 了解Message

Message是一个final类,所以不可被继承。Message封装了线程中传递的消息,如果对于一般的数据,Message提供了getData()和setData()方法来获取与设置数据,其中操作的数据是一个Bundle对象,这个Bundle对象提供一系列的getXxx()和setXxx()方法用于传递基本数据类型的键值对,对于基本数据类型,使用起来很简单,这里不再详细讲解。而对于复杂的数据类型,如一个对象的传递就要相对复杂一些。在Bundle中提供了两个方法,专门用来传递对象的,但是这两个方法也有相应的限制,需要实现特定的接口,当然,一些Android自带的类,其实已经实现了这两个接口中的某一个,可以直接使用。方法如下:

putParcelable(String key,Parcelable value):需要传递的对象类实现Parcelable接口。

pubSerializable(String key,Serializable value):需要传递的对象类实现Serializable接口。

还有另外一种方式在Message中传递对象,那就是使用Message自带的obj属性传值,它是一个Object类型,所以可以传递任意类型的对象,Message自带的有如下几个属性:

int arg1:参数一,用于传递不复杂的数据,复杂数据使用setData()传递。

int arg2:参数二,用于传递不复杂的数据,复杂数据使用setData()传递。

Object obj:传递一个任意的对象。

int what:定义的消息码,一般用于设定消息的标志。

注意

对于Message对象,一般并不推荐直接使用它的构造方法得到,而是建议通过使用Message.obtain()这个静态的方法或者Handler.obtainMessage()获取。Message.obtain()会从消息池中获取一个Message对象,如果消息池中是空的,才会使用构造方法实例化一个新Message,这样有利于消息资源的利用。并不需要担心消息池中的消息过多,它是有上限的,上限为10个。Handler.obtainMessage()具有多个重载方法,如果查看源码,会发现其实Handler.obtainMessage()在内部也是调用的Message.obtain()。

上一篇 下一篇

猜你喜欢

热点阅读