Handler基本使用及源码分析

原文链接:
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 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基本使用及源码分析)