Handler 机制和原理-Android

Posted hequnwang10

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Handler 机制和原理-Android相关的知识,希望对你有一定的参考价值。

handler主要包含 Looper,MessageQueue,Message 和 Handler。

Looper:它在消息机制里是用来把普通线程转成looper线程的(Looper.prepare()),并担当消息循环的任务,当然,UI 线程是通过Looper.PrepareMainLooper()。
它会开启无限循环(Loper.loop())并不停的从 MessageQueue 中查看是否有新消息,如果有就拿出来处理,如果没有呢,就阻塞(其实真正的阻塞在 MessageQueue 的 next 里)。Loper在构造的时候会构建一个 MessageQueue 并持有它的引用。

MessageQueue: 消息队列,它用来插入(enqueueMessage),读取消息(next)。它虽然是个消息队列,但实际实现是个单链表,因为单链表在插入和读取上有优势。它插入是根据时间戳排序来的,根据时间戳的排序来指定next的下一条消息。而当next读取的时候,如果有消息,就取出来,如果没有,就阻塞。如果 next 取出来是 null,那么就表示整个app可以结束运行了。

Handler 的主要工作就是负责消息的发送和接收了。发送消息是 post 和 send 的一堆函数,其实到最后 enqueueMessage() 来进行入队,接收消息处理则是通过msg的next里返回的 looper 在交给 handler 的 dispatchMessage() 方法。Handler 在哪个线程之下构造就会持有当前这个线程的 looper 引用。Handler 工作的时候首先会判断 msg.callback(Runnable对象)是不是 null,不为 null 就会变为 handlerCallback(执行msg的callback),然后检查 mCallback 是不是 null,不为 null 执行 handlerMessage。而这个 mCallback 则又是通过 msg 的 target(对应的handler)来进行回调调用的。

那么整个流程是:activityThread 里 main 方法执行即 app 启动的时候,会把当前线程转为 UI 线程(Loper.PrepareMainLoper()),并且会构造 activityThread(下面简称actThread)实例,它的 attach 方法会创建 Binder 线程通道(ApplicationThread,用来接受系统进程传递过来的信息的线程)。然后通过 Looper.loop 来开启无限循环。无限循环开启后,需要一个 handler 和消息队列进行交互,这个handler (ActivityThread.H,简称mH)主要用来管理四大组件的生命周期,启动,停止等消息类型。actThread 通过之前创建的bind线程与ams进行通信,ams执行完actThread的请求后会回调 binder 线程的 bind 方法,然后 binder 线程会向 mH 发送消息,mH 收到消息后会回到 actThread 中去执行。

为什么子线程中创建Handler会抛异常

子线程创建Handler会抛出异常的原因是因为在looper里面ThreadLocal sThreadLocal = new ThreadLocal() ;ThreadLocal 就是为了保存的Looper只能在指定线程中获取Looper。 因为子线程创建new Handler()并没有指定Looper 所以它就去获取ActivityThread的main方法中创建的looper 而此时的这个looper 是受线程保护的 所以子线程是无法获取的 因此抛出异常所以在子线程中没有looper 如果需要在子线程中开启handle要手动创建looper。
其中 Looper.prepare();为当前线程创建Looper并绑定在ThreadLocal中 Looper.loop();执行消息循环,这样子 Handler就能够正常工作了。
就是没有创建Looper。

Looper.prepare();
handler = new Handler();
Looper.loop();

从源码角度分析Handler的post和sendMessage方法的区别和应用场景?

post这个方法是把任务r转成一个message放进了handler所在的线程中的messageQueue消息队列中,并且是立刻发送的消息,这样它既不是异步的也不是延时的

	    public final boolean post(@NonNull Runnable r) 
	       return  sendMessageDelayed(getPostMessage(r), 0);
	    
	    /*最后我们再看post中调用的另外一个方法,源码中没有注释,
	    但我们很容易看出来,这个方法就是把r这个任务包装成了一个
	    空的消息并且返回*/
	    private static Message getPostMessage(Runnable r) 
		    Message m = Message.obtain();
		    m.callback = r;
		    return m;
		
        public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) 
        if (delayMillis < 0) 
            delayMillis = 0;
        
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    

	    public boolean sendMessageAtTime(@NonNull 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);
	    




