Handler

本文详细介绍了Android中的Handler、Looper、Message和MessageQueue等概念,以及它们如何支持线程间通信,特别强调了如何在UI线程安全的前提下使用Handler进行跨线程操作。还探讨了为何选择Handler而不是Binder/Socket进行通信,以及Handler的工作原理。
摘要由CSDN通过智能技术生成

1. 简介

Handler是一套 Android 消息传递机制,主要用于线程间通信。

handler其实就是主线程在起了一个子线程,子线程运行并生成Message,Looper获取message并传递给Handler,Handler逐个获取子线程中的Message.

Binder/Socket用于进程间通信,而Handler消息机制用于同进程的线程间通信

在实际开发中,为了UI操作的线程安全,规定只允许UI线程更新Activity里的组件。因此,使用Handler消息传递机制,即工作线程需要更新UI时,通过Handler通知UI主线程,从而在主线程更新UI操作。

img

2. 相关概念解释

概念定义作用备注
主线程
(UI线程、Main Thread)
app第一次启动时,会自动开启一条主线程处理UI事件
(如更新、操作等)
子线程
(工作线程)
人为手动开启的线程执行耗时操作
(如网络请求、数据加载等)
消息
(Message)
线程间通讯的数据单元
(即Handler接受&处理的消息对象)
存储需操作的通信信息/
消息队列
(Message Queue)
存储Handler发送过来的消息(Message)/
处理者
(Handler)
主线程和子线程的通信媒介
线程消息的主要处理者
添加消息(Message)到消息队列(Message Queue)
处理循环器(Looper)分派过来的消息(Message)
/
循环器
(Looper)
消息队列(Message Queue)和处理者(Handler)的通信媒介消息循环,即
消息获取:循环取出消息队列(Message Queue)的消息(Message)
消息分发:将取出的消息(Message)发送给对应的处理者(Handler)
每个线程中只能拥有一个Looper
一个Looper可绑定多个线程的Handler
即:多个线程可往1个Looper所持有的Message Queue中发送消息,提供了线程间通信的可能

2.1 Android消息机制

img

2.2 Handler示意图

img

2.3 消息机制的模型

  • Message:需要传递的消息,可以传递数据;
  • MessageQueue:消息队列,但是它的内部实现并不是用的队列,实际上是通过一个单链表的数据结构来维护消息列表,因为单链表在插入和删除上比较有优势。主要功能向消息池投递消息(MessageQueue.enqueueMessage)和取走消息池的消息(MessageQueue.next);
  • Handler:消息辅助类,主要功能向消息池发送各种消息事件(Handler.sendMessage)和处理相应消息事件(Handler.handleMessage);
  • Looper:不断循环执行(Looper.loop),从MessageQueue中读取消息,按分发机制将消息分发给目标处理者。

3. Handler的基本使用

3.1 创建Handler

由于Java特性,内部类会持有外部类,Activity会被Handler持有,如果Handler允许发送延时消息、而如果在延时期间用户关闭了Activity,那么Activity会泄露。

**解决方案:**将 Handler 定义成静态的内部类,在内部持有 Activity 的弱引用,并及时移除所有消息。

public class HandlerActivity extends AppCompatActivity {

    private Button bt_handler_send;

    private static class MyHandler extends Handler {

        //弱引用持有HandlerActivity , GC 回收时会被回收掉
        private WeakReference<HandlerActivity> weakReference;

        public MyHandler(HandlerActivity activity) {
            this.weakReference = new WeakReference(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            HandlerActivity activity = weakReference.get();
            super.handleMessage(msg);
            if (null != activity) {
                //执行业务逻辑
                Toast.makeText(activity,"handleMessage",Toast.LENGTH_SHORT).show();
            }
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.HandlerActivity);

        //创建 Handler
        final MyHandler handler = new MyHandler(this);

        bt_handler_send = findViewById(R.id.bt_handler_send);
        bt_handler_send.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        //使用 handler 发送空消息
                        handler.sendEmptyMessage(0);

                    }
                }).start();
            }
        });
    }

    @Override
    protected void onDestroy() {
        //移除所有回调及消息
        myHandler.removeCallbacksAndMessages(null);
        super.onDestroy();
    }
}

3.2 Message获取

获取 Message 大概有如下几种方式:

Message message = myHandler.obtainMessage(); 		   //通过 Handler 实例获取
Message message1 = Message.obtain();   			      //通过 Message 获取
Message message2 = new Message();      				 //直接创建新的 Message 实例
// 为了节省开销,我们在使用的时候尽量复用 Message,使用前两种方式进行创建。
// 通过查看源码可知,Handler 的 obtainMessage() 方法也是调用了 Message 的 obtain() 方法
public final Message obtainMessage()
{
    return Message.obtain(this);
}

