Android Handler源码解析(一)

493 阅读10分钟

前言

看到这个标题,大家肯定有些疑惑,网上关于Hanlder源码分析的文章已经够多了,你为什么还要写?我不否认这类文章真的是一抓一大把,但纸上得来终觉浅,绝知此事要躬行。如果真的想把知识点搞清楚,自己必须要通过查看其源码、明白其工作流程、领悟其设计思想,最终转化成自己的东西。

Hnadler介绍

在实际的开发过程中,我们经常将耗时的操作(比如网络请求)放到一个子线程中去,这是因为Android主线程中不允许做耗时操作,又不允许在子线程中操作UI,如何将子线程中得到的数据交给主线程去更新UI呢?这个时候就轮到Hanlder登场了,类似的操作方式还有很多,有兴趣的小伙伴可自行探究。其实Handler不仅仅局限于主线程中创建,子线程中创建也是可以的,只是我们平时用的最多的还是第一种形式。

内部重要类

在讲具体讲Hanlder机制之前,还是要了解一下其中几个重要的类:Handler、Message、MessageQueue、Looper。

Hanlder:		具体负责消息的发送和处理
Message:		将要传递的数据封装成一个Message对象,进行传输
MessageQueue:   消息队列,是一种数据结构,用于存储Hanlder发送过来的消息(Message对象)
Looper:         消息轮询器,将MessageQueue中存储的消息(Message对象)发送给对应的Handler

由这几个类的作用我们可以简要的概括一下Handler机制:
Handler发送一个消息(Message对象)到消息队列(MessageQueue)中去,通过消息轮询器(Looper对象)
不断的从消息队列中取出消息,发送给对应Handler去处理的过程。
在发消息时,Handler是消息的发送者;在处理消息时,Handler是消息的接受者。

使用方法

到这里我们已经知道Handler机制中几个重要类的作用,接下来就让我们看一下具体的用法。

子线程到主线程

public class MainActivity extends AppCompatActivity {

    private TextView tv;

    //1、定义Handler子类
    class MyHanlder extends Handler {
        @Override
        public void handleMessage(Message msg) { //2、重写handleMessage方法
            //具体要实现的UI操作
            switch (msg.what) {
                case 0:
                    tv.setText(msg.obj.toString());
                    break;
            }
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tv = findViewById(R.id.tv);

        //3、主线程创建Hadler对象
        final MyHanlder myHanlder = new MyHanlder();

        //创建子线程1
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {//延时两秒
                    Thread.sleep(2000);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                //4、创建消息对象,并赋值
                Message message = Message.obtain();
                message.what = 0;
                message.obj = "线程1发来贺电";
                myHanlder.sendMessage(message); //5、sendMessage方式发送消息
            }
        }).start();

        //创建子线程2
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {//延时四秒
                    Thread.sleep(4000);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                //post方式发送消息
                myHanlder.post(new Runnable() {
                    @Override
                    public void run() {
                        tv.setText("线程2发来贺电");
                    }
                });
            }
        }).start();
    }
}

来看一下运行效果

Handler.gif

子线程到子线程

public class MainActivity extends AppCompatActivity {
    
    private Handler handler;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //子线程中创建Hadler
        new Thread(new Runnable() {
            @Override
            public void run() {
                Looper.prepare();"-------------注意点1----------------"
                handler = new Handler() {
                    @Override
                    public void handleMessage(Message msg) {
                        switch (msg.what) {
                            case 0:
                                Log.i("MainActivity", msg.obj.toString());
                                break;
                            case 1:
                                Log.i("MainActivity", msg.obj.toString());
                                break;
                        }
                    }
                };
                Looper.loop();"-------------注意点2----------------"
            }
        }).start();
        
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {//延时两秒
                    Thread.sleep(2000);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                Message message = Message.obtain();
                message.what = 0;
                message.obj = "线程一再次发来贺电";
                handler.sendMessage(message);
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {//延时四秒
                    Thread.sleep(4000);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                Message message = Message.obtain();
                message.what = 0;
                message.obj = "线程二再次发来贺电";
                handler.sendMessage(message);
            }
        }).start();
    }
}