----------------------------------------------------------------
    public final boolean sendMessage(@NonNull Message msg) 
        return sendMessageDelayed(msg, 0);
    
    public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) 
        if (delayMillis < 0) 
            delayMillis = 0;
        
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    
    public boolean sendMessageAtTime(@NonNull 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);
    
    

post和sendMessage只是用法上的区别,本质是没有区别的。

handler.post和handler.sendMessage方法最后都会调用sendMessageAtTime方法进行消息的发送,但是在post方法中message是通过getPostMessage(Runnable r)这个方法获取的message,在这个方法中有这样一句代码m.callback = r ,给message的callback赋值为runnable对象,而在dispatchMessage这个方法中对消息进行分发的时候,先进行了msg.callback != null的判断,如果不为null,消息是通过handleCallback(msg);这个方法处理的,在这个方法中message.callback.run();调用的是post方法传递过来的runnable内的run方法处理消息,如果为空,再进行handler内部的callback判断mCallback != null,如果handler内的callback不为空,执行mCallback.handleMessage(msg)这个处理消息并判断返回是否为true,如果返回true,消息处理结束,如果返回false,消息交给handler的handleMessage(msg)处理。

所以区别就是调用post方法的消息是在post传递的Runnable对象的run方法中处理,而调用sendMessage方法需要重写handleMessage方法或者给handler设置callback,在callback的handleMessage中处理并返回true。

  1. post一类的方法发送是Runnable对象,但是最后还是会被封装成Message对象,
    将Runnable对象赋值给Message对象中的callback字段,然后交由sendMessageAtTime()方法 发送出去。
    在处理消息时,会在dispatchMessage()方法里首先被handleCallback(msg)方法执行,实际上就是执行Message对象里面的Runnable对象的run方法。

  2. sendMessage一类方法发送的消息直接是Message对象,处理消息时,在dispatchMessage里优先级会低于handleCallback(msg)方法
    是通过自己重写的handleMessage(msg)方法执行。

  3. post是属于sendMessage的一种赋值callback的特例

Handler中有Loop死循环,为什么没有阻塞主线程,原理是什么
简单一句话是:android应用程序的主线程在进入消息循环过程前,会在内部创建一个Linux管道(Pipe),这个管道的作用是使得Android应用程序主线程在消息队列为空时可以进入空闲等待状态,并且使得当应用程序的消息队列有消息需要处理时唤醒应用程序的主线程。—这一题是需要从消息循环、消息发送和消息处理三个部分理解Android应用程序的消息处理机制了,
这里我对一些要点作一个总结:

  1. Android应用程序的消息处理机制由消息循环、消息发送和消息处理三个部分组成的
  2. Android应用程序的主线程在进入消息循环过程前,会在内部创建一个Linux管道(Pipe),这个管道的作用是使得Android应用程序主线程在消息队列为空时可以进入空闲等待状态,并且使得当应用程序的消息队列有消息需要处理时唤醒应用程序的主线程
  3. Android应用程序的主线程进入空闲等待状态的方式实际上就是在管道的读端等待管道中有新的内容可读,具体来说就是是通过Linux系统的Epoll机制中的epoll_wait函数进行的。
  4. 当往Android应用程序的消息队列中加入新的消息时,会同时往管道中的写端写入内容,通过这种方式就可以唤醒正在等待消息到来的应用程序主线程。
  5. 当应用程序主线程在进入空闲等待前,会认为当前线程处理空闲状态,于是就会调用那些已经注册了的IdleHandler接口,使得应用程序有机会在空闲的时候处理一些事情。

以上是关于Handler 机制和原理-Android的主要内容,如果未能解决你的问题,请参考以下文章

这都 2022 年了,Android开发还有人在研究 Handler源码?

这都 2022 年了,Android开发还有人在研究 Handler源码?

Android的handler机制的原理?

Android消息机制和原理

Handler 机制和原理-Android

Handler 机制和原理-Android