欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Handler源码原理分析

程序员文章站 2024-01-27 21:12:40
...

Android 系统内部是消息机制,什么意思?
说说Handler运行机制?

Handler功能定义:各线程互相通讯用的。常用子线程在做完某事之后然后去更新UI之类。当然两个子线程也能用Handler相互通讯。

本文主要分析两个线程是怎么通信的?通过源码分析原理是什么?

子与主线程通讯用法:

 Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch(msg.what){
                case 100:
                    String s = msg.what+" arg1="+msg.arg1+" arg2="+msg.arg2+" obj="+msg.obj;
                    mTextView.setText(s);

                    Logger.d(s);
                  break;
                default:

                  break;
            }
        }
    };

....
 new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            Thread.sleep(3000);
                            mHandler.sendEmptyMessage(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }).start();

子与子线程通讯用法:

new Thread("thread2"){
            public void run() {
                Looper.prepare();
                mHandler = new Handler(){
                    @Override
                    public void handleMessage(Message msg) {
                        super.handleMessage(msg);
                        String s = msg.what+" arg1="+msg.arg1+" arg2="+msg.arg2+" obj="+msg.obj;
                        Logger.d(s);
                    }
                };
                Looper.loop();
            }
        }.start();

发送时不变,还是用handler对象直接发送空消息,或者创建消息发送。
就多了Looper.prepare()Looper.loop(); 等下会讲为什么。

需要分析的类

Message:消息对象,用到存放数据,和下一个Message对象
MessageQueue:字面意思消息队列,但其实它并不是队列,不过确实是负责消息的插入和取出
Handler:给用户层用来创建和发送消息,和处理收到的消息
Looper:不断循环取消息,和调用Handler处理消息
(说实话,这种看别人简单的总结根本不能理解,肯定想问,这到底是Handler发送处理消息,还是Looper发送处理消息?等等疑问。这都个人理解,还是看源码说吧)

先来看传递的对象Message,有几个常用属性。

**Message.class**
    public int what; //区分消息的标识,在发送消息和接收时确认
    public int arg1,arg2; //可以传递两个int值
    public Object obj; //其它想要传递的对象

然后直接进Handler 类 mHandler.sendEmptyMessage(100);看看发送时做了什么。

**Handler.class**
public final boolean sendMessage(Message msg){
        return sendMessageDelayed(msg,0);
}
...
//延迟多少毫秒发送
public final boolean sendMessageDelayed(Message msg,long delayMillis){
        if(delayMillis< 0){
            delayMillis=0;
        }
//        SystemClock.uptimeMillis()系统开机时间
        return sendMessageAtTime(msg,SystemClock.uptimeMillis()+delayMillis);
}
//在一个准确时间发送
public boolean sendMessageAtTime(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);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

可以看到,不管是sendMessage,还是sendMessageDelayed等……最后都是调用了sendMessageAtTime,然后enqueueMessage,又分发给了queue.enqueueMessage(msg, uptimeMillis),然后发现有个MessageQueue queue,看在哪赋值?在Handler构造函数中,Handler构造函数也很多,最终进:

**Handler.class**
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;
    }

观察到mQueue是从Looper中拿来的,如果Looper为空,就throw new RuntimeException(),那进Looper.myLooper();

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

看sThreadLocal赋值

 **Looper.class**
 // sThreadLocal.get() will return null unless you've called prepare().
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

直接在初始化就定义了,并且static final,说明一个进程只有一个,并无再赋值,然后看ThreadLocal是什么?
ThreadLocal 定义:与线程想关的数据存储类。

ThreadLocal 使用

ThreadLocal<Boolean> mThreadLocal=new ThreadLocal<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);

        mThreadLocal.set(true);
        Logger.d(Thread.currentThread() + " " + mThreadLocal.get());
        new Thread(new Runnable() {
            @Override
            public void run() {
                Logger.d(Thread.currentThread() + " " + mThreadLocal.get());
                mThreadLocal.set(false);
                Logger.d(Thread.currentThread() + " " + mThreadLocal.get());

            }
        }).start();
    }
**log**
D/: Thread[main,5,main] true
D/: Thread[Thread-221,5,main] null
D/: Thread[Thread-221,5,main] false

用法简单。只有get、set、remove。可以看到,主线程set(true),get()是true。在子线程中,第一次get,因为没有set,直接为null。然后set(false),取出false,说明在子线程中,并没有get到主线程set的值。看看实现:

