handler源码分析及手写handler


小实例

private Handler mHandler = new Handler(){
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        System.out.println(msg.what);
    }
};
@Override
public void onCreate(Bundle savedInstanceState, PersistableBundle persistentState) {
    super.onCreate(savedInstanceState, persistentState);
    setContentView(R.layout.activity_main);
    new Thread(new Runnable() {
        @Override
        public void run() {
            //进行耗时操作后
            Message message = Message.obtain();
            message.what = 1;
            mHandler.sendMessage(message);
        }
    }).start();
}

在子线程中,进行耗时操作,执行完操作后,发送消息,通知主线程更新UI。

子线程中创建handler

如果在子线程中创建handler:

new Thread(){
    @Override
    public void run() {
        Handler handler = new Handler();
    }
}.start();

代码运行报错:

java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()

可以修改为:

new Thread(){
    @Override
    public void run() {
        Looper.prepare();
        Handler handler = new Handler();
        Looper.loop();
    }
}.start();

在主线程中不需要,究其原因,在ActivityThread的main()方法中:

...	
Looper.prepareMainLooper();//准备循环
...
Looper.loop();//循环
....

其中prepareMainLooper()方法会调用prepare(false)方法。

源码流程分析

分析源码的思路,就是小实例中,从子线程调用mHandler.sendMessage(message);到主线程更新数据执行handleMessage(Message msg)方法的流程。

第一步

sendMessage到MessageQueue:Handler.sendMessageHandler.sendMessageDelayedHandler.sendMessageAtTimeMessageQueue.enqueueMessage

Handler中sendMessageAtTime()方法

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    //其中mQueue是消息队列,从Looper中获取的
    MessageQueue queue = mQueue;
    if (queue == null) {
        RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
        Log.w("Looper", e.getMessage(), e);
        return false;
    }
    //调用enqueueMessage方法
    return enqueueMessage(queue, msg, uptimeMillis);
}

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    //注意此处的msg.target就是当前的Handler,Looper中的loop会使用。
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    //调用MessageQueue的enqueueMessage方法
    return queue.enqueueMessage(msg, uptimeMillis);
}

可以看到sendMessageAtTime()方法的作用很简单,就是调用MessageQueue的enqueueMessage()方法,往消息队列中添加一个消息。

知识扩展:在子线程中通过Handler的post()方式或send()方式发送消息,最终都是调用了sendMessageAtTime()方法。子线程中调用Activity中的runOnUiThread()中更新UI,其实也是发送消息通知主线程更新UI,最终也会调用sendMessageAtTime()方法。

MessageQueue中enqueueMessage()方法

boolean enqueueMessage(Message msg, long when) {
    // 每一个Message必须有一个target
    if (msg.target == null) {
        throw new IllegalArgumentException("Message must have a target.");
    }
    if (msg.isInUse()) {
        throw new IllegalStateException(msg + " This message is already in use.");
    }
    synchronized (this) {
        //正在退出时,回收msg,加入到消息池
        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) {

            //p为null(代表MessageQueue没有消息) 或者msg的触发时间是队列中最早的(即第一个msg),则进入该该分支.
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked;
        } else {
            
            //将消息按时间顺序插入到MessageQueue。
            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;
        }
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

MessageQueue是按照Message触发时间(when)的先后顺序排列的,队头的消息是最早触发的消息。当有消息需要加入消息队列时,会从队列头开始遍历,直到找到消息应该插入的合适位置,以保证所有消息的时间顺序。MessageQueue消息队列采用链表存储Message,因为链表是增加删除快,数组是查询快

第一步总结:Handler.sendMessage是把Message加入了消息队列(MessageQueue)中,消息队列采用的是链表存储方式,按照when也就是时间排序。

第二步

我们知道主线程中,已经创建了Looper,在Loop方法中消息循环调用了Handler.handleMessage。

初始化Looper

无参情况下,默认调用prepare(true);表示的是这个Looper可以退出,而对于false的情况则表示当前Looper不可以退出。

Looper中

public static void prepare() {
        prepare(true);
}
    
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));
}

调用sThreadLocal.set,在ThreadLocal中

