千家信息网

Android中Handler,MessageQueue与Looper关系是什么

发表于:2025-01-18 作者:千家信息网编辑
千家信息网最后更新 2025年01月18日,这篇文章主要介绍了Android中Handler,MessageQueue与Looper关系是什么,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起
千家信息网最后更新 2025年01月18日Android中Handler,MessageQueue与Looper关系是什么

这篇文章主要介绍了Android中Handler,MessageQueue与Looper关系是什么,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。

一说到Android的消息机制,自然就会联想到Handler,我们知道Handler是Android消息机制的上层接口,因此我们在开发过程中也只需要和Handler交互即可,很多人认为Handler的作用就是更新UI,这也确实没错,但除了更新UI,Handler其实还有很多其他用途,比如我们需要在子线程进行耗时的I/O操作,可能是读取某些文件或者去访问网络等,当耗时操作完成后我们可能需要在UI上做出相应的改变,但由于Android系统的限制,我们是不能在子线程更新UI控件的,否则就会报异常,这个时候Handler就可以派上用场了,我们可以通过Handler切换到主线程中执行UI更新操作。

下面是Handler一些常用方法:

void handleMessage(Message msg):处理消息的方法,该方法通常会被重写。

final boolean hasMessages(int what):检测消息队列中是否包含what属性为指定值的消息。

Message obtainMessage():获取消息的方法,此函数有多个重载方法。

sendEmptyMessage(int what):发送空消息。

final boolean sendEmptyMessageDelayed(int what , long delayMillis):指定多少毫秒后发送空消息。

final boolean sendMessage(Message msg):立即发送消息。

final boolean sendMessageDelayed(Message msg ,long delayMillis):指定多少毫秒后发送消息。

final boolean post(Runnable r):执行runnable操作。

final boolean postAtTime(Runnable r, long upTimeMillis):在指定时间执行runnable操作。

final boolean postDelayed(Runnable r, long delayMillis):指定多少毫秒后执行runnable操作。

介绍完方法后,我们就从一个简单的例子入手吧,然后一步步的分析:

