首页 > 编程知识 正文

handler原理和机制,多线程通信都有哪些方式

时间:2023-05-03 10:56:28 阅读:61972 作者:3020

主题: Handler如何进行线程通信? 原理是什么? 首先,我们需要知道这个问题想考察什么。 是否知道Handler的基本用法,是否知道Handler消息机制的执行过程, 考察Handler进行线程通信的原理利用知识点Handler进行线程切换的基本进程Handler消息机制相关的类和关系Handler如何进行线程通信,该如何回答, 首先告诉我Handler机制的执行流程,涉及的类之间的关系Handler消息机制主要涉及四个类: Handler、Looper、MessageQueue、Message

Handler:实际上是指处理器,负责分发和处理消息,内部有looper对象和消息队列对象。 消息队列对象来自looper对象

Looper:字面意思是循环器,它在特定线程中运行,具有消息队列对象,主要是loop )方法循环地从消息队列接收和处理当前线程要处理的消息在我们通常的意义上切换线程,毕竟是切换到了这种方法。 为什么需要loop (循环? 通常,打开新线程。 代码执行结束后线程也结束。 必须打开looper,以便该线程能够继续接收和处理外部发送的消息。

MessageQueue:消息队列(Message存储位置)维护一个按时间优先级排序的单链表,处理程序的消息将在此队列中存储后运行。

Message:消息具有参数和标识符,以及发送该消息的处理程序对象。 post(Runnable )方法最后也更改为消息对象,将runnable分配给消息的回调,并在处理消息时调用。

完整的事件发送和处理流程是: Handler调用sendMessage/post方法,调用所维护的MessageQueue对象的enqueueMessage方法,将消息添加到消息队列中, looper继续通过loop )方法从消息队列中检索消息。调用消息所具有的target(Handler对象的dispatchMessage方法,然后调用Handler对象的dispatchMessage方法要说Handler是如何实现线程通信的,它的原理是什么,我们在创建新的Handler时,会为其构造方法中拥有的mLooper对象赋值,Handler在mLooper中与线程绑定

mLooper对象可以通过构造方法传递,也可以通过构造方法从Looper.myLooper ()方法获取。

//handler.Java public handler (@ nullablecallbackcallback,boolean async ) ) { mLooper=Looper.myLooper; mQueue=mLooper.mQueue; mCallback=callback; mAsynchronous=async; } public handler (@ nonnulllooperlooper,@Nullable Callback callback,boolean async ) ) { mLooper=looper; mQueue=looper.mQueue; mCallback=callback; mAsynchronous=async; //looper.javastaticfinalthreadlocalloopersthreadlocal=newthreadlocallooper (; 公共静态@ nullableloopermylooper () { return sThreadLocal.get; }私有语音更新(布尔队列) if (sthreadlocal.get )!=null(//当前线程已经有looper对象,异常thrownewruntimeexception (' onlyoneloopermaybecreatedperthread ' ); }sthreadlocal.set(newlooper ) quitallowed ); }可以看到}looper对象存储为ThreadLocal。 这意味着每个线程都有自己的looper对象。

线程中的looper对象在调用Looper.prepare ()方法时初始化并赋值。 此外,空判定还确保线程中只有一个looper对象。

注意,主线程中的looper是在创建APP应用程序进程时使用ActivityThread的main )方法创建的,并打开循环。 虽然不需要手动创建,但子线程中的looper必须调用prepare ) )/loop )方法来初始化和打开循环。

//activity thread.javapublicstaticf

