Handler机制和原理分析(一篇搞懂)

一、基础概念

  • UI线程
    主线程 ActivityThread ,主线程也是Ui线程,应用启动的时候会启动一个ui线程
  • Handler
    负责发送消息和处理消息。
  • Looper
    负责消息循环,循环取出 MessageQueue 里面的 Message,并交给相应的 Handler 进行处理。
  • MessageQueue
    消息队列,用来存放通过Hangdler 发送的消息,按照先进先出的顺序取出消息,内部使用的是单链表结构(优先级链表)
  • Message
    Handler发送和处理的消息个体,有MessageQueue 管理,携带Handler信息和具体消息。
抽象概念具体化:
  • Handler:快递员(收件(发送消息)和派件(处理消息))(收发都是统一快递员,只属于同一家快递公司)
  • Message:快递包裹(包裹中携带物品,和快递员身份消息)
  • MessageQueue:快递分拣中心(将快递按照时间优先级整理好包裹)
  • Looper:快递公司(不停地发送消息)
    在这里插入图片描述

二、Looper

1、先看一下Looper的构造方法:
	private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

从这个构造方法中可以看到,Looper在创建的时候会创建一个MessageQueue,

2、Looper中有两个比较重要的方法 prepare( ) 和 loop( )
  • 方法一:prepare( )
	//定义一个ThreadLocal 存放 Looper 每个线程会有唯一的sThreadLocal
	static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
	private static Looper sMainLooper;  // guarded by Looper.class

	//准备初始化looper
 	public static void prepare() {
        prepare(true);
    }
	
	private static void prepare(boolean quitAllowed) {
		//一个线程只有一个Looper
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
		//创建一个Looper
        sThreadLocal.set(new Looper(quitAllowed));
    }

	 public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

	  public static @Nullable Looper myLooper() {
		//从ThreadLocal中拿出Looper
        return sThreadLocal.get();
    }	

这里需要对ThreadLocal有一定的了解

ThreadLocal 是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储以后,只有在指定线程中可以获取到存储的数据,对于其他线程来说则无法获取到数据,这样就保证了一个线程对应了一个 Looper,从源码中也可以看出一个线程也只能有一个 Looper,否则就会抛出异常。

  • prepareMainLooper() 方法是 系统在 ActivityThread 中调用的。

问题:一个线程中可以有几个Looper?
一个线程只有一个Looper,由于ThreadLocal保证了,一个线程中只能有一个Looper,并且通过

if (sThreadLocal.get() != null) {
throw new RuntimeException(“Only one Looper may be created per thread”);
}

可以保证线程中的Looper不会被替换,Looper一旦创建无法修改。

  • 方法二:loop() 开启永动机,一直在轮询消息
	public static void loop() {
        final Looper me = myLooper();
        if (me == null) {//如果Loop没有创建,就会抛出异常,prepare() 要先执行。
						//所以如果我们在子线程中使用 Handler 则必须手动调用 Looper.prepare() 和 Looper.loop()
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        
		...

		//死循环轮询消息
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            .....
			
            try {
                msg.target.dispatchMessage(msg);//处理消息
                if (observer != null) {
                    observer.messageDispatched(token, msg);
                }
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } catch (Exception exception) {
                if (observer != null) {
                    observer.dispatchingThrewException(token, msg, exception);
                }
                throw exception;
            } finally {
                ThreadLocalWorkSource.restore(origWorkSource);
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }

           ....

            msg.recycleUnchecked();// 回收消息
        }
    }
  • 通过上面的代码我们可以知道,开启Looper.loop();首先要先获取Looper,获取不到则会报错,所以在子线程中必须手动调用 Looper.prepare() 和 Looper.loop()。 主线程中为啥不用调动呢,那是因为在主线程初始化的时候就已经调用了。
    系统在代码中提供了示例代码
	class LooperThread extends Thread {
            public Handler mHandler;

            public void run() {
                Looper.prepare();

                mHandler = new Handler() {
                    public void handleMessage(Message msg) {
                        // process incoming messages here
                    }
                };

                Looper.loop();
            }
        }
  • 然后就是进入了一个死循环,Message msg = queue.next(); 通过这个方法从 Looper 的 MessageQueue 中去获取 Message。
    queue.next();是一个阻塞方法,没有消息会等待。当有消息可以获取到的时候就会调用 msg.target.dispatchMessage(msg);处理消息。

