美文网首页
Handler基本使用及源码分析

Handler基本使用及源码分析

作者: Innocencellh | 来源:发表于2020-06-12 11:16 被阅读0次

    原文链接:
    https://segmentfault.com/a/1190000004866028
    https://www.jianshu.com/p/f70ee1765a61

    Handler定义

    Handler是Android一套非阻塞消息传递机制/异步通信机制

    Handler存在意义

    在Android中,为了UI操作是线程安全的,规定了只允许UI线程更新 Activity里的UI组件,主线程不能进行耗时操作,否则会阻塞线程,产生ANR异常,所以常常把耗时操作放到其它子线程中进行。但在实际开发中,存在多个线程并发操作UI组件的情况,导致UI操作的线程不安全,所以保证多个线程可并发操作UI组件的同时,线程是安全的。

    Handler基本用法

    android.os.Handler handler = new Handler(){
      @Override
      public void handleMessage(final Message msg) {
        //这里接受并处理消息
      }
    };
    //发送消息
    handler.sendMessage(message);
    handler.post(runnable);
    

    通过实例化一个Handler重写handleMessage方法,然后在需要发消息的时候调用handler对象的send以及post系列方法,并且支持发送延时消息,handler发送消息的方法:

    sendMessage(Message msg)
    sendMessageDelayed(Message msg, long uptimeMillis)
    post(Runnable r)
    postDelayed(Runnable r, long uptimeMillis)
    sendMessageAtTime(Message msg,long when)
    sendEmptyMessage(int what)
    sendEmptyMessageDelayed(int what, long uptimeMillis)
    sendEmptyMessageAtTime(int what, long when)
    

    单看基本用法是不是很简单,那网上说的跟handler相关的MessageQueueLooper呢?并没有看到它们的身影呢?别慌,我们下面从源码一点一点分析。

    Handler源码分析

    1、首先我们先从Handler发送消息切入,以sendMessage为例,看下handler是如何把消息入队的。

    // Handler类相关方法
    public final boolean sendMessage(@NonNull Message msg) {
            return sendMessageDelayed(msg, 0);
        }
    
            ...
    
     public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
            if (delayMillis < 0) {
                delayMillis = 0;
            }
            return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
        }
    
            ...
    public boolean sendMessageAtTime(@NonNull 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(@NonNull MessageQueue queue, @NonNull Message msg,
                long uptimeMillis) {
            msg.target = this;
            msg.workSourceUid = ThreadLocalWorkSource.getUid();
    
            if (mAsynchronous) {
                msg.setAsynchronous(true);
            }
            return queue.enqueueMessage(msg, uptimeMillis);
        }
            ...
    
    //MessageQueue相关方法
    boolean enqueueMessage(Message msg, long when) {
            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) {
                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) {
                    // 消息顺序入队
                    msg.next = p;
                    mMessages = msg;
                    needWake = mBlocked;
                } else {
                    // 消息插入队列,按照when进行比对
                    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);
                }
            }
            return true;
        }
    

    至此就完成了消息入队操作,值得注意的是,如果消息入队时,队列中有信息,就要进行插队操作,插队按照时间message#when来进行。

    2、说完了消息是如何入队的,接下来我们来讲讲消息是如何出队的,还是从源码的角度进行分析。

    // ActivityThread就是我们常说的主线程或UI线程,ActivityThread的main方法是整个APP的入口
    public static void main(String[] args) {
            ...
    
    Looper.prepareMainLooper();
            ...
    Looper.loop();
    
    }
    //Looper类相关 
     public static void prepareMainLooper() {
            prepare(false);
            synchronized (Looper.class) {
                if (sMainLooper != null) {
                    throw new IllegalStateException("The main Looper has already been prepared.");
                }
                sMainLooper = myLooper();
            }
        }
             ...
    
     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;
                }
    
                // This must be in a local variable, in case a UI event sets the logger
                ...
    
                // Handler target
                msg.target.dispatchMessage(msg);
               
               ...
                msg.recycleUnchecked();
            }
    }
    
    
    //Handler类相关
     public void dispatchMessage(@NonNull Message msg) {
            if (msg.callback != null) {
                handleCallback(msg);
            } else {
                if (mCallback != null) {
                    if (mCallback.handleMessage(msg)) {
                        return;
                    }
                }
                handleMessage(msg);
            }
        }
    
    //Message类操作 
    void recycleUnchecked() {
            // Mark the message as in use while it remains in the recycled object pool.
            // Clear out all other details.
            flags = FLAG_IN_USE;
            what = 0;
            arg1 = 0;
            arg2 = 0;
            obj = null;
            replyTo = null;
            sendingUid = UID_NONE;
            workSourceUid = UID_NONE;
            when = 0;
            target = null;
            callback = null;
            data = null;
    
            synchronized (sPoolSync) {
                if (sPoolSize < MAX_POOL_SIZE) {
                    next = sPool;
                    sPool = this;
                    sPoolSize++;
                }
            }
        }
    
    

    以上部分就完成了消息出队操作:ActivityThreadmain()调用Looper.loop(),Looper开始轮询,通过MessageQueue的next()方法,Looper获取到了message对象之后,调用msg.target.dispatchMessage(msg)进行消息分发处理。

    3、讲完消息的入队出队操作,第三部分要讲的是 Handler,Looper,MessageQueue,Message是如何串联起来的。借用下面图来加深理解。


    handler机制图.png

    Handler 常见Q&A

    Q:说一说Handler中涉及到哪些类,各自功能是什么 ?

    A:handlerloopermessageMessageQueue主要是这4个,ThreadLocal是JDK本身自带类 并不是专门为handler设计的。
    handler是主线程与子线程之间通信的媒介,也是消息的主要处理者。它的作用是将message对象发送到MessageQueue中,也负责Looper分派过来的消息处理。
    Looper是消息队列(Message Queue)与处理者(Handler)的通信媒介,负责从MessageQueue中不断轮询取出队列消息,将消息发送给对应的处理者handler。
    MessageQueue是负责存储handler发送过来的消息,存储特点是 先进先出。
    message是线程间通信的数据单元,即handler接受&处理的对象,存储需要操作的通信信息。

    Q:MessageQueue 中的 Message 是有序的吗?排序的依据是什么

    A:是有序的,排序的依据是 Message#when字段,该值是SystemClock#uptimeMillis()delayMillis之和表示一个相对时间,该值是由 MessageQueue#enqueueMessage(Message, Long)方法设置的。

    Q:子线程中可以创建Handler吗?

    A:答案是可以的,前提是需要先创建Looper对象

    Looper.prepare();
    Handler handler = new Handler();
    // ....
    // 这一步可别可少了
    Looper.loop();
    
    Q:Handler 是如何与 Looper 关联的?

    A:我们直接调用无参构造方法时会有一个自动绑定过程

     public Handler(@Nullable Callback callback, boolean async) {
            if (FIND_POTENTIAL_LEAKS) {
                final Class<? extends Handler> klass = getClass();
                if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                        (klass.getModifiers() & Modifier.STATIC) == 0) {
                    Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                        klass.getCanonicalName());
                }
            }
    
            //就是这里 Looper跟Handler进行了绑定  
            mLooper = Looper.myLooper();
            if (mLooper == null) {
                throw new RuntimeException(
                    "Can't create handler inside thread " + Thread.currentThread()
                            + " that has not called Looper.prepare()");
            }
            mQueue = mLooper.mQueue;
            mCallback = callback;
            mAsynchronous = async;
        }
    
    Q:Looper 是如何与 Thread 关联的?

    A:Looper 与 Thread 之间是通过 ThreadLocal 关联的,这个可以看 Looper#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));
        }
    
    Q:Handler 有哪些构造方法?

    A:Handler的构造方法可以传参数类型为 :LooperHandler$Callback,现在我们就可以算出有多少个公共构造方法了:无参、单Looper、单Callback、Looper和Handler,共4种。

    public Handler() {
        this(null, false);
    }
    public Handler(Callback callback) {
        this(callback, false);
    }
    public Handler(Looper looper) {
        this(looper, null, false);
    }
    public Handler(Looper looper, Callback callback) {
        this(looper, callback, false);
    }
    
    Q:looper为什么调用的是Handler的dispatchMessage方法?

    A:我们看下dispatchMessage方法

    // Handler.java:97
    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时会执行 handleCallback(msg),这表示这个 msg 对象是通过 handler#postAtTime(Runnable, long)相关方法发送的,所以 msg.what和 msg.obj 都是零值,不会交给Handler#handleMessage方法。
    • Handler可以接受一个 Callback参数,类似于 View 里的 OnTouchListener ,会先把事件交给 Callback#handleMessage(Message)处理,如果返回 false 时该消息才会交给 Handler#handleMessage(Message)方法。

    相关文章

      网友评论

          本文标题:Handler基本使用及源码分析

          本文链接:https://www.haomeiwen.com/subject/alfltktx.html