inal void main(String[] args) { //主线程, 进程启动时自动调用 Looper.prepareMainLooper() //由这里的loop()方法处理的消息运行在主线程 Looper.loop();}//XXActivity.javanew Thread() { @Override public void run() { super.run(); //子线程, 需手动调用 Looper.prepare(); //由这里的loop()方法处理的消息运行在子线程 Looper.loop(); }}.start();

由此我们知道了Looper.loop()在哪个线程调用,其消息循环就执行在哪个线程中。

而Looper.loop()中干了什么事?就是获取到当前线程的looper对象(怎么获取的?前面说了,ThreadLocal中获取),再拿到其内部MessageQueue对象,从其中不断地取出消息并执行,也就是我们线程代码实际运行的地方。既然loop()中执行的是MessageQueue中的消息,我们不难想到,若想让代码执行在这个某个线程中,肯定是要跟这个线程的MessageQueue发生关系的。

//Handler.javapublic boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) { MessageQueue queue = mQueue; //这里的mQueue即mLooper对象中的MessageQueue if (queue == null) { RuntimeException e = new RuntimeException( this + " sendMessageAtTime() called with no mQueue"); Log.w("Looper", e.getMessage(), e); return false; } return enqueueMessage(queue, msg, uptimeMillis);}private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg, long uptimeMillis) { msg.target = this; msg.workSourceUid = ThreadLocalWorkSource.getUid(); if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); //将Message对象添加到MessageQueue队列中}//Looper.java (此处仅贴出关键代码)public static void loop() { final Looper me = myLooper(); final MessageQueue queue = me.mQueue; for (;;) { Message msg = queue.next(); msg.target.dispatchMessage(msg); //dispatchMessage 会调用到handler的handleMessage方法或者Runnable的run方法 //或者额外传入的Callback的handleMessage方法. }}

可以看到,当(比如子线程中)调用Handler.sendMessage时,会将消息传入该Handler所绑定的mLooper的MessageQueue中,这一步是发送消息的过程。也就是说,消息会从一个线程被发送到另一个线程的消息队列。如果创建Handler时传入的是主线程的looper对象,那么主线程中的Looper.loop()函数就会从这个MessageQueue中取到子线程发来的这个消息并执行。怎么执行呢?就是调用message.target.dispatchMessage() -> handleMessage()方法,而这里的target就是发送该消息的handler。而由于Looper.loop()是在主线程,所以这条消息也就执行在了主线程中。

所以Handler完成线程通信通俗点说原理就是:在A线程中获得绑定了B线程looper的handler的引用,用此handler发送的消息会进入B线程的消息队列,最终会跑在B线程的Looper.loop()方法中,这样就完成了从A线程到B线程的通信。

这里要区分一下线程对象和线程的概念:线程对象就是Thread对象,是虚拟机中实实在在存在的,文章中所说的哪个线程的mLooper对象,MessageQueue对象都是指这个Thread对象所持有的对象;而线程是系统调度的单位,是一段代码的运行过程,通过Thread类的start()方法来真实启动的。所以一个线程中调用Looper.loop()方法,从本线程对象的MessageQueue中拿数据,而这个MessageQueue,其他线程也可以通过Handler往里面发送消息(这里需要加锁来保证线程安全),这样就完成了线程的通信。线程通信本质上是线程间对象或者说内存的共享。

下面以一段代码来更清楚地演示一下从子线程向主线程发送消息以及从主线程向子线程发送消息的过程。

public class MyActivity extends AppCompatActivity { //与主线程绑定的Handler, 此处主动传入looper对象, 与调用默认构造方法效果是一样的 Handler mMainHandler = new MainThreadHandler(Looper.myLooper()); //与子线程绑定的Handler, 需要在子线程中创建, 定义为全局变量以便在主线程中引用 SubThreadHandler mSubHandler; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); new Thread() { @Override public void run() { super.run(); mMainHandler.sendEmptyMessage(0); //子线程发送消息给主线程 Looper.prepare(); //子线程中需要手动初始化looper mSubHandler = new SubThreadHandler(); //初始化子线程Handler Looper.loop(); //开启循环 } }.start(); //主线程发送消息给子线程, 这里延后1秒钟以保证mSubHandler完成初始化 mMainHandler.postDelayed(() -> mSubHandler.sendEmptyMessage(1), 1000); } private static class MainThreadHandler extends Handler { public MainThreadHandler(Looper looper) { super(looper); } @Override public void handleMessage(@NonNull Message msg) { super.handleMessage(msg); //do work in main thread Log.d("HandlerTest", "MainThreadHandler: " + msg.what + ", current thread: " + Thread.currentThread().getName()); } } private static class SubThreadHandler extends Handler { @Override public void handleMessage(@NonNull Message msg) { super.handleMessage(msg); //do work in sub thread Log.d("HandlerTest", "SubThreadHandler: " + msg.what + ", current thread: " + Thread.currentThread().getName()); } }}

执行结果:

10928-10928/com.xx.newtest D/HandlerTest: MainThreadHandler: 0, current thread: main
10928-11722/com.xx.newtest D/HandlerTest: SubThreadHandler: 1, current thread: Thread-4

首先得到的是子线程发送来的消息,执行在主线程;接着得到主线程发送来的消息,执行在子线程。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。