通透的分析handler的工作流程

Handler的使用

主线程中使用handler



static class MyHandler extends Handler {
    WeakReference<Activity > mActivityReference;MyHandler(Activity activity) {
        mActivityReference= new WeakReference<Activity>(activity);
    }@Override
    public void handleMessage(Message msg) {
        final Activity activity = mActivityReference.get();
        if (activity != null) {
           if (msg.what == 1){
            Toast.makeText(JavaDemo.this, "更新UI操作", Toast.LENGTH_SHORT).show();
        };
        }
    }
}

private MyHandler handler=new MyHandler(this);

@Override
protected void onCreate(Bundle savedInstanceState) {
​
​
    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                Thread.sleep(5000);
                handler.sendEmptyMessage(1);
            } catch (InterruptedException e) {
                e.printStackTrace();}
        }
    }).start();
}

下面我们通过源码具体分析handler内部所干的事儿。

handler的源码主要是由Handler、Looper、MessageQueue、ThreadLocal几个部分组成。

首先从handler的构造方法讲起


public Handler(Callback callback, boolean async) {
    
    mLooper = Looper.myLooper(); 
    if (mLooper == null) { 
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

首先调用了mylooper()方法一会我们进去看一下,之后判断对象是否为空,为空则抛异常,不为空则获取mlooper消息队列,这样Handler也就和消息队里进行了绑定。


public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
}

这里调用了一个get方法

public T get() {
        // Optimized for the fast path.
        Thread currentThread = Thread.currentThread();
        Values values = values(currentThread);
        if (values != null) {
            Object[] table = values.table;
            int index = hash & values.mask;
            if (this.reference == table[index]) {
                return (T) table[index + 1];
            }
        } else {
            values = initializeValues(currentThread);
        }return (T) values.getAfterMiss(this);
    }

从代码中可以看到,get()方法先获取当前线程,如果values不为空则将值返回,如果为空则先赋初值然后再返回初始值。

之后在使用handler的时候一般都会使用sendMessage()方法去发送消息。看看这个方法内部做了什么操作。


public final boolean sendMessage(Message msg)
{
    return sendMessageDelayed(msg, 0);
}
​
​
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
​
​
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;//构造方法里的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);
}

可以看到里面层层递进,sendMessage()里面调用sendMessageDelayed(),sendMessageDelayed()又调用了sendMessageAtTime(),最终发现其实所有的发送消息方法最后都会来到sendMessageAtTime()方法里,于是着重看这个方法。这个方法里先获取消息队列MessageQueue,然后将队列queue、发送的消息msg以及延时时间uptimeMillis一起传入到enqueueMessage()里去。


private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

注意这里绑定了一个msg.target。
在这个方法里,又调用queue.enqueueMessage()方法将发射的消息传入到消息队列MessageQueue当中去。也就是说从handler中发送的消息其实最后全都送到了MessageQueue当中去。

接下来我们分析下looper

Looper主要由两部分东西组成,prepare()和loop()。

首先来看prepare()。
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));
}

可以看到,在prepare()中主要做了两件事。第一,判断ThreadLocal中是否能够取出Looper对象,如果不为空,则抛出"一个线程只能有一个Looper"的异常。这就代表说在一个线程里有且仅可以创建一个Looper,如果多次调用prepare()方法创建Looper则程序会抛出异常。如果发现线程之中没有Looper,那么便会new一个Looper将其set进入ThreadLocal当中去。那么这个ThreadLocal又是什么?

ThreadLocal被称为线程内部存储类。他有一个特点就是在A线程里面进行set()存储的数据,只能在A线程get()取出。


public void set(T value) {
    Thread currentThread = Thread.currentThread();
    Values values = values(currentThread);
    if (values == null) {
        values = initializeValues(currentThread);
    }
    values.put(this, value);
}

我们看到set()方法中,首先会去获取当前线程currentThread,之后通过values从当前线程中获取数据。判断这个数据是否为空“if (values == null)”,为空则调用initializeValues()方法赋初值,否则将获取到的value值put()进入values中“values.put(this, value)”。
还记得我们上面调用的get()方法么,原来handler构造方法中mylooper()方法get()出来的looper是从prepare()方法里set()进去的looper。
由于set()和get()方法都涉及到了从currentThread()中获取数据,这也就解释了为什么在一个线程中存储数据必须要在相同线程中才能取的到的原因。

上面说了在判断ThreadLocal中取出来的数据为空时会去new一个Looper,并把他添加进ThreadLocal中,那我们来看看这个Looper的构造方法。

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

构造方法非常简单,里面实例化一个消息队列MessageQueue,并且还会获取当前线程。也就是说消息队列此时已经和当前线程绑定,其作用的区域为当前实例化Looper的线程 。

我们再来看loop()。
public static void loop() {
    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue;
​
​
    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);
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
​
       
​
        msg.recycleUnchecked();
    }
}

这里对代码进行了一些删减。可以看到首先会调用myLooper()去获取一个Looper对象。而从myLooper()的源码看到从ThreadLocal里取出在prepare()中存入的Looper对象。先判断对象是否为空,若为空,则抛出异常告诉程序在调用loop()方法之前必须要有一个Looper。这也就说在使用的时候,prepare()方法必须要在loop()方法之前被调用。

之后通过Looper对象获取消息队列MessageQueue,进入一个死循环for( ; ; ),调用MessageQueue的next()方法,不断从消息队列里获取消息Message,如果获取的消息为空,则return跳出循环,如果不为空,则将msg消息交给msg.target.dispatchMessage(msg)去处理,那么这个dispatchMessage()又是什么,其实这个就是handler,不过我们后面再分析。最后调用recycleUnchecked()方法回收。到此Looper源码分析完成。

在分析loop的时候我们看到,在loop里面又调用了MessageQueue的next()方法,而之前的handler.sendMessage()最终调用的queue.enqueueMessage()方法将发射的消息传入到消息队列MessageQueue当中去

最后我们来看看dispatchMessage()方法
public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

在handler里面我们找到了这个方法。这个方法会根据是否有callback而去调用不同方法,如果有回调则调用handleCallback(),如果没有回调则调用handleMessage();


public void handleMessage(Message msg) {
}

我们可以看到handleMessage()中是一个空方法,这就代表只要覆写了这个方法所有的一切就全部由我们自己来写逻辑了。

handler的整体流程

1.首先调用Looper.prepare()创建一个Looper对象,prepare()中将looper对象set()进当前线程里。
2.handler初始化时会通过get()方法在当前线程里获取到looper.prepare()里set()进去的looper,然后获取到looper里的mQueue消息队列。
3.然后通过sendMessage()方法,将发送的消息绑定到消息队列中。
4.最后looper通过loop()不断的从消息队列MessageQueue当中取出消息交给dispatchMessage()去处理。
5.dispatchMessage()最后调用handleMessage(),所有的逻辑都交给我们自己去处理。

主线程执行prepare()和loop()的时机是在下图中,所以这些都不用我们手动实现。
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值