Android线程通信:Handler,MessageQueue和Looper原理分析

本文深入剖析Android线程通信的核心机制,涉及Handler的sendMessage, sendEmptyMessage, post方法,以及MessageQueue和Looper的角色。通过理解这一机制,可以更好地在子线程中安全更新UI,并实现线程间的有效控制。" 119203737,1106395,C++11变参数模板详解:从基础到STL应用,"['C++', '编程语言', '模板编程']

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、概述

Android的线程通信机制主要是指Handler的运行机制,而Handler的运行又离不开MessageQueue和Looper的支持,由这三个构建了Android的线程通信机制。

Handler负责投递消息和处理消息,MessageQueue是消息队列,Handler将消息投递到MeesageQueue中,而Looper则无限循环地去抽取MessageQueue中的消息并分发给目标Handler,若是没有新消息则堵塞等待。

由于Android 的View不是线程安全的,所以系统只允许在UI线程也就是主线程对UI进行更新操作,所以当试图在子线程更新UI,会抛出异常。因此,我们常在主线程创建Handler实例,然后在子线程持有该实例并调用mHandler.sendMessage()、mHandler.post或是没有手动创建Handler实例,直接mView.post()来在子线程更新UI,而这些方法,都是使用了Handler。当然Handler不只是用来更新UI,我们也可以在子线创建Handler,然后在其他线程对该线程进行控制。


2、Handler的使用

Handler的使用很简单,首先我们要先创建Looper和MessageQueue,有的可能会奇怪,那怎么以前使用Handler来更新UI时怎么没有创建过Looper和MessageQueue呢?原因是主线程的Looper系统已经帮我们创建好了,不需要手动创建了,若是想要在子线程使用Handler,则需要先手动创建Looper,MessageQueue。然后就是创建Handler实例并重写public void handleMessage(Message msg)方法,有一点注意的是:想要通知哪个线程处理事务,handler的实例就要在哪个线程创建,然后由其他线程持有该Handler实例,并通过mHandler.sendMessage()来向目标线程发出消息。例如我们最常用的更新UI,因为我们要通知主线程更新UI,所以我们一定要在主线程创建Handler实例,这样Handler实例就与主线程的Looper、MessageQueue绑定,然后在子线程通过这个handler实例向主线程发出消息。
接下来直接上代码,看看Handler具体是怎么使用的:
在主线程更新UI:
public class MainActivity extends AppCompatActivity {

    private Button mButton;
    private TextView mTextView;
    private Handler mHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mButton = (Button) findViewById(R.id.test_btn);
        mTextView = (TextView) findViewById(R.id.test_iv);
	//因为要在主线程更新UI,所以在主线程创建Handler实例
        mHandler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what){
                    case 0:
                        //取出Bundle中的数据
                        String info = msg.getData().getString("info");
                        //获得message.obj
                        String str = (String)msg.obj;
                        mTextView.setText(str);
                        break;
                    default:
                        break;
                }
                super.handleMessage(msg);
            }
        };

        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        //创建Message对象,在该对象中保存信息
                        Message message = Message.obtain();
                        //一般以message.waht作为标志
                        message.what = 0;
                        //可以直接将任意一个对象附在message.obj
                        message.obj = "123456789";
                        //可以创建Bundle对象,并Bundle对象中保存信息
                        //Bundle中只能保存基本数据类型、String和实现序列化的对象
                        Bundle bundle = new Bundle();
                        bundle.putString("info","just for test");
                        message.setData(bundle);
                        mHandler.sendMessage(message);
                        //若是没有需要额外传递的信息,可以使用
                        //mHandler.sendEmptyMessage(int what)
                        // 会自动创建一个message对象,并将message.what设为我们传入的参数

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

在子线程创建使用Handler:
public class MainActivity extends AppCompatActivity {

    private Button mButton;
    private TextView mTextView;
    private Handler mHandler;
    private static final String TAG = "HandlerTest";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mButton = (Button) findViewById(R.id.test_btn);
        mTextView = (TextView) findViewById(R.id.test_iv);

        new Thread(new Runnable() {
            @Override
            public void run() {
  		//创建Looper,在Looper的构造方法中会创建MessageQueue,因此不必手动创建
                Looper.prepare();
               
		//如果想在子线程处理一些事务,如创建Handler实例,
		//一定要在Looper.loop()之前处理,因为,Looper.loop()调用之后
		//会无限循环,所以在Looper.loop()之后的事务将不会得到处理
                mHandler = new Handler(){
                    @Override
                    public void handleMessage(Message msg) {
                        switch (msg.what){
                            case 0:
                                Looper.myLooper().quit();

                        }
                        super.handleMessage(msg);
                    }
                };
		//启动Looper,当没调用Looper.myLooper().quit();
                // 或Looper.myLooper().quitSafely()
                //Looper会一直进行循环下去,子线程也就会一直存在
                //因此当不需要时,要终止Looper
                Looper.loop();
            }
        }).start();

