Handler机制详解

2019-08-05  本文已影响0人  懒烂蓝

1. Handler简介

Handler机制,说的也是Android中的消息传递机制。也就是将工作线程(子线程)中创建的消息,传递到主线程,以进行UI更新操作的过程。简单的可理解为,子线程和主线程之间的通讯。采用这样的机制是为了防止并发更新UI所导致的线程安全问题,而不使用线程锁是为了提高系统的运行效率。

2.Handler简单使用

2.1 使用sendMessage()方法。

Thread thread = new Thread(new Runnable() {
                    @Override
                    public void run() {
                        Message message = new Message();
                        message.what = 0x11;
                        message.obj = "异步创建字符串";
                        handler.sendMessage(message);
                    }
                });
                thread.start();

2.2 使用post方法

Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        textView.setText("异步创建字符串");
                    }
                });
            }
        });
        thread.start();

2.3 接收、处理消息

private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case 0x11:
                    String text = (String) msg.obj;
                    textView.setText(text);
                    break;
            }
        }
    };

3.Handler相关方法

3.1 sendMessage系列

sendMessage()方法主要是用于发送一条消息。一般用法是通过重写handler的handler消息是通过Message进行封装。如:

 Message message = new Message();
 message.what = 0x11;
 message.obj = "异步创建字符串";
 message.arg1 = 1;
 message.arg2 = 2;
 handler.sendMessage(message);

除了sendMessage方法可以调用外,还有如下方法:


image.png

sendEmptyMessage用户发送一条空消息,参数what用于表示一个消息类型。

    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 final boolean sendEmptyMessage(int what)   {
        return sendEmptyMessageDelayed(what, 0);
    }

    public final boolean sendEmptyMessageDelayed(int what, long delayMillis)    {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
    }

我们可以发现,sendEmptyMessage()方法,最终调用的是sendMessageDelayed()方法。是在sendEmptyMessageDelayed()方法中为我们创建了一个Message对象。

3.2 post系列

post方法主要是post一个Runnable对象,在Runnable的run方法中进行消息的处理,这种方法在使用方面不用自己去创建Message对象,但内部其实还是通过Message实现的。这种方法可以实现在子线程中方便的更新UI而不用去通过重写Handler的handleMessage()方法来更新UI。如:

 Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        textView.setText("异步创建字符串");
                    }
                });
            }
        });
thread.start();

同样,post也有延迟发送等方法:


image.png

4.消息机制

接下来通过源码来分析Handler的消息机制。

4.1 相关的类

4.2 流程

下面一张图理解Handler、Message和MessageQueue之间的关系。


image.png

大概的流程为:handler发送message到MessageQueue中。Looper轮询MessageQueue,将消息发送给Handler进行处理。

4.3 Handler的创建过程

4.3.1 主线程中创建Handler

handle的创建只需要通过new Handler()就可以创建一个Handler。在创建handler的时候需要指定Looper,主线程的Looper是在ActivityThread的main方法中创建的,通过Looper.prepareMainLooper()方法进行创建。

    /**
     * Initialize the current thread as a looper, marking it as an
     * application's main looper. The main looper for your application
     * is created by the Android environment, so you should never need
     * to call this function yourself.  See also: {@link #prepare()}
     */
    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

在prepareMainLooper方法中,sMainLooper通过myLooper()方法创建,而myLooper方法是通过调用ThreadLocal的get方法得到Looper对象。

    /**
     * Return the Looper object associated with the current thread.  Returns
     * null if the calling thread is not associated with a Looper.
     */
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

这里,先不具体讲ThreadLocal,简单的可理解为讲数据和线程做一个绑定,在当前线程中使用ThreadLocal.get()方法获取对象,则获取到的对象是当前线程所独立拥有的对象,和其他线程中的对象互不干扰。ThreadLocal中有一个Map,用来保存线程和数据的映射关系。

通过ThreadLocal.get()方法获取数据,首先要调用ThreadLocal.set()方法设置数据。所以刚才在myLooper方法中使用ThreadLocal.get()方法获取Looper对象,那是在什么时候设置进去的呢?答案就是prepareMainLooper方法中调用的prepare方法。

     /** Initialize the current thread as a looper.
      * This gives you a chance to create handlers that then reference
      * this looper, before actually starting the loop. Be sure to call
      * {@link #loop()} after calling this method, and end it by calling
      * {@link #quit()}.
      */
    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));
    }

我们再看prepare(boolean)方法。这里首先调用了ThreadLocal.get()方法检查当前线程是否已经创建了Looper对象,如果创建了,就抛出一个RuntimeException。然后调用ThreadLocal.set()方法设置了一个新的Looper,quitAllowed参数是表示是否可以退出消息队列。
由于Looper的构造方法是私有的,所以我们需要通过Looper.prepare()方法来创建Looper。在Looper的构造方法中,创建了一个MessageQueue,并且获取了当前线程信息。