msg.target 指的发送和处理消息的Handler。 这个方法就会调用我们handleMessage(Message msg)这个方法处理消息。
所以上面的问题就可以回答了,Looper 不需要考虑怎么区分 Message 由哪个 Handler 处理,只负责开启消息循环接收消息并处理消息即可。处理完消息后会调用 msg.recycleUnchecked() 来回收消息。

如何停止消息

Looper提供了两个方法来停止消息

	 public void quit() {
        mQueue.quit(false);
    }

	 public void quitSafely() {
        mQueue.quit(true);
    }

这两个方法都会调动MessageQueue中的 void quit(boolean safe)

	 void quit(boolean safe) {
        if (!mQuitAllowed) {
            throw new IllegalStateException("Main thread not allowed to quit.");
        }

        synchronized (this) {
            if (mQuitting) {
                return;
            }
            mQuitting = true;//注意,这里会结束循环。。。

            if (safe) {
                removeAllFutureMessagesLocked();
            } else {
                removeAllMessagesLocked();
            }

            // We can assume mPtr != 0 because mQuitting was previously false.
            nativeWake(mPtr); //这里会停止消息阻塞
        }
    }

MessageQueue  -->
	Message next() {
        
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;//直接返回null
        }
	}
Looper --loop();
	for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;  //直接return 结束了循环
            }
	}

调用Message的quit()方法的时候会调用native方法将 唤醒阻塞方法。mQuitting = true;结束循环
–> MessageQueue 的next() 里面的死循环就会执行if(ptr == 0)return null。 停止掉了死循环。
–> 由于next()返回为null ,所以在Looper 的loop() 中。if(msg==nul) return; 停止掉了loop这个永动机。

Message(消息)

Message就是一个承载消息的的类,

Message 只有一个无参的构造方法,除了构造方法为还可以通过静态的 Obtain()来获取重用对象。

	public static final Object sPoolSync = new Object();    //同步锁对象 
    private static Message sPool;                           //全局池消息实例 sPool为最近一个可以使用的空消息对象

	private static final int MAX_POOL_SIZE = 50;    

	/**
     * 从全局池返回一个新的消息实例,允许我们在许多情况下避免分配新对象。
     */
    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();
    }

	//回收之后的消息会被存储在消息池中,消息池的最大为 MAX_POOL_SIZE = 50
	void recycleUnchecked() {
        ....
        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }
  • 如果当前全局的消息池的Message实例不为空,就会返回第一个消息实例,并且将消息池指针后移。 回收之后的消息也会加入到消息池中。所以在使用Message 的 obtain();方法可以很好的重复利用资源,避免消耗更多的系统资源。当消息数小于50的时候消息可以一直重复利用。
  • Message中有一个next属性,可以存储下一个消息对象,也让消息池和MessageQueue中的消息组成了一个单向链表。

Handler

Handler的构造方法中,空构造方法中使用的是

	 public Handler(@Nullable Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }

        mLooper = Looper.myLooper();//获取当前线程的Looper
        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;
    }

handler在初始化的时候就已经拿到了当前线程的Looper。前面Looper的代码已经通过ThreadLocal限定了一个线程只有一个Looper,并且拿到了Looper中的MessageQueue。
所以handler在创建的时候就会持有当前线程的Looper和MessageQueue。

Handler 发送消息的方法。