		//点击Button,第一次点击,通过sendMessage()退出子线程的Looper,并返回true
		//第二次点击由于Looper已经退出,sendMessage()将返回false
        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //停止Looper,当我们调用mHandler.sendEmptyMessage(),
                // 若Looper未停止消息循环 返回true,
                //若Looper已停止循环,返回false
                boolean isSuccessful = mHandler.sendEmptyMessage(0);
                Log.i(TAG, "send message return :"+isSuccessful);
            }
        });
    }
    
}


可以看到Handler的使用还是比较简单的,在其他线程持有目标线程创建的Handler实例mHandler的引用,通过mHandler.sendMessage()将消息送到目标线程的消息队列MessageQueue中,由目标线程的Looper无限循环抽取消息,并分配给将该消息投入MessageQueue的mHandler,调用mHandler.handlerMessage()来处理消息,这样就完成了线程的切换。


3、MessageQueue

MessageQueue中文翻译为消息队列,是来存放Handler传递的消息。虽然MessageQueue叫做消息队列,但是内部实现并不是队列,而是用单链表的数据结构来维护消息列表。MessageQueue有两个主要方法:enqueueMessage()和next()。enqueueMessage()作用是将一条消息插入消息队列,主要就是单链表的插入操作。next()则是从消息队列中读取一条信息,并将该信息从消息队列中删除。next()是一个无限循环循环的方法,如果消息队列中没有消息,就会堵塞,一直等待,直到新消息到来,就会返回新消息,并将该消息从消息队列中删除。


4、Looper

前面的demo提到,Looper.prepare()创建Looper,Looper.loop()启动Looper,现在追踪代码看看源码。
先看下Looper.prepare()
 private static void prepare(boolean quitAllowed) {
        //因为一个线程只能有一个Looper
	//如果sThreadLocal.get() != null,则说明当前线程已经有了一个Looper,会抛出异常
	if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
	//创建并保存Looper在ThreadLocal对象中
        sThreadLocal.set(new Looper(quitAllowed));
    }
在看看New Looper(quitAllowed)
private Looper(boolean quitAllowed) {
	//在Looper的构造方法里创建了MessageQueue实例
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
这样Looper和MessageQueue就创建完成了。

接下来看看Looper.loop()
public static void loop() {
        final Looper me = myLooper();//获取当前线程的Looper实例
        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 (;;) {
	    // 调用MessageQueue.next()抽取消息队列的一条消息
	    //由于next()在消息队列没有消息时会堵塞,所以里也会堵塞
            Message msg = queue.next(); 
            if (msg == null) {
                //当调用looper.quit()或looper.quitSafely(),MessageQueue.mQuitting属性会被标记
		//next会返回null,也只有这种情况next()才会返回null,否则要么堵塞,要么返回消息
		//当返回null,Looper直接退出
                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 {
		//把消息分发给将该发送该消息的对象,也就是Handler对象
		//msg.target就是发送该消息的对象Handler
                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();
        }
    }


Looper.loop()方法中的几个重要部分都注释清楚了,其中有获取线程Looper的myLooper()方法和分配msg的msg.target.dispatchMessage(msg)我们可以再深入看看
先看看myLooper()
public static @Nullable Looper myLooper() {
	//在创建Looper的时候,将Looper保存在了sThreadLocal
	//这里通过sThreadLocal.get()获得Looper
        return sThreadLocal.get();
    }
ThreadLocal是一个线程内部的数据存储类,通过它可以在指定线程存储数据,且只能在指定线程获取数据,在其他线程无法或取数据。更多关于ThreadLocal的可以查看这篇博客,写的很透彻


再看看msg.target.dispatchMessage(msg),因为msg.target就是发送该条消息的mHandler,也就是mHandler.dispatchMessage(),Looper调用Handler的这个方法来处理消息
public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
可以看到,Handler处理消息的步骤是这样的:
首先检查msg.callback是否为空,不为空则调用handlerCallback(msg),
那这个msg.callback是什么呢?其实Message.callback就是我们通过mHandler.post(Runnable)发送消息时的Runnable对象,看handlerCallback(msg)的实现就知道了:
private static void handleCallback(Message message) {
        message.callback.run();
    }

若是 msg.callback为空,就判断mCallback是为为空,若不为空,则调用mCallBack.handleMessage(msg)处理消息。那么问题又来了,这个mHandler.mCallBack又是个什么东西?它是Handler的一个内部接口
public interface Callback {
        public boolean handleMessage(Message msg);
    }
它的使用时这样的
Handler.Callback mCallback = new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            return false;
        }
    };
Handler mHandler = new Handler(mCallback);
创建后Handler发送消息的用法跟一般创建的Handler用法一样,就不列出代码了
那这个Handler.Callback存在的意义是什么呢?
“Callback interface you can use when instantiating a Handler to avoid
having to implement your own subclass of Handler.“
这是源码上的注释,意思是可以使用这个接口来创建Handler实例而不用去派生Handler的实例(但是我实在不明白除了消息处理的优先级比较高,跟我们派生Handler实例有什么区别或者说优点?不都要重写个handleMessage(),代码也没少多少,实在搞不懂为什么要弄这么个接口出来?)

若是mCallback为空,则调用handleMessage(msg)处理消息。
以上就是mHandler.dispatchMessage(msg)的全部过程。


5、Handler

关于Handler,主要是发送消息和处理消息,处理消息的方法dispatchMessage(msg),在刚刚介绍Loop的时候一起分析了,接下来分析下Handler如何发送消息。
在概述部分,提到到过可以通过mHandler.sendMessage(Message)、mHandler.post(Runnable)向目标线程发送消息,甚至mView.post(Runnable)也是使用了Handler来向主线程发送消息更新UI,接下来一个一个分析。

1、sendMessage(Message msg)

首先看看mHandler.sendMessage(),
public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }
调用了sendMessageDelay(),延迟指定时间再将Message插入MessageQueue,继续往下追踪
public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
sendMessageDelay()又调用sendMessageAtTime(),继续追踪

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;
        }
	//调用了Handler的enqueueMessage(),插入消息
	//显然enqueueMessage()内肯定是通过Handler实例保存的MessageQueue实例
	//向MessageQueue插入消息
        return enqueueMessage(queue, msg, uptimeMillis);
    }
	