**ThreadLocal.class**
public class ThreadLocal<T> {

    public T get() {
        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);
    }
    public void set(T value) {
        Thread currentThread = Thread.currentThread();
        Values values = values(currentThread);
        if (values == null) {
            values = initializeValues(currentThread);
        }
        values.put(this, value);
    }
    ...
    Values values(Thread current) {
        return current.localValues;
    }
     static class Values {
        private Object[] table;
     }
}

Values 为ThreadLocal 的静态内部类,可以看到,数据最终存储的地方在Thread对象本身里的localValues.table数组里,ThreadLocal只是判断线程,然后把数据有序在放在table里,这里用了一个高效存储算法,table[i]为key,[i+1]为value,大概就是这样意思,这样可能效率性能会好吧!
如果Thread对象本身Values存储怎么办?实现这个和线程相关的存储数据有很多方法,自己也可以实现,下面自己实现一个:

//自己写的ThreadLocal
public class ThreadLocal<V> {

    private Map<Long,V> mMap;

    public ThreadLocal() {
        mMap = new HashMap<>();
    }

    public V get(){
        long id = Thread.currentThread().getId();
        return mMap.get(id);
    }

    public void put(V value){
        long id = Thread.currentThread().getId();
        mMap.put(id,value);
    }
}

用线程唯一标识为Key就行,效率可能没google写的ThreadLocal好。
再回到Looper.myLooper()方法。

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

这里线程取到的肯定为空,唯一赋值方法在

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));
}

所以我们在子线程初始化Handler时,才要Looper.prepare();为了实例化Looper,而且每个线程还只能调用一次,那我们的主线程在那调用的呢?那就要到ActivityThread.class看看了,ActivityThread就是每个应用的大门,应用的main函数。activity、service、broadcasts等都是在这里启动。

public final class ActivityThread {
    ...
    public static void main(String[] args) {
        ...省略代码若干
         Looper.prepareMainLooper();
        ActivityThread thread = new ActivityThread();
        thread.attach(false);
        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
        ...
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

    final Handler getHandler() {
        return mH;
    }
}

可以看到,有熟悉的java的main,new Handler()之前调用Looper.prepareMainLooper();这个内部也是prepare(),而且这个Handler处理这个应用系统所有Message,Activity生命周期、createService等都是这个handler进行分发。这个类以后要好好研究,所以为什么这就是android系统内部的消息机制,无论点击按钮,启动页面,都是通过Handler进行发送消息然后分发到各个类处理。

Ok,创建了Looper,创建了MessageQueue,然后看MessageQueueenqueueMessage方法

boolean enqueueMessage(Message msg, long when) {
  ...
    synchronized (this) {
        ...
        msg.markInUse();
        msg.when = when;
        Message p = mMessages;//单链表的第一个message
        boolean needWake;//是否需要唤醒,如果链表中没有待处理的message,主线程将睡眠
        if (p == null || when == 0 || when < p.when) {
            // New head, wake up the event queue if blocked.
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked;//上一个message为空,说明这是要处理message队列的第一个,将唤醒主线程,这里会为true
        } else {
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            //这一段逻辑是取出链表中的最后一个Message prev,把新的msg赋值给它的next
            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);//为true,将唤醒主线程
        }
    }
    return true;
}

这里可以看到,当前msg是赋值给最后一个msg的next,这里像是单链表。如图:
Handler源码原理分析
Message mMessages;即是链表第一个msg,也就是最快将要处理的msg(如果不为空的话)
这里有两种情况,else表示链表不为空,通过for循环,定位到最后一个msg,然后把新传递进来的msg赋值给最后一个msg的next。这就算是添加成功了。if表示链表为空(如果你进入一个新页面,不做任何操作,msg队列将会被处理完,然后主线程会睡眠,当有新的msg进来时,要唤醒主线程) nativeWake(mPtr);是个native方法,唤醒主线程。

到此,msg就算是添加到队列中了,然后在哪里处理呢?
这就要看ActivityThread中main方法最后调用了Looper.loop();因为这个会阻塞线程,所以要放在线程最后调用。

 public static void loop() {
        final Looper me = myLooper();
        ...
        final MessageQueue queue = me.mQueue;
        ...
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                return;
            }
            Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            msg.target.dispatchMessage(msg);

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }
            ...
        }
    }

