Handler机制.源码分析

Posted 杨伟乔

tags:

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

Handler机制的原理 :

android提供了handler looper 来满足线程之间的通信

Handler是先进先出的原则

一个线程可以产生一个looper对象,由它去管理线程里面消息队列 MessageQueue

Handler 你可以构造handler对象来与looper沟通.可以发送消息 和处理消息

 

MessageQueue 用来存放线程放入的消息

 

线程 一般值的是主线程 UIthread

Android启动程序的时候会替他建立一个MessageQueue

 

.Handler创建消息

消息池

 消息池

 

 

 

Handler发送消息

ThreadLocal

Handler 处理消息

 

andriod提供了Handler Looper 来满足线程间的通信。Handler先进先出原则。Looper类用来管理特定线程内对象之间的消息交换(MessageExchange)

1)Looper:
一个线程可以产生一个Looper对象,由它来管理此线程里的MessageQueue(消息队列)
2)Handler:
你可以构造Handler对象来与Looper沟通,以便push新消息到MessageQueue;或者接收LooperMessage Queue取出)所送来的消息。
3) Message Queue(
消息队列):用来存放线程放入的消息。

4)线程:UIthread 通常就是main thread,而Android启动程序时会替它建立一个MessageQueue

1.Handler创建消息

        每一个消息都需要被指定的Handler处理,通过Handler创建消息便可以完成此功能。Android消息机制中引入了消息池。Handler创建 消息时首先查询消息池中是否有消息存在,如果有直接从消息池中取得,如果没有则重新初始化一个消息实例。使用消息池的好处是:消息不被使用时,并不作为垃 圾回收,而是放入消息池,可供下次Handler创建消息时使用。消息池提高了消息对象的复用,减少系统垃圾回收的次数。消息的创建流程如图所示。

2.Handler发送消息

UI主线程初始化第一个Handler时会通过ThreadLocal创建一个Looper,该LooperUI主线程一一对应。使用 ThreadLocal的目的是保证每一个线程只创建唯一一个Looper。之后其他Handler初始化的时候直接获取第一个Handler创建的 LooperLooper初始化的时候会创建一个消息队列MessageQueue。至此,主线程、消息循环、消息队列之间的关系是1:1:1

HandlerLooperMessageQueue的初始化流程如图所示:

Hander持有对UI主线程消息队列MessageQueue和消息循环Looper的引用,子线程可以通过Handler将消息发送到UI线程的消息队列MessageQueue中。

3.Handler处理消息

UI主线程通过Looper循环查询消息队列UI_MQ,当发现有消息存在时会将消息从消息队列中取出。首先分析消息,通过消息的参数判断该消息对应的Handler,然后将消息分发到指定的Handler进行处理。

子线程通过HandlerLooperUI主线程通信的流程如图所示。

 

 

相关的术语 :

通信的同步 Synchronous指向客户端发送请求后,必须要在服务端有回应后客户端才继续发送其它的请求,所以这时所有请求将会在服务端得到同步,直到服务端返回请求。

通信的异步 Asynchronous  指客户端在发送请求后,不必等待服务端的回应就可以发送下一个请求。

 

 

所谓同步调用,就是在一个函数或方法调用时,没有得到结果之前,该调用就不返回,直到返回 结果。异步调用和同步是相对的,在一个异步调用发起后,被调用者立即返回给调用者,但是调用者不能立刻得到结果,被调用都在实际处理这个调用的请求完成 后,通过状态、通知或回调等方式来通知调用者请求处理的结果。

 

 

 

Android的消息处理有三个核心的类

Looper handler message 其实还有一个MessageQueue 但是它被封装在looper,不会直接和它打交道,所以不算是核心的类.


消息类Message  主要的功能是进行消息的封装,同时可以指定消息的操作形式

Int whatobject obj      Handler getTraget

尽管message有默认的构造,但是我们应该是通过Message.ontain() 来从消息池中获取消息对象,节省资源

如果是只是简单地携带int信息,优先使用Message.arg1Message.arg2来传递信息,这比Bundle更省内存

 

善用message.what 来标示信息,以便不同的方式来处理message

 