我们发现sendMessage()最终调用了sendMessageAtTime(),接着sendMessageAtTime()内又调用了Handler实例的
enqueueMessage(),显然最终将消息插入目标线程MessageQueue的一步就在这个方法里了,我们进入这个方法内部看看
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        //将发送该消息的Hnndler实例的引用保存在msg.target中
	msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
	//调用保存在Handler实例中目标线程的MessageQueue实例的enqueueMessage()
	//将消息插入目标线程的消息队列
        return queue.enqueueMessage(msg, uptimeMillis);
    }
	
我们看到 msg.target = this,对照前面分析Looper如何分配消息
msg.target.dispatchMessage(msg);
一切就很清楚了,在将消息插入消息队列前,把发送该消息的Handler的实例保存在消息中,这样Looper在主线程非配处理消息时就能准确的把消息交给正确的Handler实例来处理。

2、mHandler.sendEmptyMessage(int what)

我们有时因为只想通知线程,并不需要传递其他数据,就会使用mHanlder.sendEmptyMessage(int what),来向目标线程发送消息,这样的话就省去创建Message实例的步骤了,那mHanlder.sendEmptyMessage(int what)有时如何工作的呢?我们可以追踪这个方法看看
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);
    }
我们发现,最终在sendEmptyMessageDelay()里,创建了Message实例,并且what属性就是我们传入的参数,接下来有调用了sendMEssageDelay(),显然,从这里开始,后面的过程与sendMessage()一样。

3、mHandler.post(Runnable r)

我们再看看mHandler.post(Runnable)方法
public final boolean post(Runnable r)
    {
	//调用了sendMessageDelayed(),该方法的第一个参数应该是一个Message,
	//显然getPostMessage(r)创建了一个Message实例
       return  sendMessageDelayed(getPostMessage(r), 0);
    }
显然接下来的过程也跟sendMessage()一样,再看看getPostMessage(r)
private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }
果然,在这个方法里创建了Message实例,并且把Runnable实例保存在Message的callback中,显然,dispatchMessage()中的msg.callback就是这时候设置的
if (msg.callback != null) {
            handleCallback(msg);
        } 

4、mView.post(Runnable r)

有时候我们要在子线程向更新UI,但又不是很频繁,使用Handler显然有些麻烦,这时候,
使用mView.post(Runnable r)来更新UI是个很好地选择, 我们看看这个方法的工作原理是什么?
 public boolean post(Runnable action) {
        final AttachInfo attachInfo = mAttachInfo;
        if (attachInfo != null) {
            return attachInfo.mHandler.post(action); //获得了View内部的Handler实例并调用该实例的post()方法
        }

        // Postpone the runnable until we know on which thread it needs to run.
        // Assume that the runnable will be successfully placed after attach.
        getRunQueue().post(action);
        return true;
    }
我们可以发现mView.post(Runnable r)最终还是通过Handler实例的post(Runnable r)方法。
通过对前面四个向主线程发送消息更新UI方法的分析,我们发现这四种方法都使用了Handler,无论我们是否显式地使用Handler。


总结

经过前面的分析,整个Android线程通信机制应该已经很清晰了:
Handler和本线程的Looper和MessageQueue绑定,一个线程可以有很多个Handler,但只能有一个Looper和MessageQueue;
其他线程通过持有这个Handler实例的引用,可以向这个Handler实例所在的线程发出消息,发送消息是在其他线程进行的;
消息送到消息队列MessageQueue,Looper则不停地抽取消息队列中的消息,Looer循环处理消息是在Handler所在线程也就是目标线程进行的,并把消息交给发送它的Handler实例的handleMessage()方法处理,这样就完成了从其他线程到目标线程的切换,这就是Android线程通信的实质。











评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值