Android Handler消息机制原理最全解读(持续补充中)

Android Handler消息机制原理最全解读(持续补充中)Handler 在 Android 开发的过程中 我们常常会将耗时的一些操作放在子线程 workthread 中去执行 然后将执行的结果告诉 UI 线程 mainthread 熟悉 Android 的朋友都知道 UI 的更新只能通过 Mainthread 来进行 那么这里就涉及到了如何将子线程的数据传递给 mainthread 呢 Android 已经为我们提供了一个消息传递的机制 Hand

 本文主要详细去解读Android开发中最常使用的Handler,以及使用过程中遇到的各种各样的疑问。

Handler


private Handler mHandler = new Handler(){ 
     @Override public void handleMessage(Message msg) { 
     super.handleMessage(msg); switch (msg.what) { 
     case MESSAGE_WHAT: Log.d(TAG, "main thread receiver message: " + ((String) msg.obj)); break; } } }; private void sendMessageToMainThreadByWorkThread() { 
     new Thread(){ 
     @Override public void run() { 
     Message message = mHandler.obtainMessage(MESSAGE_WHAT); message.obj = "I am message from work thread"; mHandler.sendMessage(message); } }.start(); } /* * 通常我们在主线程中创建一个Handler, * 然后重写该Handler的handlerMessage方法,可以看到该方法传入了一个参数Message, * 该参数就是我们从其他线程传递过来的信息。 * * 我们在来看下子线程中如何传递的信息,子线程通过Handler的obtainMessage()方法获取到一个Message实例, * 我们来看看Message的几个属性: * Message.what------------------>用来标识信息的int值,通过该值主线程能判断出来自不同地方的信息来源 * Message.arg1/Message.arg2----->Message初始定义的用来传递int类型值的两个变量 * Message.obj------------------->用来传递任何实例化对象 * 最后通过sendMessage将Message发送出去。 * * Handler所在的线程通过handlerMessage方法就能收到具体的信息了,如何判断信息的来源呢?当然是通过what值啦。 * 怎么样很简单吧 */ 

private Handler handler; private void handlerDemoByTwoWorkThread() { 
      Thread hanMeiMeiThread = new Thread() { 
      @Override public void run() { 
      // Looper.prepare(); handler = new Handler() { 
      @Override public void handleMessage(Message msg) { 
      Log.d(TAG, "hanMeiMei receiver message: " + ((String) msg.obj)); Toast.makeText(MainActivity.this, ((String) msg.obj), Toast.LENGTH_SHORT).show(); } }; // Looper.loop(); } }; Thread liLeiThread = new Thread() { 
      @Override public void run() { 
      try { 
      Thread.sleep(2000); } catch (InterruptedException e) { 
      e.printStackTrace(); } Message message = handler.obtainMessage(); message.obj = "Hi MeiMei"; handler.sendMessage(message); } }; hanMeiMeiThread.setName("韩梅梅 Thread"); hanMeiMeiThread.start(); liLeiThread.setName("李雷 Thread"); liLeiThread.start(); /* * 搞定,我们创建了两个Thread,liLeiThread和hanMeiMeiThread两个线程,很熟悉的名字啊! * 跟之前的代码没太大区别hanMeiMeiThread创建了Handler,liLeiThread通过Handler发送了消息。 * 只不过此处我们只发送一个消息,所以没有使用what来进行标记 * 运行看看,我们的李雷能拨通梅梅吗? * 啊哦,出错了 * 05-13 17:08:17.709 20673-20739/? E/AndroidRuntime: FATAL EXCEPTION: 韩梅梅 Thread Process: design.wang.com.designpatterns, PID: 20673 java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare() at android.os.Handler. 
     
       (Handler.java:200) at android.os.Handler. 
      
        (Handler.java:114) *Can't create handler inside thread that has not called Looper.prepare() * -----------》它说我们创建的handler没有调用Looper.prepare(); * 好的,我们在实例化Handler之前调用下该方法,看一下。加上是不是没有报错了呢。 * 等等,虽然没有报错,但是hanMeiMeiThread也没有接到消息啊,消息呢?别急。 * 我们在Handler实例化之后加上Looper.loop();看一看,运行一下,是不是收到消息了呢。 * 这是为什么呢? * 接下来我们就去看看Handler是怎么实现的发消息呢,弄清楚了原理,这里的原因也就明白了。 */ 
       
      } 

