Android 谈谈 Handler 那些事

转载请注明出处
作者:developerHaoz
Github 地址:developerHaoz

本文的主要内容

  • Handler 是什么
  • Handler 的两个体系
  • Message

一、Handler是什么

Handler 是 Android 中引入的一种让开发者参与处理线程中消息循环的机制,Handler直接继承自 Object,每个 Handler 都关联了一个线程,每个线程内部都维护了一个消息队列 MessageQueue,这样 Handler 实际上也就关联了一个消息队列。这样就可以通过 Handler 将 Message 和 Runnable 对象发送到该Handler所关联线程的 MessageQueue(消息队列)中,然后该消息队列一直在循环拿出一个 Message,对其进行处理,处理完之后拿出下一个 Message,继续处理

Handler 可以用来在多线程之间进行通信,在另一个线程中去更新 UI 线程中的 UI 控件只是 Handler 使用中的一种典型案例,除此之外,Handler 还可以做其他很多的事情,Handler 是 Thread 的代言人,是多线程之间通信的桥梁,通过 Handler,我们可以在一个线程中控制另一个线程去做某些事

二、Handler的两个体系

Handler 可以把一个 Message 对象或者 Runnable 对象压入到消息队列中,进而在UI线程中获取Message 或者执行 Runnable 对象,Handler 压入消息队列有两大体系,Post 和 sendMessage

  • Post:Post允许把一个 Runnable 对象入队到消息队列中,它的方法有:post(Runnable)、PostAtTime(Runnable, long)、postDelayed(Runnable, long)
  • sendMessage:sendMessage允许把一个包含消息数据的Message对象压入到消息队列中,它的方法有sendEmptyMessage(int)、sendMessage(Message)、sendMessageAtTime(Message, long)、sendMessageDelayed(Message, long)

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

1、Post

对于 Handler 的 Post 方式来说,它会传递一个 Runnable 对象到消息队列中,在这个 Runnable 对象中,重写 run() 方法,一般是在这个 run() 方法中写入需要在UI线程中的操作

在 Handler 中,关于 Post 方式的方法有

方法名称作用
boolean post(Runnable r)把一个 Runnabled 入队到消息队列中,UI 线程从消息队列中取出这个对象后,立即执行
boolean postAtTime(Runnable r, long uptimeMills)把一个 Runnable 入队到消息队列中,UI线程从消息独立列中取出这个对象后,在特定的时间执行
boolean postDelayed(Runnable r, long delayMills)把一个 Runnable 入队到消息队列中,UI线程从消息队列中能够取出这个对象后,延迟delayMills秒执行
void removeCallbacks(Runnable r)从消息队列中移除一个 Runnable 对象

示例代码

public void onClick() {
    new Thread(new Runnable() {
        @Override
        public void run() {
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                  // 将TextView的内容进行修改
                    mTvShowInfo.setText("This is a test");
                }
            });
        }
    }).start();
}

有一点需要注意的是,对于 Post 方式而言,它其中的 Runnable 对象的 run() 方法的代码,均执行在UI线程上,所以如果是不能在 UI 线程上执行的操作,如网络请求之类的,一样无法使用Post方式执行

2、sendMessage

在Handler中,与Message发送消息相关的方法

方法作用
Message obtainMessage()获取一个Message对象
boolean sendMessage()发送一个Message对象到消息队列中,并在UI线程取到消息之后,立即执行
boolean sendMessageDelayed()发送一个Message对象到消息队列中,在 UI 线程取到消息后,延迟执行
boolean sendEmptyMessage(int what)发送一个空Message对象到消息队列中,并在 UI 线程取到消息之后,立即执行
boolean sendEmptyMessageDelayed(int what)发送一个空Message对象到消息队列中,并在 UI 线程取到消息之后,延迟执行
void removeMessage()从消息队列中移除一个未响应的消息

示例代码

        new Thread(new Runnable() {
            @Override
            public void run() {
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        String testStr = "This is a test";
                        Message message = Message.obtain();
                        message.obj = testStr;
                        mHandler.sendMessage(message);
                    }
                });
            }
        }).start();
    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == RESULT_OK_HANDLER) {
                String infoStr = (String)msg.obj;
                mHandlerTvShowInfo.setText(infoStr);
            }
        }
    };

三、Message

Handler 如果使用 sendMessage 的方式把消息入队到消息队列中,需要传递一个 Message 对象,而在 Handler,需要重写 handleMessage() 方法,用于获取工作线程中传递过来的消息,此方法运行在 UI 线程上

1、获取一个 Message 对象

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

    public final Message obtainMessage()
    {
        return Message.obtain(this);
    }

2、设置、获取和传递数据

Message是一个 final 类,所以不可被继承,Message 封装了线程中传递过来的消息,如果对于一般的数据,Message 提供了 getData() 和 setData 方法来获取和设置数据,其中操作的数据是一个Bundle 对象,这个 Bundle 对象提供一系列的 getXxx() 和 setXxx() 方法用于传递基本数据类型的键值对,使用起来比较简单。

示例代码

new Thread(new Runnable() {
            @Override
            public void run() {
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        String testStr = "This is a test";
                        Message message = Message.obtain();
                        Bundle testBundle = new Bundle();
                        testBundle.putString(KEY_STRING, testStr);
                        message.setData(testBundle);
                        message.what = RESULT_OK_HANDLER;
                        mHandler.sendMessage(message);
                    }
                });
            }
        }).start();
    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == RESULT_OK_HANDLER) {
                String infoStr = msg.getData().getString(KEY_STRING);
                mHandlerTvShowInfo.setText(infoStr);
            }
        }
    };

而对于复杂的数据类型,如一个对象的传递就要相对复杂一些,在 Bundle 中提供了两个方法,专门用来传递对象的,但是这两个方法也有相应的限制,需要实现特定的接口,当然,一些 Android 自带的类,其实已经实现了这两个接口中的某一个,可以直接使用

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

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

属性作用
int arg1参数一,用于传递不复杂的数据,复杂数据用 setData() 传递
int arg2参数二,用于传递不复杂的数据,复杂数据用 setData() 传递
Object obj传递一个任意的对象
Messaenger replyTo是作为线程通信的时候使用
int what定义的消息码,一般用于设定消息的标志,辨别究竟是从哪里中发来的消息

参考:
Android 中 Handler 的使用
Android — 多线程之 Handler

    原文作者:developerHaoz
    原文地址: https://www.jianshu.com/p/6e0f29da8719
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