此时主线程中的Looper已经创建完成。再看ActivityThread的main方法,在main方法的最后,调用了Looper.loop()方法,之后抛出一个RuntimeException。此时我们是不是有疑问,为啥要抛出一个RuntimeException?我们知道java程序的入口是main方法,当main方法执行结束,也表示应用程序已经执行结束。所以我们应该会想到,作为一个Android应用程序,怎么可以刚打开就结束了呢。所以说正常情况下只不会执行结束的。关键就在于Looper.loop()方法。我们查看Looper.loop()方法:

     /**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     */
    public static void loop()   {
        //此处省略...........
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null)   {
                // No message indicates that the message queue is quitting.
                return;
            }
            //此处省略..........
        }
        //此处省略..........
    }

在loop方法中有一个死循环,这个死循环就防止了应用程序执行结束。我们看到通过queue.next()方法拿到一个Message,queue的定义在loop方法中,并且在for循环之前:

        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;

通过myLooper方法拿到当前线程的Looper对象me,并通过me.mQueue方法拿到了当前Looper对象中的MessageQueue,这个MessageQueue的创建是在前面讲到的Looper的构造方法中。通过MessageQueue.next()方法从MessageQueue中取得一个Message。

此时Handler、Looper、MessageQueue已经创建完成,并且Looper循环已经开启。只需要handler开始发送消息,并处理消息。

4.3.2 子线程中创建Handler

上面是从主线程的角度开始讲解Handler的创建过程,那子线程呢?
在子线程中使用Handler,首先需要new一个handler,此时handler中是没有Looper对象的。上面我们分析源码得出。Looper的构造方法是私有的,只能通过Looper.prepare()方法来创建当前线程的Handler,并且每个线程最多只能存在一个Looper对象,如果多次创建,会抛出RuntimeException。
所以我们在子线程中要使用Handler的时候,必须指定Looper。为什么说是指定而不是创建呢,因为我们可以通过Looper.getMainLooper()方法拿到主线程的Handler。当Looper指定完成以后,就可以通过Looper.loop方法开启循环。所以说,消息处于哪个线程的Looper中,则消息回调处理就在哪个线程中。

4.4 消息发送

4.4.1 使用Send方法发送Message

上面说到了sendMessage()方法最终都是调用了sendMessageAtTime()方法。我们看看sendMessageAtTime()方法做了什么操作。

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

在sendMessageAtTime()方法中,首先检查了当前Handler是否指定了MessageQueue。如果没有指定,则返回false,如果已经指定,则执行了enqueueMessage()方法,我们再看enqueueMessage()方法。

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

在这里我们就可以看明白,是在这个方法中将消息加入到MessageQueue中。并且在加入message之前,将msg的target字段赋值为this,这个this也就是当前的Handler对象了。mAsynchronous表示是否异步,这个参数是在handler创建的时候初始化的,默认为false。

4.4.2 使用Post方法发送Runnable

我们首先看Post方法中执行了什么操作。

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

我们发现post()方法直接调用的是sendMessageDelayed()方法。但是message的获取是通过getPostMessage()方法。我们看看getPostMessage()方法。

    private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

在getPostMessage()方法中通过Message.obtain()得到了一个Message对象,并且将runnable对象设置给message的callback,之后就返回了这个message对象。Message.obtain()方法简单理解是获取一个可复用的Message对象,降低了内存开销,提高效率。
所以说,我们使用post()方法最终还是通过Message,并且是将这个回调Runnable设置给Message对象中的callback属性。

4.5 消息的处理

我们回到Loop的loop()方法。

public static void loop() {
   //... 省略 
   for (;;) {
        //... 省略
          try {
                msg.target.dispatchMessage(msg);
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
        //... 省略
   }
  //... 省略
}

在loop()方法的for循环中,有一行代码。msg.target.dispatchMessage()。调用了message的target的dispatchMessage()方法。之前我们看过,message的target实际上是保存的当前Handler的实例,所以这里调用的是handler对象的dispatchMessage()方法进行消息的封发处理。我们查看dispatchMessage()方法。

    /**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

这里我们发现,首先判断了msg的callback属性是否为null,这里的callback就是使用post方法传入的Runnable参数。

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

handleCallback()方法直接调用了message.callback.run(),也就是调用了传入的Runnable的run方法。

写在最后

首先,谢谢大家的阅读。由于小编也是初次学习写技术文章,都是按照自己的理解来写的,可能会有错误的地方欢迎大家留言指出。文章中语言的组织也不是很好,希望大家理解。写这篇文章一是为了能让自己进一步理解Handler机制,并加强记忆。二是能和大家交流技术,共同进步。也希望能提起大家阅读源码的兴趣。最后,还是感谢大家阅读。

上一篇下一篇

猜你喜欢

热点阅读