public void set(T value) {
    Thread t = Thread.currentThread();
    //从线程中获取ThreadLocalMap ,一个线程中只有一个ThreadLocalMap
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

一个线程中只有一个ThreadLocalMap,一个线程中只能创建一个Looper,ThreadLocalMap中把ThreadLocal为key,Looper为value,存储起来。其中ThreadLocal是线程本地存储区(Thread Local Storage,简称为TLS),每个线程都有自己的私有的本地存储区域,不同线程之间彼此不能访问对方的TLS区域。

开启Looper

public static void loop() {
    //获取线程中TLS存储的Looper对象 
    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }

    //获取Looper对象中的消息队列
    final MessageQueue queue = me.mQueue;
    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();

    //进入loop的主循环方法,死循环
    for (;;) {
        Message msg = queue.next(); //可能会阻塞,因为next()方法可能会无限循环
        if (msg == null) { //消息为空,则退出循环
            // No message indicates that the message queue is quitting.
            return;
        }

        try {

            //重点地方,获取msg的目标Handler,然后通过handler去执行Message这个时候就调用了handleMessage方法
            msg.target.dispatchMessage(msg);
            end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        
        // 回收消息
        msg.recycleUnchecked();
    }
}

loop()进入循环模式,直到消息为空时退出循环:读取MessageQueue的下一条Message;把Message分发给相应的target。

当next()取出下一条消息时,队列中已经没有消息时,next()会无限循环,产生阻塞。等待MessageQueue中加入消息,然后重新唤醒。

Handler创建

创建Handler

public Handler() {
    this(null, false);
}

public Handler(Callback callback, boolean async) {

    //必须先执行Looper.prepare(),才能获取Looper对象,否则为null.
    mLooper = Looper.myLooper();  //从当前线程的TLS中获取Looper对象
    if (mLooper == null) {
        throw new RuntimeException("");
    }
    mQueue = mLooper.mQueue; //消息队列,来自Looper对象
    mCallback = callback;  //回调方法
    mAsynchronous = async; //设置消息是否为异步处理方式
}

对于Handler的无参构造方法,默认采用当前线程TLS中的Looper对象,并且callback回调方法为null,且消息为同步处理方式。只要执行了Looper.prepare()方法,那么便可以获取有效的Looper对象。

Handler分发消息

Looper.loop()方法中,获取到下一条消息后,执行msg.target.dispatchMessage(msg),来分发消息到目标Handler对象。

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        //当Message存在回调方法,回调msg.callback.run()方法;
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            //当Handler存在Callback成员变量时,回调方法handleMessage();
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        //Handler自身的回调方法handleMessage()
        handleMessage(msg);
    }
}

private static void handleCallback(Message message) {
        message.callback.run();
}

public void handleMessage(Message msg) {
}

第三步

获取消息:当发送了消息后,在MessageQueue维护了消息队列,然后在Looper中通过loop()方法,不断地获取消息。上面对loop()方法进行了介绍,其中最重要的是调用了queue.next()方法,通过该方法来提取下一条信息。下面我们来看一下next()方法的具体流程。

MessageQueue中

Message next() {
    final long ptr = mPtr;
    if (ptr == 0) { //当消息循环已经退出,则直接返回
        return null;
    }
    int pendingIdleHandlerCount = -1; // 循环迭代的首次为-1
    int nextPollTimeoutMillis = 0;
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }
        //阻塞操作,当等待nextPollTimeoutMillis时长,或者消息队列被唤醒,都会返回
        nativePollOnce(ptr, nextPollTimeoutMillis);
        synchronized (this) {
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            if (msg != null && msg.target == null) {
                //当消息Handler为空时,查询MessageQueue中的下一条异步消息msg,为空则退出循环。
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            if (msg != null) {
                if (now < msg.when) {
                    //当异步消息触发时间大于当前时间,则设置下一次轮询的超时时长
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    // 获取一条消息,并返回
                    mBlocked = false;
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    //设置消息的使用状态,即flags |= FLAG_IN_USE
                    msg.markInUse();
                    return msg;   //成功地获取MessageQueue中的下一条即将要执行的消息
                }
            } else {
                //没有消息
                nextPollTimeoutMillis = -1;
            }
         //消息正在退出,返回null
            if (mQuitting) {
                dispose();
                return null;
            }
            ...............................
    }
}