//我们先来看看new Handler();时出错的原因。后续讲解源码分析只贴出关键部分。 //如下是Handler构造函数里抛出上文异常的地方,可以看到,由于mLooper对象为空才抛出的该异常。 mLooper = Looper.myLooper(); if (mLooper == null) { 
       throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } /* 异常的原因看到了,接下来我们看看Looper.prepare()方法都干了些什么? */ 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)); } /* 可以看到,该方法在当前thread创建了一个Looper(), ThreadLocal主要用于维护线程的本地变量, */ private Looper(boolean quitAllowed) { 
       mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); } //而Looper的构造函数里面又为我们创建了一个MessageQueue()对象。 
 Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { 
       sMainThreadHandler = thread.getHandler(); } if (false) { 
       Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); } // End of event ActivityThreadMain. Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); Looper.loop(); /* 真相只有一个,是的在创建主线程的时候Android已经帮我们调用了Looper.prepareMainLooper() 和Looper.loop()方法,所以我们在主线程能直接创建Handler使用。 */ 

 我们接着来看Handler发送消息的过程:

//调用Handler不同参数方法发送Message最终都会调用到该方法 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); } 

 sendMessage的关键在于enqueueMessage(),其内部调用了messageQueue的enqueueMessage方法

boolean enqueueMessage(Message msg, long when) { 
        ... synchronized (this) { 
        if (mQuitting) { 
        IllegalStateException e = new IllegalStateException( msg.target + " sending message to a Handler on a dead thread"); Log.w(TAG, e.getMessage(), e); msg.recycle(); return false; } msg.markInUse(); msg.when = when; Message p = mMessages; boolean needWake; if (p == null || when == 0 || when < p.when) { 
        // New head, wake up the event queue if blocked. msg.next = p; mMessages = msg; needWake = mBlocked; } else { 
        needWake = mBlocked && p.target == null && msg.isAsynchronous(); Message prev; for (;;) { 
        prev = p; p = p.next; if (p == null || when < p.when) { 
        break; } if (needWake && p.isAsynchronous()) { 
        needWake = false; } } msg.next = p; // invariant: p == prev.next prev.next = msg; } // We can assume mPtr != 0 because mQuitting is false. if (needWake) { 
        nativeWake(mPtr); } } return true; } /*从代码可以看出Message被存入MessageQueue时是将Message存到了上一个Message.next上, 形成了一个链式的列表,同时也保证了Message列表的时序性。 */ 
public static void loop() { 
        //可以看到,在调用Looper.prepare()之前是不能调用该方法的,不然又得抛出异常了 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; // Make sure the identity of this thread is that of the local process, // and keep track of what that identity token actually is. Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); for (;;) { 
        Message msg = queue.next(); // might block if (msg == null) { 
        // No message indicates that the message queue is quitting. return; } // This must be in a local variable, in case a UI event sets the logger final Printer logging = me.mLogging; if (logging != null) { 
        logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } final long traceTag = me.mTraceTag; if (traceTag != 0) { 
        Trace.traceBegin(traceTag, msg.target.getTraceName(msg)); } try { 
        msg.target.dispatchMessage(msg); } finally { 
        if (traceTag != 0) { 
        Trace.traceEnd(traceTag); } } if (logging != null) { 
        logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); } // Make sure that during the course of dispatching the // identity of the thread wasn't corrupted. final long newIdent = Binder.clearCallingIdentity(); if (ident != newIdent) { 
        Log.wtf(TAG, "Thread identity changed from 0x" + Long.toHexString(ident) + " to 0x" + Long.toHexString(newIdent) + " while dispatching to " + msg.target.getClass().getName() + " " + msg.callback + " what=" + msg.what); } msg.recycleUnchecked(); } } /* 这里我们看到,mLooper()方法里我们取出了,当前线程的looper对象,然后从looper对象开启了一个死循环 不断地从looper内的MessageQueue中取出Message,只要有Message对象,就会通过Message的target调用 dispatchMessage去分发消息,通过代码可以看出target就是我们创建的handler。我们在继续往下分析Message的分发 */ public void dispatchMessage(Message msg) { 
        if (msg.callback != null) { 
        handleCallback(msg); } else { 
        if (mCallback != null) { 
        if (mCallback.handleMessage(msg)) { 
        return; } } handleMessage(msg); } } /*好了,到这里已经能看清晰了 可以看到,如果我们设置了callback(Runnable对象)的话,则会直接调用handleCallback方法 */ private static void handleCallback(Message message) { 
        message.callback.run(); } //即,如果我们在初始化Handler的时候设置了callback(Runnable)对象,则直接调用run方法。比如我们经常写的runOnUiThread方法,由于Handler在主线程创建,所以最终得以在主线程执行: runOnUiThread(new Runnable() { 
        @Override public void run() { 
        } }); public final void runOnUiThread(Runnable action) { 
        if (Thread.currentThread() != mUiThread) { 
        mHandler.post(action); } else { 
        action.run(); } } /* 而如果msg.callback为空的话, 存在一种情况,当创建Handler使用了带Callback的构造方法的话,将会执行Callback的handleMessgae方法,并且会根据其方法的返回值判断是否会被callback拦截, 是否需要继续执行Handle的handlMessgae方法,如果不存在Callback的拦截则Handler本身的handleMessage方法得意执行。(Handler(Callback)的构造方法使用场景暂时未遇到,欢迎补充) */ 

 到这里,想必你应该清楚如何在不同的线程之间来使用Handler了吧。

