消息机制Handler探索

Android中的消息机制指的就是Handler的运行机制,Handler是用来跨线程通信的,为了避免ANR,我们会把耗时操作放到子线程中去执行,因为子线程不能更新UI,这里就要借助到Android的消息机制也就是Handler了,Handler会把相关的UI操作切换到主线程中去执行。

注意:为什么不能在子线程中访问UI?

答:UI控件非线程安全,在多线程中并发访问可能会导致UI控件处于不可预期的状态,而不对UI控件的访问加上锁,那是因为加上锁会让UI控件变得复杂和低效,同时也会阻塞某些进程的执行。

  • Handler的使用
  • Handler的工作原理

一.Handler的使用

1.核心要素

(1)Message:消息,由Handler发送,包括消息ID、消息处理对象以及处理的数据等,由MessageQueue统一列队,最终由Handler处理。

(2)MessageQueue:消息队列,用来存放Handler发送过来的消息,内部由单链表的数据结构来维护数据列表,等待Looper抽取。

(3)Handler:负责消息的发送和处理,通过Handler.sendMessage()向消息池发送各种消息事件,再通过Handler.handleMessage()处理相应的事件。

(4)Looper:轮询器,通过Looper.loop()不断地从MessageQueue中抽取Message,按分发机制将消息分发给目标处理者。

2.Thread和Handler的关系

(1)Thread负责调度整个消息循环,即消息循环的执行场所。

(2)一个Thread只能有一个Looper,可以有多个Handler。

(3)Looper有一个MessageQueue,可以处理来自多个Handler的Message。即MessageQueue中有一组待处理的Message,这些Message可来自不同的Handler。

(4)Message中记录了负责发送和处理消息的Handler。

(5)Handler中有Looper和MessageQueue。

3.具体实现

(1)在主线程实例化一个Handler对象。

(2)在需要更新UI的子线程里实例化Message并填充必要的数据,通过Handler.sendMessage()发送出去。

(3)复写handleMessage()方法,对不同Message执行相关操作。

public class HandlerActivity extends AppCompatActivity {

    private static class MyHandler extends Handler{
        /*
        * 如果直接持有外部类的强引用,可能会导致内存泄露
        * 这里用软引用保存,当内存不足可以被GC回收
        * */
        private WeakReference<HandlerActivity> mHandlerActivity;
        public MyHandler(HandlerActivity activity) {
            this.mHandlerActivity = new WeakReference<HandlerActivity>(activity);
        }

        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            String message  = (String) msg.obj;
            Toast.makeText(mHandlerActivity.get(),message,Toast.LENGTH_SHORT).show();
        }
    }

    private MyHandler myHandler = new MyHandler(this);

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler);
        Button bt = findViewById(R.id.bt);
        bt.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        Message message = new Message();
                        message.obj = "这里是子线程发送的消息";
                        myHandler.sendMessage(message);
                    }
                }).start();
            }
        });
    }
}

二.工作原理

1.工作流程

在这里插入图片描述
(1)Handler.sendMessage()发送消息时,会通过MessageQueue.enqueueMessage()向MessageQueue中添加一条消息。

(2)通过Looper.loop开启循环后,不断轮询调用MessageQueue.next()。

(3)通过调用Handler.dispatchMessage()去传递消息,目标Handler收到消息后回调Handler.handleMessage()处理消息。

2.工作原理

(1)Looper:在主线程中自动创建了Looper,子线程需要手动创建.需要注意的是,无论是主线程还是子线程,Looper只能被创建一次,即一个Thread只有一个Looper.而所创建的Looper会保存在ThreadLocal中,TreadLocal是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储以后,只有在指定线程中可以获取到存储的数据,对于其它线程来说则无法获取到数据,在这里可以通过TreadLocal获取每一个Thread的Looper.

//子线程手动创建Looper
 new Thread(new Runnable() {
            @Override
            public void run() {
            
                Looper.prepare();//为子线程创建Looper
                Handler mHandler = new Handler();
                Looper.loop();//开启消息轮询
                
            }
        }).start();

注意:

a.Looper可以通过quit和quitSafely来退出,二者的区别是quit会直接退出,quitSafely只是设定一个退出标记,然后把消息队列中的已有消息处理完毕后才会安全退出.

b.loop方法是一个死循环,唯一跳出循环的方式是MessageQueue的next方法返回了null,当调用了quit和quitsafely方法,next方法就会返回null.

(2)MessageQueue:在Looper的构造函数中创建了一个MessageQueue,它主要包含两个操作,插入和读取,这两个操作分别对应的方法是enqueueMessage和next,enqueueMessage的作用是往消息队列中插入一条消息,而next的作用是从消息队列中取出一条消息并将其从消息队列中移除,它的内部是通过一个单链表的数据结构来维护消息列表,next方法是一个无限循环的方法,如果消息队列中没有消息,那么next方法会一直阻塞在那里.

 private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
public static void loop() {
 ...
		
        for (;;) {//死循环
            Message msg = queue.next(); // 用于提取下一条消息,当没有处理该Message的Handler时,会一直阻塞
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

       ...
            try {
                msg.target.dispatchMessage(msg);//从MessageQueue中拿到Message,通过和它绑定的Handler将它发送到handleMessage处理
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
        ...
    }

(3)Handler:主要负责消息的发送和接收过程,消息的发送可以通过post的一系列方法和send的一系列方法来实现,post的一系列方法最终是通过send的一系列方法来实现的.

public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }

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

Handler发送消息仅仅是向消息队列中插入了一条消息,MessageQueue的next方法就会返回这条消息给Looper,Looper收到消息后就开始处理了,最终消息由Looper交给Handler处理,即Handler的dispatchMessage方法会被调用,这时Handler就进入了处理消息的阶段.

下面通过一张图整体了解下Handler的运行机制(来源于android的消息机制——Handler机制)

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值