public class MainActivity extends AppCompatActivity {  public static final int MSG_FINISH = 0X001; //创建一个Handler的匿名内部类 private Handler handler = new Handler() {  @Override public void handleMessage(Message msg) {  switch (msg.what) {  case MSG_FINISH:   LogUtils.e("handler所在的线程id是-->" + Thread.currentThread().getName());   break;  } }  };  @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //启动耗时操作 consumeTimeThread(findViewById(R.id.tv));// handler.post() }  //启动一个耗时线程 public void consumeTimeThread(View view) { new Thread() {  public void run() {  try {   LogUtils.e("耗时子线程的Name是--->" + Thread.currentThread().getName());   //在子线程运行   Thread.sleep(2000);   //完成后,发送下载完成消息   handler.sendEmptyMessage(MSG_FINISH);  } catch (InterruptedException e) {   e.printStackTrace();  }  } }.start(); }}

运行结果:

上面的例子其实就是Handler的基本使用,在主线中创建了一个Handler对象,然后通过在子线程中模拟一个耗时操作完成后通过sendEmptyMessage(int)方法发送一个消息通知主线程的Handler去执行相应的操作。通过运行结果我们也可以知道Handler确实也是在主线程运行的。

那么问题来了,通过Handler发送的消息是怎么到达主线程的呢?接下来我们就来掰掰其中的奥妙,前方高能,请集中注意力!为了更好的理解Handler的工作原理,我们先来介绍与Handler一起工作的几个组件:

Message:Handler接收和处理消息的对象。

Looper:每个线程只能有一个Looper。它的loop方法负责读取MessageQueue中的消息,读到消息后把消息发送给Handler进行处理。

MessageQueue:消息队列,它采用先进先出的方式来管理Message。程序创建Looper对象时,会在它的构造方法中创建MessageQueue对象。

Handler:它的作用有两个-发送消息和处理消息,程序使用Handler发送消息,由Handler发送的消息必须被送到指定的MessageQueue;否则消息就没有在MessageQueue进行保存了。而MessageQueue是由Looper负责管理的,也就是说,如果希望Handler正常工作的话,就必须在当前线程中有一个Looper对象。我们先对上面的几个组件有大概的了解就好,后面我们都会详细分析,既然消息是从Handler发送出去,那么我们就先从Handler入手吧。先来看看Handler 的构造方法源码:

public class Handler {        /**         * 未实现的空方法handleMessage()         */        public void handleMessage(Message msg) {        }        /**         * 我们通常用于创建Handler的构造方法之一         */        public Handler() {                this(null, false);        }        // 构造方法的内调用的this(null, false)的具体实现        public Handler(Callback callback, boolean async) {//检查Handler是否是static的,如果不是的,那么有可能导致内存泄露 if (FIND_POTENTIAL_LEAKS) {   final Class klass = getClass();   if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&    (klass.getModifiers() & Modifier.STATIC) == 0) {   Log.w(TAG, "The following Handler class should be static or leaks might occur: " +    klass.getCanonicalName());   }  }  //重要的组件出现啦!Looper我们先理解成一个消息队列的管理者,用来从消息队列中取消息的,后续会详细分析 mLooper = Looper.myLooper();  if (mLooper == null) {   //这个异常很熟悉吧,Handler是必须在有Looper的线程上执行,这个也就是为什么我在HandlerThread中初始化Handler   //而没有在Thread里面初始化,如果在Thread里面初始化需要先调用Looper.prepare方法   throw new RuntimeException(   "Can't create handler inside thread that has not called Looper.prepare()");  }  //将mLooper里面的消息队列复制到自身的mQueue,这也就意味着Handler和Looper是公用一个消息队列  mQueue = mLooper.mQueue;  //回调函数默认是Null  mCallback = null;         }

分析:Handler的构造方法源码不是很多,也比较简单,但是我们从源码中也可以得知,在创建Handler时,Handler内部会去创建一个Looper对象,这个Looper对象是通过Looper.myLooper()创建的(后续会分析这个方法),同时还会创建一个消息队列MessageQueue,而这个MessageQueue是从Looper中获取的,这也就意味着Handler和Looper共用一个消息队列,当然此时Handler,Looper以及MessageQueue已经捆绑到一起了。上面还有一个情况要说明的,那就是:

if (mLooper == null) {  //这个异常很熟悉吧,Handler是必须在有Looper的线程上执行,这个也就是为什么我在HandlerThread中初始化Handler  //而没有在Thread里面初始化,如果在Thread里面初始化需要先调用Looper.prepare方法  throw new RuntimeException(  "Can't create handler inside thread that has not called Looper.prepare()");  }

这里先回去判断Looper是否为空,如果为null,那么就会报错,这个错误对我们来说应该比较熟悉吧,那为什么会报这个错误呢?我们在前面说过Handler的作用有两个-发送消息和处理消息,我们在使用Handler发送消息,由Handler发送的消息必须被送到指定的MessageQueue;否则就无法进行消息循环。而MessageQueue是由Looper负责管理的,也就是说,如果希望Handler正常工作的话,就必须在当前线程中有一个Looper对象。那么又该如何保障当前线程中一定有Looper对象呢?这里其实分两种情况:

(1)在主UI线程中,系统已经初始化好了一个Looper对象,因此我们可以直接创建Handler并使用即可。

(2)在子线程中,我们就必须自己手动去创建一个Looper对象,并且去启动它,才可以使用Handler进行消息发送与处理。使用事例如下:

class childThread extends Thread{ public Handler mHandler;  @Override public void run() {  //子线程中必须先创建Looper  Looper.prepare();    mHandler =new Handler(){  @Override  public void handleMessage(Message msg) {   super.handleMessage(msg);   //处理消息  }  };  //启动looper循环  Looper.loop(); } }

分析完Handler的构造方法,我们接着看看通过Handler发送的消息到底是发送到哪里了?我们先来看看Handler的几个主要方法源码:

// 发送一个空消息的方法,实际上添加到MessagerQueue队列中public final boolean sendEmptyMessage(int what) {        return sendEmptyMessageDelayed(what, 0);}// 给上一个方法调用public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {        Message msg = Message.obtain();        msg.what = what;        return sendMessageDelayed(msg, delayMillis);}// 给上一个方法调用public final boolean sendMessageDelayed(Message msg, long delayMillis) {        if (delayMillis < 0) {                delayMillis = 0;        }        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);}// 给上一个方法调用public boolean sendMessageAtTime(Message msg, long uptimeMillis) {        MessageQueue queue = mQueue;        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(MessageQueue queue, Message msg,                long uptimeMillis) {        msg.target = this;// 设置发送目标对象是Handler本身        if (mAsynchronous) {                msg.setAsynchronous(true);        }        return queue.enqueueMessage(msg, uptimeMillis);// 添加到消息队列中}// 在looper类中的loop()方法内部调用的方法public void dispatchMessage(Message msg) {        if (msg.callback != null) {                handleCallback(msg);        } else {                if (mCallback != null) {                        if (mCallback.handleMessage(msg)) {                                return;                        }                }                handleMessage(msg);        }}

分析:通过源码我们可以知道,当我们调用sendEmptyMessage(int)发送消息后。最终Handler内部会去调用enqueueMessage(MessageQueue queue,Message msg)方法把发送的消息添加到消息队列MessageQueue中,同时还有设置msg.target=this此时就把当前handler对象绑定到msg.target中了,这样就完成了Handler向消息队列存放消息的过程。这个还有一个要注意的方法 dispatchMessage(Message),这个方法最终会在looper中被调用(这里我们先知道这点就行,后续还会分析)。话说我们一直在说MessageQueue消息队列,但这个消息队列到底是什么啊?其实在Android中的消息队列指的也是MessageQueue,MessageQueue主要包含了两种操作,插入和读取,而读取操作本身也会伴随着删除操作,插入和读取对应的分别是enqueueMessage和next,其中enqueueMessage是向消息队列中插入一条消息,而next的作用则是从消息队列中取出一条消息并将其从队列中删除。虽然我们一直称其为消息队列但是它的内部实现并不是队列,而是通过一个单链表的数据结构来维护消息列表的,因为我们知道单链表在插入和删除上比较有优势。至内MessageQueue的内部实现,这个属于数据结构的范畴,我们就不过多讨论了,还是回到原来的主题上来,到这里我们都知道Handler发送的消息最终会添加到MessageQueue中,但到达MessageQueue后消息又是如何处理的呢?还记得我们前面说过MessageQueue是由Looper负责管理的吧,现在我们就来看看Looper到底是如何管理MessageQueue的?

public final class Looper {        // sThreadLocal.get() will return null unless you've called prepare().        //存放线程的容器类,为确保获取的线程和原来的一样        static final ThreadLocal sThreadLocal = new ThreadLocal();        private static Looper sMainLooper; // guarded by Looper.class        //消息队列        final MessageQueue mQueue;        final Thread mThread;        //perpare()方法,用来初始化一个Looper对象        public static void prepare() {                prepare(true);        }                        private static void prepare(boolean quitAllowed) {                if (sThreadLocal.get() != null) {                throw new RuntimeException("Only one Looper may be created per thread");                }                sThreadLocal.set(new Looper(quitAllowed));        }        //handler调用的获取Looper对象的方法。实际是在ThreadLocal中获取。        public static Looper myLooper() {                return sThreadLocal.get();        }                        //Looper类的构造方法,可以发现创建Looper的同时也创建了消息队列MessageQueue对象        private Looper(boolean quitAllowed) {                mQueue = new MessageQueue(quitAllowed);                mRun = true;                mThread = Thread.currentThread();        }                //这个方法是给系统调用的,UI线程通过调用这个线程,从而保证UI线程里有一个Looper //需要注意:如果一个线程是UI线程,那么myLooper和getMainLooper是同一个Looper  public static final void prepareMainLooper() {         prepare();         setMainLooper(myLooper());         if (Process.supportsProcesses()) {          myLooper().mQueue.mQuitAllowed = false;          }         }          //获得UI线程的Looper,通常我们想Hanlder的handleMessage在UI线程执行时通常会new Handler(getMainLooper());  public synchronized static final Looper getMainLooper() {          return mMainLooper;  }                 //looper中最重要的方法loop(),该方法是个死循环,会不断去消息队列MessageQueue中获取消息,然后调dispatchMessage(msg)方法去执行 public static void loop() {        final Looper me = myLooper();        if (me == null) {                throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");                }         final MessageQueue queue = me.mQueue;         //死循环         for (;;) {                Message msg = queue.next(); // might block                if (msg == null) {                // No message indicates that the message queue is quitting.                                return;                }//这里其实就是调用handler中的方法,而在Handler的源码中也可以知道dispatchMessage(msg)内部调用的就是handlerMessage()方法                msg.target.dispatchMessage(msg);                msg.recycle();        }}

分析:代码不算多,我们拆分开慢慢说,在Looper源码中我们可以得知其内部是通过一个ThreadLocal的容器来存放Looper的对象本身的,这样就可以确保每个线程获取到的looper都是唯一的。那么Looper对象是如何被创建的呢?通过源码我们可以知道perpare()方法就可以创建Looper对象:

//perpare()方法,用来初始化一个Looper对象public static void prepare() {        prepare(true);}                private static void prepare(boolean quitAllowed) {if (sThreadLocal.get() != null) {        throw new RuntimeException("Only one Looper may be created per thread");        }        sThreadLocal.set(new Looper(quitAllowed));}

在创建Looper对象前先会去判断ThreadLocal中是否已经存在Looper对象,如果不存在就新创建一个Looper对象并且存放ThreadLocal中。这里还有一个要注意的是在Looper创建的同时MessageQueue消息队列也被创建完成,这样的话Looper中就持有了MessageQueue对象。

//Looper类的构造方法,可以发现创建Looper的同时也创建了消息队列MessageQueue对象        private Looper(boolean quitAllowed) {                mQueue = new MessageQueue(quitAllowed);                mRun = true;                mThread = Thread.currentThread();        }

那么我们如何获取已经创建好的Looper对象呢?通过源码我们知道myLooper()方法就可以获取到Looper对象:

//handler调用的获取Looper对象的方法。实际是在ThreadLocal中获取。        public static Looper myLooper() {                return sThreadLocal.get();        }

Looper对象的创建和获取,还有MessageQueue对象的创建,现在我们都很清楚了,但是Looper到底是怎么管理MessageQueue对象的呢?这就要看looper()方法了:

//looper中最重要的方法loop(),该方法是个死循环,//会不断去消息队列MessageQueue中获取消息,//然后调dispatchMessage(msg)方法去执行 public static void loop() {        final Looper me = myLooper();        if (me == null) {                throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");                }         final MessageQueue queue = me.mQueue;         //死循环         for (;;) {                Message msg = queue.next(); // might block                if (msg == null) {                // No message indicates that the message queue is quitting.                                return;                }//这里其实就是调用handler中的方法,而在Handler的源码中也可以知道dispatchMessage(msg)内部调用的就是handlerMessage()方法                msg.target.dispatchMessage(msg);                msg.recycle();        }

通过looper()方法内部源码我们可以知道,首先会通过myLoooper()去获取一个Looper对象,如果Looper对象为null,就会报出一个我们非常熟悉的错误提示,"No Looper;Looper.prepare() wasn't called on this thread",要求我们先通过Looper.prepare()方法去创建Looper对象;如果Looper不为null,那么就会去获取消息队列MessageQueue对象,接着就进入一个for的死循环,不断从消息队列MessageQueue对象中获取消息,如果消息不为空,那么久会调用msg.target的dispatchMessage(Message)方法,那么这个target又是什么,没错target就是我们创建的Handler对象,还记得我们前面分析Handler源码时说过的那个方法嘛?

// 在looper类中的loop()方法内部调用的方法public void dispatchMessage(Message msg) {        if (msg.callback != null) {                handleCallback(msg);        } else {                if (mCallback != null) {                        if (mCallback.handleMessage(msg)) {                                return;                        }                }                handleMessage(msg);        }

现在明白了吧?首先,检测Message的callback是否为null,不为null就通过handleCallback方法来处理消息,那么Message的callback是什么?其实就是一个Runnable对象,实际上就是Handler的post方法所传递的Runnable参数,我们顺便看看post方法源码:

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

现在明白Message的callback是什么了吧?而对应handleCallback方法逻辑也比较简单:

private static void handleCallback(Message message) { message.callback.run(); }

嗯,是的,因此最终执行的还是通过post方法传递进来的Runnable参数的run方法。好了,我们继续dispatchMessage()方法的分析,接着会去检查mCallback是否为null,不为null,则调用mCallback的handleMessage方法来处理消息。至于Callback则就是一个接口定义如下:

/** * Callback interface you can use when instantiating a Handler to avoid * having to implement your own subclass of Handler. * * @param msg A {@link android.os.Message Message} object * @return True if no further handling is desired */ public interface Callback { public boolean handleMessage(Message msg); }

这个接口有什么用呢?其实通过Callback接口我们就可以采取如下方法来创建Handler对象:

Handler handler =new Handler(callback)

那么这样做到底有什么意义,其实这样做可以用callback来创建一个Handler的实例而无需派生Handler的子类。在我们的开发过程中,我们经常使用的方法就是派生一个Hanlder子类并重写其handleMessage方法来处理具体的消息,而Callback给我们提供了另外一种方式,那就是当我们不想派生子类的时候,可以通过Callback来实现。继续dispatchMessage()方法的分析,最后如果以上条件都不成立的话,就会去调用Handler的handleMessage方法来处理消息。而 我们的Handler是在主线程创建的,也就是说Looper也是主线程的Looper,因此handleMessage内部处理最终都会在主线程上执行,就这样整个流程都执行完了。下面提供一个图解帮助大家理解:


最后我们来个小总结:Android中的Looper类主要作用是来封装消息循环和消息队列的,用于在android线程中进行消息处理。handler是用来向消息队列中插入消息的并最好对消息进行处理。

(1) Looper类主要是为每个线程开启的单独的消息循环。 默认情况下android中新诞生的线程是没有开启消息循环的。(主线程除外,主线程系统会自动为其创建Looper对象,开启消息循环) Looper对象负责管理MessageQueue,而MessageQueue主要是用来存放handler发送的消息,而且一个线程只能有一个Looper,对应一个MessageQueue。

(2) 我们通常是通过Handler对象来与Looper进行交互的。Handler可看做是Looper的一个接口,用来向指定的Looper中的MessageQueue发送消息并且Handler还必须定义自己的处理方法。 默认情况下Handler会与其被定义时所在线程的Looper绑定,如Handler在主线程中定义,它是与主线程的Looper绑定。 mainHandler = new Handler() 等价于 new Handler(Looper.myLooper())Looper.myLooper():获取当前进程的looper对象, Looper.getMainLooper() 用于获取主线程的Looper对象。

(3) 在非主线程中直接new Handler() 会报如下的错误: Can't create handler inside thread that has not called Looper.prepare() 原因是非主线程中默认没有创建Looper对象,需要先调用Looper.prepare()启用Looper,然后再调用Looper.loop()。

(4) Looper.loop():启动looper中的循环线程,Handler就会从消息队列里取消息并进行对应处理。 最后要注意的是写在Looper.loop()之后的代码不会被执行,这个函数内部应该是一个循环,当调用mHandler.getLooper().quit()后,loop()才会中止,其后的代码才能得以运行。

感谢你能够认真阅读完这篇文章,希望小编分享的"Android中Handler,MessageQueue与Looper关系是什么"这篇文章对大家有帮助,同时也希望大家多多支持,关注行业资讯频道,更多相关知识等着你来学习!

消息 方法 线程 对象 队列 处理 就是 循环 源码 分析 管理 同时 也就是 作用 接口 是在 会报 运行 实际 情况 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 关于传统数据库描述错误的是 鬼泣巅峰之战不同服务器互通吗 工信部10大网络安全创新 玖玖叁玖网络技术 安徽云都智慧网络技术有限公司 每个数据库有多个主数据文件 我的世界月球服务器 易保网络技术 需求分析面试 河南惠普服务器虚拟化技术云空间 服务器风扇噪音大啥毛病 qq数据库泄露下载 网络安全技术发展研究报告 视图对应于关系数据库的 学校网络安全监测管理制度 南方电网软件开发笔试多久出结果 网络技术 发展趋势 网络安全产业发展现状 安徽浪潮服务器租用 地平线无法连接到内容服务器 关于数据库安全不正确的描述 云计算下的网络安全技术 聊天记录存在服务器里多久删除 服务器管理软件华为 软件开发和商务电商哪个好 陕西省第五届网络安全宣传周 大公司软件开发文档 如何使用阿里云服务器 计算机网络技术crc题 互联网科技公司税率 第三方软件开发市场报价
0