3.3 Handler发送消息

Handler 提供了一些列的方法让我们来发送消息,如 send()系列 、post()系列(需要传入一个Runnalbe对象)

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

不过不管我们调用什么方法,最终都会走到 MessageQueue.enqueueMessage(Message,long) 方法。
sendEmptyMessage(int) 方法为例:

//Handler
sendEmptyMessage(int)
    -> sendEmptyMessageDelayed(int,int)
    -> sendMessageAtTime(Message,long)
    -> enqueueMessage(MessageQueue,Message,long)
    -> queue.enqueueMessage(Message, long);

4. 两个实例(主线程-子线程)

4.1 子线程向主线程

首先我们在MainActivity中添加一个静态内部类,并重写其handleMessage方法。

private static class MyHandler extends Handler {
    // mTarget持有对MainActivity的弱引用
    // 允许你持有一个对象的引用,但不会阻止该对象对被GC回收
    private final WeakReference<MainActivity> mTarget;

    public MyHandler(MainActivity activity) {
        mTarget = new WeakReference<MainActivity>(activity);
    }

    @Override
    public void handleMessage(@NonNull Message msg) {
        super.handleMessage(msg);
        HandlerActivity activity = mTarget.get();
        // 如果从弱引用中得到的activity不为null(意味着它还没有被垃圾收集),那么会进行一些业务逻辑的处理。
        if (null != activity) {
            //执行业务逻辑
            if (msg.what == 0) {
                Log.e("myhandler", "change textview");
                MainActivity ma = mTarget.get();
                ma.textView.setText("hahah");
            }
            Toast.makeText(activity,"handleMessage",Toast.LENGTH_SHORT).show();
        }

    }
}

然后创建一个类型为MyHandler的私有属性:

 private Handler handler1 = new MyHandler(this);

最后在onCreate回调中创建一个线程,用于接收发送消息:

new Thread(new Runnable() {
    @Override
    public void run() {
        handler1.sendEmptyMessage(0);
    }
}).start();

总结如下:

new Thread(new Runnable() {
    @Override
    public void run() {
        handler1.sendEmptyMessage(0);
    }
}).start();

4.2 主线程向子线程

首先创建一个MyHandler类。

private static class MyHandler extends Handler {

    @Override
    public void handleMessage(@NonNull Message msg) {
        super.handleMessage(msg);
        if (msg.what == 0) {
            Log.e("child thread", "receive msg from main thread");
        }
    }
}

声明一个Handler类型的私有变量,进行默认初始化为null。

 private Handler handler1;

创建子线程,使handler指向新创建的MyHandler对象。

new Thread(new Runnable() {
    @Override
    public void run() {
        Looper.prepare();
        handler1 = new MyHandler();
        Looper.loop();
        Log.e("child thread", "child thread end");
    }
}).start();

在主线程中向子线程中发送消息

while (handler1 == null) {

}

handler1.sendEmptyMessage(0);
handler1.getLooper().quitSafely();

5. Handler机制原理

5.1 Handler原理

普通的线程是没有looper的,如果需要looper对象,那么必须要先调用Looper.prepare方法,并且一个线程只能有一个looper

Handler是如何完成跨线程通信的?

Android采用管道通信,所谓“管道”,其实就是一个文件,管道的两端是对应同一个文件的文件描述符(分别用于读写)。

在这里插入图片描述

  • Handler通过sendMessage()发送Message到MessageQueue队列
  • Looper通过loop(),不断提取出达到触发条件的Message,并将Message交给target来处理
  • 经过dispatchMessage()后,交回给Handler的handleMessage()来进行相应地处理
  • 将Message加入MessageQueue时,处往管道写入字符,可以会唤醒loop线程;如果MessageQueue中- 没有Message,并处于Idle状态,则会执行IdelHandler接口中的方法,往往用于做一些清理性地工作

为什么采用管道而非Binder?

  • 从内存角度:通信过程中Binder还涉及一次内存拷贝,handler机制中的Message根本不需要拷贝,本身就是在同一个内存。Handler需要的仅仅是告诉另一个线程数据有了。
  • 从CPU角度:为了Binder通信底层驱动还需要为何一个binder线程池,每次通信涉及binder线程的创建和内存分配等比较浪费CPU资源。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kopppoooooo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值