简单理解就是循环从MessageQueuenext();方法取msg,为空将结束循环,否则msg.target.dispatchMessage(msg);这里msg.target就是创建msg时的handler,进
dispatchMessage方法。

**Handler.class**
 public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

如果msg有callback,这个callback是Runnable,否则检查mCallback不为空,调用mCallback.handleMessage(msg),最后调用自身的handleMessage(msg);,我们一般都是直接new Handler(),重写handleMessage(msg)方法。可以看见传递callback也是可以的。这里比较简单。

然后重点看queue.next(),也就是取出msg方法

**MessageQueue.class**
 Message next() {
        int nextPollTimeoutMillis = 0;
        for (; ; ) {
            ...
            nativePollOnce(ptr, nextPollTimeoutMillis);
            ...
            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                ...
                if (msg != null) {
                    // Got a message.
                    mBlocked = false;
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    msg.markInUse();
                    return msg;

                } else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }
                ...
            }
        }
    }

大致为mMessages是否为空,不为空就把return mMessages出去,并且next标记清掉,给mMessages = msg.next;赋于下一个msg。为空的话nextPollTimeoutMillis = -1;下次循环中,
nativePollOnce(ptr, nextPollTimeoutMillis);为-1时将睡眠。

而且我们还发现Printer logging = me.mLogging;会调用Printer.println(String n),说明我们可以监听到msg的处理,例:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            Looper.myLooper().setMessageLogging(new Printer(){
                @Override
                public void println(String x) {
                    Log.d("TAG",x);
                }
            });
            Looper.myLooper().getQueue().addIdleHandler(new MessageQueue.IdleHandler() {
                @Override
                public boolean queueIdle() {//消息队列处理完毕回调,return false 将只能监听一次结束
                    return true;
                }
            });
        }

addIdleHandler 可以监听消息队列处理完毕,setMessageLogging可以监听消息处理前后,打打log:

D/: >>>>> Dispatching to Handler (android.view.Choreographer$FrameHandler) {c0d260e} android.view.Choreographer$FrameDisplayEventReceiver@5a82f: 0
D/: <<<<< Finished to Handler (android.view.Choreographer$FrameHandler) {c0d260e} android.view.Choreographer$FrameDisplayEventReceiver@5a82f
D/: >>>>> Dispatching to Handler (android.view.ViewRootImpl$ViewRootHandler) {650110} android.view.Viewaaa@qq.com: 0
D/: <<<<< Finished to Handler (android.view.ViewRootImpl$ViewRootHandler) {650110} android.view.Viewaaa@qq.com
D/: >>>>> Dispatching to Handler (android.view.ViewRootImpl$ViewRootHandler) {650110} android.view.Viewaaa@qq.com: 0
D/: <<<<< Finished to Handler (android.view.ViewRootImpl$ViewRootHandler) {650110} android.view.Viewaaa@qq.com
D/: >>>>> Dispatching to Handler (android.app.ActivityThread$H) {b0be8e6} null: 101
D/: <<<<< Finished to Handler (android.app.ActivityThread$H) {b0be8e6} null
D/: >>>>> Dispatching to Handler (android.view.ViewRootImpl$ViewRootHandler) {650110} null: 6
D/: <<<<< Finished to Handler (android.view.ViewRootImpl$ViewRootHandler) {650110} null
D/: >>>>> Dispatching to Handler (android.view.Choreographer$FrameHandler) {c0d260e} android.view.Choreographer$FrameDisplayEventReceiver@5a82f: 0
D/: <<<<< Finished to Handler (android.view.Choreographer$FrameHandler) {c0d260e} android.view.Choreographer$FrameDisplayEventReceiver@5a82f
D/: >>>>> Dispatching to Handler (android.app.ActivityThread$H) {b0be8e6} null: 100

总结:
Handler消息机制就是,主线程不断循环(Looper.loop)检查有无待处理msg(queue.next),有就处理(handler.dispatchMessage()),没有就睡眠(nativePollOnce())。当子线程发送了一个msg时(queue.enqueueMessage),唤醒主线程(nativeWake()),主线程就接着处理(queue.next),从而实现了子线程与主线程通信。原理也就是子线程与主线程共同操作一个队列,一存,一取。原理简单粗暴,不过nativePollOnce睡眠、nativeWake唤醒、两个为native方法,肯定不是sleep那么简单。