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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,723评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,080评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,604评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,440评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,431评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,499评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,893评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,541评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,751评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,547评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,619评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,320评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,890评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,896评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,137评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,796评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,335评论 2 342

推荐阅读更多精彩内容