nativePollOnce是阻塞操作,其中nextPollTimeoutMillis代表下一个消息到来前,还需要等待的时长;当nextPollTimeoutMillis = -1时,表示消息队列中无消息,会一直等待下去。可以看出next()方法根据消息的触发时间,获取下一条需要执行的消息,队列中消息为空时,则会进行阻塞操作。

总结

消息机制主要包含部分:

Message:需要传递的消息,可以传递数据;

MessageQueue:消息队列,但是它的内部实现并不是用的队列,实际上是通过一个单链表的数据结构来维护消息列表,因为单链表在插入和删除上比较有优势。主要功能向消息池投递消息(MessageQueue.enqueueMessage)和取走消息池的消息(MessageQueue.next);

Handler:主要功能向消息池发送各种消息事件(Handler.sendMessage)和处理相应消息事件(Handler.handleMessage);

Looper:不断循环执行(Looper.loop),从MessageQueue中读取消息,按分发机制将消息分发给目标处理者。

在主线程中,默认已经调用了Looper.preper()方法,调用该方法的目的是在Looper中创建MessageQueue成员变量并把Looper对象绑定到当前线程中。当调用Handler的sendMessage()方法的时候就将Message对象添加到了Looper创建的MessageQueue队列中,同时给Message指定了target对象,其实这个target对象就是Handler对象。主线程默认执行了Looper.looper()方法,该方法从Looper的成员变量MessageQueue中取出Message,然后调用Message的target对象的handleMessage()方法。这样就完成了整个消息机制。

每个线程中只能存在一个Looper,Looper是保存在ThreadLocal中的。主线程(UI线程)已经创建了一个Looper,所以在主线程中不需要再创建Looper,但是在其他线程中需要创建Looper。每个线程中可以有多个Handler,即一个Looper可以处理来自多个Handler的消息。 Looper中维护一个MessageQueue,来维护消息队列。MessageQueue有一组待处理的Message,消息队列中的Message可以来自不同的Handler。

思考

在Activity中使用Handler的时候如何去除警告信息

将Handler类声明为static,同时在Handler类中使用弱引用去引用Context对象。

static class MyHandler extends Handler {
    private SoftReference<Context> srf;
    public MyHandler(Context context) {
        srf = new SoftReference<Context>(context);
    }
    @Override
    public void handleMessage(Message msg) {
        Toast.makeText(srf.get(), msg.toString(), Toast.LENGTH_SHORT).show();
    }
}

Android中为什么主线程不会因为Looper.loop()里的死循环卡死

主线程,我们是绝不希望会被运行一段时间,自己就退出,那么如何保证能一直存活呢?简单做法就是可执行代码是能一直执行下去的,死循环便能保证不会被退出,例如,binder线程也是采用死循环的方法,通过循环方式不同与Binder驱动进行读写操作,当然并非简单地死循环,无消息时会休眠。但这里可能又引发了另一个问题,既然是死循环又如何去处理其他事务呢?通过创建新线程的方式。

真正会卡死主线程的操作是在回调方法onCreate/onStart/onResume等操作时间过长,会导致掉帧,甚至发生ANR,looper.loop本身不会导致应用卡死。

ActivityThread实际上并非线程,ActivityThread并没有真正继承Thread类,只是往往运行在主线程,该人以线程的感觉,其实承载ActivityThread的主线程就是由Zygote fork而创建的进程。

主线程的死循环一直运行是不是特别消耗CPU资源呢?其实不然,这里就涉及到Linux pipe/epoll机制,简单说就是在主线程的MessageQueue没有消息时,便阻塞在loop的queue.next()中的nativePollOnce()方法里,此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒主线程工作。主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。

Activity的生命周期都是依靠主线程的Looper.loop,当收到不同Message时则采用相应措施:

在H.handleMessage(msg)方法中,根据接收到不同的msg,执行相应的生命周期。

比如收到msg=H.LAUNCH_ACTIVITY,则调用ActivityThread.handleLaunchActivity()方法,最终会通过反射机制,创建Activity实例,然后再执行Activity.onCreate()等方法。

手写handler实现

实现代码github


文章作者:
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 !
  目录