看一下运行结果:

HandlerResult.jpg

但是在编写这段代码时出现了连个要注意的地方,如上面代码所示。 1、假设注意点1处和注意点2处的代码都没有,再运行一次会出现如下图所示的错误

HandlerWrongResult.jpg

2、假设只有注意点1的代码,没有注意点2的代码会出现收不到消息的情况。 在这里留下几个问题:

1、Looper.prepare()的作用是什么?
2、Looper.loop()的作用时什么?
3、为什么在主线程中没有使用这两个方法,程序正常运行?

大家稍安勿躁,带着这三个问题我们到源码中一探究竟。

源码解析

1、Handler构造方法。

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

public Handler(Callback callback, boolean async) {
          ------省去无用代码-----
      //获取当前线程的Looper对象
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        //获取当前线程的Looper对象所对应的MessageQueue
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
}

从构造方法中我们可以看出创建Handler对象时做了两部分操作:1、默认为当前线程创建了一个Looper对象;2、获取Looper对应的MessageQueue对象,将Handler对象和MessageQueue对象绑定到一起。 但是我们在代码中也看到一条报错信息与上文注意点1处的报错信息一致。由源码可以看出:注意点1处之所以报错,是因为在子线程中默认没有创建其对应的Looper对象,所以我们要手动调用Looper.prepare()方法。

2、为什么主线程中没有调用Looper.prepare()方法而没有报错?

在Android应用进程启动时,会默认创建1个主线程(ActivityThread,也叫UI线程)创建时,会自动调用ActivityThread的1个静态的main()方法,这个方法相当于是程序的入口。我们来看一下main方法。

public static void main(String[] args) {
        //调用prepareMainLooper方法
        Looper.prepareMainLooper();
        long startSeq = 0;
        if (args != null) {
            for (int i = args.length - 1; i >= 0; --i) {
                if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
                    startSeq = Long.parseLong(
                            args[i].substring(PROC_START_SEQ_IDENT.length()));
                }
            }
        }
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop();
}

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

private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        //调用Looper的构造方法
        sThreadLocal.set(new Looper(quitAllowed));
    }

