Android异步消息处理机制 全解析

本文深入解析Android的Handler、MessageQueue和Looper的工作原理。通过源码分析,揭示了为何在主线程中无需调用Looper.prepare()即可创建Handler,而子线程需要。同时介绍了异步消息处理机制如何使得在子线程中操作UI成为可能。文章详述了Handler的创建、消息发送、MessageQueue的入队和出队,以及Looper的循环处理。最后,提到了其他在子线程操作UI的方法,如Handler的post()、View的post()和Activity的runOnUiThread()。
摘要由CSDN通过智能技术生成

本文已授权微信公众号"Android技术杂货铺" 发布。转载请申明出处。

Android异步消息处理机制主要是指Handler的运行机制以及Hanlder所附带的MessageQueue和Looper的工作过程。

本文将通过结合源码(api-28)的形式,全面解析Handler和MessageQueue、Looper的关系。并分析Android异步消息机制的相关原理。在分析之前,先给出结论性的东西,便于在分析过程中有一个主脉络。
在这里插入图片描述

一.Handler

在分析Handler源码之前,我们尝试在程序中创建两个Handler对象,一个在主线程中创建,一个在子线程中创建,代码如下所示:

//代码片1
public class MainActivity extends Activity {
   
	
	private Handler handler1;
	
	private Handler handler2;
 
	@Override
	protected void onCreate(Bundle savedInstanceState) {
   
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		handler1 = new Handler();
		new Thread(new Runnable() {
   
			@Override
			public void run() {
   
				handler2 = new Handler();
			}
		}).start();
	}
 
}

运行代码之后,会报错: java.lang.RuntimeException: Can’t create handler inside thread that has not called Looper.prepare()
并且提示是第16行代码报错.说是不能在没有调用Looper.prepare() 的线程中创建Handler。

那我们在第16行代码前面尝试先调用一下Looper.prepare()呢,即代码如下:

//代码片2
new Thread(new Runnable() {
   
	@Override
	public void run() {
   
		Looper.prepare();
		handler2 = new Handler();
	}
}).start();

运行后发现的确没有报错了。

或许我们有疑问,为什么在子线程中加了Looper.prepare() 就不再报错了呢? 并且为什么第12行代码没有加Looper.prepare() 却没有报错。

先带着第一个问题(即 子线程加了Looper.prepare() 后不报错),来看看Handler的源码吧:

//代码片3
public Handler(Callback callback, boolean async) {
   
      ......
      //仅贴出核心代码
        mLooper = Looper.myLooper();
        if (mLooper == null) {
   
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

可以看到,在第5行调用了Looper.myLooper()方法获取了一个Looper对象,如果Looper对象为空,则会抛出一个运行时异常,提示的错误正是 Can’t create handler inside thread that has not called Looper.prepare()!那什么时候Looper对象才可能为空呢?这就要看看Looper.myLooper()中的代码了,如下所示:

//代码片4
 public static @Nullable Looper myLooper() {
   
        return sThreadLocal.get();
    }

这个方法非常简单,就是从sThreadLocal对象中调用get()方法。

从名字我们可以大胆猜测一下,这是从sThreadLocal取出Looper,如果没有Looper存在自然就返回空了。

既然有get,应该也有set()方法,我们搜索一下(在Looper.java类中) sThreadLocal.set,果然有:

代码如下:

//代码片5
 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.prepare()方法。

可以看到,首先判断sThreadLocal中是否已经存在Looper了,如果还没有则创建一个新的Looper设置进去。这样也就完全解释了为什么我们要先调用Looper.prepare()方法,才能创建Handler对象。同时也可以看出每个线程中最多只会有一个Looper对象。

现在我们来通过源码分析 代码片1第12行代码没有添加Looper.myLooper() 方法也没有报错 的原因。这是由于在程序启动的时候,系统已经帮我们自动调用了Looper.prepare()方法。查看ActivityThread中的main()方法,代码如下所示:

//代码片6
public static void main(String[] args) {
   
   .....
   //核心代码如下
    Looper.prepareMainLooper();
    ActivityThread thread = new ActivityThread();     //创建主线程
    thread.attach(false);
    if (sMainThreadHandler == null) {
   
        sMainThreadHandler = thread.getHandler();
    }
    AsyncTask.init();
    if (false) {
   
        Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread"));
    }
    Looper.loop();
    throw new RuntimeException("Main thread loop unexpectedly exited");
}

可以看到,在第5行调用了Looper.prepareMainLooper()方法,

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值