消息通道 looper 系统会自动创建looper对象,但是如果是用户自定义的一个类中,就需要手动的调用looper中的方法.才可以启动looper对象

表面的意思是循环者,它被设计一个普通的线程变成looper线程,所说的looper线程就是循环工作的线程 .looper.prepare()  ;    处理其他,比如实例化handler ……  looper.loop()

 

一个线程只可以有一个looper对象,为什么???

 

 public class Looper {
    // 每个线程中的Looper对象其实是一个ThreadLocal,即线程本地存储(TLS)对象
    private static final ThreadLocal sThreadLocal = new ThreadLocal();
    // Looper内的消息队列
    final MessageQueue mQueue;
    // 当前线程
    Thread mThread;
    //其他属性
    // 每个Looper对象中有它的消息队列,和它所属的线程
    private Looper() {
        mQueue = new MessageQueue();
        mRun = true;
        mThread = Thread.currentThread();
    }
    // 我们调用该方法会在调用线程的TLS中创建Looper对象
    public static final void prepare() {
        if (sThreadLocal.get() != null) {
            // 试图在有Looper的线程中再次创建Looper将抛出异常
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper());
    }
    // 其他方法
}

 

 看源码 ,可以看出创建looper对象其实就是将looper对象定义为ThreadLocal,如果这个对象以前定义了,妮又尝试着去重新的定义,就是说threaLocal.get()!=null 这个时候对象存在,就会爆异常 .

 

 

 

looper.loop() 这个代表着启动looper 线程开始工作了.它不断的从队列里面取出消息.每次去的是第一个消息.源码分析 :

调用这个方法后,内部的执行流程

 
1.获取当前线程的looper对象 Looper me = myLooper();这个方法内部其实就是返回 ThreadLocal.get()

 

2. 获取消息队列 MessageQueue queue = me.mQueue;

 

3. 开启一个while死循环

用队列的对象取出下一个消息Message msg = queue.next();

判断消息是否为空,如果是空,就不会继续执行了

如果不是空,将消息交给handler msg.target.dispatchMessage(msg);
msg.recycle();回收msg资源

 

 

looper中的其他的方法 myLooper()   getThread()

       quit()  结束looper的循环 
public void quit() {
        // 创建一个空的message,它的targetNULL,表示结束循环消息
        Message msg = Message.obtain();
        // 发出消息
        mQueue.enqueueMessage(msg, 0);
    }

 

综上,Looper有以下几个要点:

1)每个线程有且只能有一个Looper对象,它是一个ThreadLocal

2Looper内部有一个消息队列,loop()方法调用后线程开始不断从队列中取出消息执行

3Looper使一个线程变成Looper线程。

那么,我们如何操作Message Queue上的消息呢?这就是Handler的用处了

 

 

 

 

消息操作类  handler

处理消息,但是很小气,只处理自己发出的消息

 

默认关联当前线程的looper   
mLooper = Looper.myLooper();
判断looper是不是空的,如果是空的就爆异常,就是指handler只可以在looper线程中使用,
关联looper的消息队列,因为他的消息要发送到looper的消息队列中
public class LooperThread extends Thread {
    private Handler handler1;
    private Handler handler2;
 
    @Override
    public void run() {
        // 将当前线程初始化为Looper线程
        Looper.prepare();
        
        // 实例化两个handler
        handler1 = new Handler();
        handler2 = new Handler();
        
        // 开始循环处理消息队列
        Looper.loop();
    }
}
一个线程可以有多个handler 但是只能有一个looper
有了handler之后 就可以发送消息; 
post(Runnable)
 postAtTime(Runnable, long)
 postDelayed(Runnable, long)
 sendEmptyMessage(int)
sendMessage(Message)
sendMessageAtTime(Message, long)
sendMessageDelayed(Message, long)
 
post发送消息最后都是把里面的Runnable封装成了message 
// 此方法用于向关联的MQ上发送Runnable对象,它的run方法将在handler关联的looper线程中执行
Handler机制.源码分析

Handler机制源码分析

Handler机制源码分析

Handler源码分析

从源码分析Handler机制

从源码分析Handler机制