private Looper(boolean quitAllowed) {
        //将当前线程和MessageQueue进行绑定
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

看到上面这段代码恍然大悟,在应用初始化时已经自行调用了Looper.prepareMainLooper();方法,为主线程创建了一个Looper对象,并将主线程和MessageQueue对象进行了绑定。 之前我们还说过Looper.prepare()方法,其实和Looper.prepareMainLooper()方法调用过程相同,只是当先的线程对象不同:一个是主线程,一个是子线程。有兴趣的小伙伴可以自行查阅。

3、Looper.loop()方法做了什么?

public static void loop() {
        //1、获取Looper对象
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        //2、得到Looper对象对应的MessageQueue
        final MessageQueue queue = me.mQueue;

        for (;;) {//死循环
            Message msg = queue.next(); //查找消息队列中的下一条消息
            if (msg == null) { //如果消息队列中没有消息,直接返回
                return;
            }
      -------------------省去不必要代码-----------------
            try {
                //msg.target获取的是Handler对象。
                //找到Message对应的Handler对象,调用dispatchMessage(msg)方法进行消息分发
                msg.target.dispatchMessage(msg);
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            //对分发过的消息进行回收
            msg.recycleUnchecked();
        }
}

public void dispatchMessage(Message msg) {
        if (msg.callback != null) {//初始化消息对象时没有初始化callback字段
            handleCallback(msg);
        } else {
            if (mCallback != null) {//如果使用的是post方法时,mCallback不为null;如果是sendMessage方法,则为null
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            //此处调用的handleMessage方法就是在创建Handler对象时重写的handleMessage方法
            handleMessage(msg);
        }
    }

从上面的代码可以看出Looper.loop()方法的执行过程如下: 1、找到当前线程对应的Looper对象。 2、通过第一步获取的Looper对象,获取其绑定的MessageQueue对象。 3、轮询消息队列对消息进行分发。

小结

从上面三个步骤我们就可以解释之前留下来的三个问题:

1、Looper.prepare()的作用是什么?
这个方法中主要做了两件事:1、获取当前对象的Looper对象;2、将当前前程和MessageQueue绑定到一起。
2、Looper.loop()的作用时什么?
这个方法通过轮询当前线程的MessageQueue对象,将消息发送给对应的Handler对象。
3、为什么在主线程中没有使用这两个方法,程序正常运行?
通过ActivityThread->main()方法可以看出:在主线程中默认调用了Looper.prepareMainLooper()和Looper.loop()方法。

4、创建Message对象

//实例化Message对象,通过new或者obtain方法获取
Message message = Message.obtain();
message.what = 0; //初始化消息属性字段
message.obj = "线程1发来贺电";

public static Message obtain() {
        //在消息内容存在一个消息池,可以将Message进行复用,从而加快对象的初始化速度。
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0;
                sPoolSize--;
                return m;
            }
        //从源码中可以看出,官方推荐使用obtain方法进行Message对象的初始化,提高了对象的复用性。
        //减少由于对象的创建而多占用内存。
        }
        //如果从消息池中没有去处消息,再通过new的方式创建Message对象。
        return new Message();
}

5、在工作线程中发送消息:Handler.sendMessage(Message)

public final boolean sendMessage(Message msg){
        //调用sendMessageDelayed(Message,long)方法
        return sendMessageDelayed(msg, 0);
}

public final boolean sendMessageDelayed(Message msg, long delayMillis){
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        //调用sendMessageAtTime方法
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        //获取MessageQueue对象
        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) {
         //将当前Handler对象赋值给Message.target字段。
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        //调用MessageQueue对象的enqueueMessage方法
        return queue.enqueueMessage(msg, uptimeMillis);
}

---------------MessageQueue->enqueueMessage----------------
boolean enqueueMessage(Message msg, long when) {
        synchronized (this) {
            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;
            }

            if (p == null || when == 0 || when < p.when) {
                //如果消息队列中的消息为空,将消息放到消息队列的第一位
                //如果当前消息处于阻塞状态,将对其进行唤醒
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else { //将消息插入到消息队列中
                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;
                prev.next = msg;
            }
            if (needWake) { //是否需要唤醒队列
                nativeWake(mPtr); //native方法
            }
      }
      return true;
}

发送消息流程总结如下:

1、调用Handler->sendMessage方法,通过sendMessageDelayed方法,最后会调用MessageQueue的enqueueMessage方法
2、在enqueueMessage方法中,首先会判断当前消息队列中是否存在消息。
      如果不存在,将消息插入到第一位;
      如果存在消息,则遍历整个队列,根据消息的创建时间进行插入。

最后根据之前的Looper.loop()方法不断轮询消息队列,从消息队列中取出消息,根据消息的target属性找到对应的Handler对象处理消息。 至此Handler机制已经基本上分析完毕,一些重要的说明在注释中已经表现。

总结

1、在主线程中创建Handler时,会默认调用Looper.prepareMainLooper()和Looper.loop()方法方法。这两个方法主要是给当前线程创建Looper对象、将当前线程绑定一个MessageQueue对象。
2、创建Message对象时将要发送的消息内容设置给Message对象的各个字段。
3、发送消息时,首先会为Message对象设置target属性,将其和对应的Handler对象进行绑定;然后会将Message对象放入MessageQueue中去,如果消息队列中没有消息,将要发送的消息放入到第一位;如果消息队列处于阻塞状态,需要将其唤醒。
4、通过Looper轮询消息队列,将消息发送给对应的target(Handler)对象进行处理。

下一篇文章将会从源码的角度分析“消息屏障”。最后,欢迎给位大佬批评指正。