最后总结一下:

  1. 在使用handler的时候,在handler所创建的线程需要维护一个唯一的Looper对象, 每个线程对应一个Looper,每个线程的Looper通过ThreadLocal来保证,如需了解ThreadLocal,点击查看详细讲解 ,
    Looper对象的内部又维护有唯一的一个MessageQueue,所以一个线程可以有多个handler,
    但是只能有一个Looper和一个MessageQueue。




  2. Message在MessageQueue不是通过一个列表来存储的,而是将传入的Message存入到了上一个
    Message的next中,在取出的时候通过顶部的Message就能按放入的顺序依次取出Message。

  3. Looper对象通过loop()方法开启了一个死循环,不断地从looper内的MessageQueue中取出Message,
    然后通过handler将消息分发传回handler所在的线程。




Handler补充:

1. Handler在使用过程中,需要注意的问题之一便是内存泄漏问题。

  1. 首先,非静态内部类是会隐式持有外部类的引用,所以当其他线程持有了该Handler,线程没有被销毁,则意味着Activity会一直被Handler持有引用而无法导致回收。
  2. 同时,MessageQueue中如果存在未处理完的Message,Message的target也是对Activity等的持有引用,也会造成内存泄漏。
解决的办法:

 (1). 使用静态内部类+弱引用的方式:

  静态内部类不会持有外部类的的引用,当需要引用外部类相关操作时,可以通过弱引用还获取到外部类相关操作,弱引用是不会造成对象该回收回收不掉的问题,不清楚的可以查阅JAVA的几种引用方式的详细说明。

private Handler sHandler = new TestHandler(this); static class TestHandler extends Handler { private WeakReference 
          
            mActivity; TestHandler(Activity activity) { mActivity = new WeakReference<>(activity); } @Override public void handleMessage(Message msg) { super.handleMessage(msg); Activity activity = mActivity.get(); if (activity != null) { //TODO: } } } 
          

 (2). 在外部类对象被销毁时,将MessageQueue中的消息清空。例如,在Activity的onDestroy时将消息清空。

@Override protected void onDestroy() { handler.removeCallbacksAndMessages(null); super.onDestroy(); } 

2. 在使用Handler时,通常是通过Handler.obtainMessage()来获取Message对象的,而其内部调用的是Message.obtain()方法,那么问题来了,为什么不直接new一个Message,而是通过Message的静态方法obtain()来得到的呢?

下面就通过代码来一探究竟

public static Message obtain() { 
            synchronized (sPoolSync) { 
            if (sPool != null) { 
            Message m = sPool; sPool = m.next; m.next = null; m.flags = 0; // clear in-use flag sPoolSize--; return m; } } return new Message(); } 
public void recycle() { 
            if (isInUse()) { 
            if (gCheckRecycle) { 
            throw new IllegalStateException("This message cannot be recycled because it " + "is still in use."); } return; } recycleUnchecked(); } void recycleUnchecked() { 
            // Mark the message as in use while it remains in the recycled object pool. // Clear out all other details. flags = FLAG_IN_USE; what = 0; arg1 = 0; arg2 = 0; obj = null; replyTo = null; sendingUid = -1; when = 0; target = null; callback = null; data = null; synchronized (sPoolSync) { 
            if (sPoolSize < MAX_POOL_SIZE) { 
            next = sPool; sPool = this; sPoolSize++; } } } 
总结:

3. Handler sendMessage原理解读。

 引入问题!

  1. sendMessageDelayed是如何实现延时发送消息的?
  2. sendMessageDelayed是通过阻塞来达到了延时发送消息的结果,那么会不会阻塞新添加的Message?

详细分析请移步下篇文章:Handler进阶之sendMessage原理探索


欢迎提供其他有关Handler的问题分享讨论

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/205223.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月19日 下午6:31
下一篇 2026年3月19日 下午6:31


相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号