Handler 有多个可以发送消息的方法,最后都是调用了sendMessageAtTime(@NonNull Message msg, long uptimeMillis) 方法

	public final boolean post(@NonNull Runnable r) {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

	public final boolean postDelayed(@NonNull Runnable r, long delayMillis) {
        return sendMessageDelayed(getPostMessage(r), delayMillis);
    }

	public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

	public final boolean postAtTime(@NonNull Runnable r, long uptimeMillis) {
        return sendMessageAtTime(getPostMessage(r), uptimeMillis);
    }

	 public final boolean postAtTime(
            @NonNull Runnable r, @Nullable Object token, long uptimeMillis) {
        return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
    }
  • 这里看一下sendMessageAtTime的调用流程。
  • sendMessageAtTime(Handler) -> enqueueMessage(Handler)->enqueueMessage(MesssageQueue)
  • 上面的这个流程就将消息发送到了MessageQueue中。在MessageQueue中enqueueMessage 将发过来的消息组成了一个优先级队列,等待loop()取数据。
    在这里插入图片描述
再来仔细看一下Handler的enqueueMessage()方法
 private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
        msg.target = this;
        msg.workSourceUid = ThreadLocalWorkSource.getUid();

        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

这个方法主要的关注点就是msg.target = this; message 中已经将Hangler 携带到message中了。所以message发出去之后由谁来接收处理就不会迷路。

还有一个方法需要了解一下。
 	new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                Log.e("Handler", "Handler --- > :" + Thread.currentThread().getName());
            }
        }, 5000);

这样一个写法肯定都比较熟悉,那么这个方法的调用流程是什么呢。

1、Handler里面的

	 public final boolean postDelayed(@NonNull Runnable r, long delayMillis) {
        return sendMessageDelayed(getPostMessage(r), delayMillis);
    }
	//组装好消息,将Runnable 赋给 Message.callback
  	private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

	//处理消息的
  	public void dispatchMessage(@NonNull Message msg) {
        if (msg.callback != null) {//callback不为空
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
	//调用run方法。
	 private static void handleCallback(Message message) {
        message.callback.run();//执行run方法。
    }

可以看出Runable post之后会发到一个Message 里面去。处理消息的时候如果 callback不为空,就会调用handleCallback 方法,这个方法调用.run() 方法,所以这个方法是运行在Hangler所属的线程中的。不一定是子线程。

Thread 只有在执行了 .start()方法才会在子线程中运行,直接调动.run()不会启动子线程,只会在所属线程运行。

处理消息

处理消息就比较简单了。 直接调用 handleMessage(msg),非常熟悉了。

MessageQueue

MessageQueue就是存储消息的一个队列。持有链条是这样的。 一个线程会有 唯一的一个Looper —>唯一的Looper中会有唯一的messageQueue。
Handler 通过looper-messagequeue 往队列里面添加消息。同一个线程的消息就会放到同一个MessageQueue中,被同线程的Looper循环取出。

  • MessageQueue有两个比较重要的方法,分别是,enqueueMessage() 和 next() 他们一个是存消息一个数取消息。

enqueueMessage()

	 boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }

        synchronized (this) {
            if (msg.isInUse()) {
                throw new IllegalStateException(msg + " This message is already in use.");
            }

            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 {
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                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;
    }

通过上面的代码可以看出,消息在添加到队列的时候就要进行按照时间的排序。形成一个优先级的队列。
还有就是提供给Looper.loop()调用的next()方法,这个方法代码较长不贴出自己去看一下。

总结就是MessageQueue就是将详细按照时间排好队,然后按照先后顺序让loop取出,发给handler处理。

Handler 原理的总结

Handler原理的整个流程总结下来就是:

handler将消息发送到Looper的消息队列中(messageQueue),
messageQueue 将数据按照时间先后排好队,等待Looper.loop()按照先后顺序取出Message
Looper.loop()取出消息之后,调用消息的Message的target,即附属的Handler的dispatchMessage()方法,将该消息回调到handleMessage()方法中.
handler 在 handleMessage(msg)方法中处理我们自己的逻辑。
在这里插入图片描述

问题

  • 1、一个线程有几个Handler?一个线程有几个Looper?

一个线程可以有多个Handler,可以直接new。
一个线程只有一个Looper,由ThreadLocal控制。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值