目的:handle的出现主要是为了解决线程间通讯。
举个例子,android是不允许在主线程中访问网络,因为这样会阻塞主线程,影响性能,所以访问网络都是放在子线程中执行,对于网络返回的结果则需要显示在主线程中,handler就是连接主线程和子线程的桥梁。
1.handler基本使用方法
看一下使用方法:
- public static final int EMPTY_MSG = 0;
- @SuppressLint("HandlerLeak")
- Handler handler = new Handler(){
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what){
- case 0:
- Toast.makeText(MainActivitys.this, "接受到消息", Toast.LENGTH_SHORT).show();
- break;
- }
- }
- };
- @Override
- protected void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- new Thread(new Runnable() {
- @Override
- public void run() {
- handler.sendEmptyMessage(0);
- }
- }).start();
- }
通过上边代码就完成了子线程向主线程发送消息的功能。
2. handler,Looper,MessageQueue 解释
handler:负责发送和处理消息
Looper:消息循环器,也可以理解为消息泵,主动地获取消息,并交给handler来处理
MessageQueue:消息队列,用来存储消息
3.源码分析
程序的启动是在ActivityThread的main方法中
- public static void main(){
- Looper.prepare(); //1
- Handler handler = new Handler();//2
- Looper.loop(); //3
- }
Looper.prepare()会初始化当前线程的looper
- 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));
- }
会调用到sThreadLocal.set()方法,ThreadLocal是线程安全的,不同的线程获取到的值是不一样的,下面先分析一下ThreadLocal是如何做到线程安全。
- public void set(T value) {
- Thread t = Thread.currentThread();
- ThreadLocalMap map = getMap(t);
- if (map != null)
- map.set(this, value);
- else
- createMap(t, value);
- }
不同的线程会设置不同的looper,下面看一下ThreadLocalMap是如何存储数据的
- ThreadLocalMap(ThreadLocal firstKey, Object firstValue) {
- table = new Entry[INITIAL_CAPACITY];
- int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
- table[i] = new Entry(firstKey, firstValue);
- }
ThreadLocalMap会创建一个数组,key是通过特殊的算法来创建出来,一个线程中会有一个ThreadLocalMap,这个map中会存多个ThreadLocal和values。
下面看下ThreadLocalMap是如何set一个值的
- private void set(ThreadLocal key, Object value) {
- // We don't use a fast path as with get() because it is at
- // least as common to use set() to create new entries as
- // it is to replace existing ones, in which case, a fast
- // path would fail more often than not.
- Entry[] tab = table;
- int len = tab.length;
- int i = key.threadLocalHashCode & (len-1);
- for (Entry e = tab[i];
- e != null;
- e = tab[i = nextIndex(i, len)]) {
- ThreadLocal k = e.get();
- if (k == key) {
- e.value = value;
- return;
- }
- if (k == null) {
- replaceStaleEntry(key, value, i);
- return;
- }
- }
- tab[i] = new Entry(key, value);
- int sz = ++size;
- if (!cleanSomeSlots(i, sz) && sz >= threshold)
- rehash();
- }
其实是遍历threadLocalMap中的table,如果当前table中存在threadLocal这个key就更新,不存在就新建。ThreadLocal的set方法到此结束。
下面看下Handler handler = new Handler()中执行了哪些操作:
- public Handler(Callback callback, boolean async) {
- mLooper = Looper.myLooper();
- mQueue = mLooper.mQueue;
-
- }
重要的就是构造函数中这两个方法,在handler中初始化looper和messageQueue。这个就不展开讲了。
下面看一下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) {
- // No message indicates that the message queue is quitting.
- return;
- }
- msg.target.dispatchMessage(msg);
- msg.recycleUnchecked();
- }
- }
queue.next()是个无限for循环,其实也是个阻塞方法,其中比较重要的是下面这个方法,其作用是不会一直循环。底层采用的是pipe/epoll机制。
- nativePollOnce(ptr, nextPollTimeoutMillis);
message.next()返回消息之后会接着调用 msg.target.dispatchMessage(msg);在这个方法里边会进行判断,来决定执行哪一种回调。
- public void dispatchMessage(Message msg) {
- if (msg.callback != null) {
- handleCallback(msg);
- } else {
- if (mCallback != null) {
- if (mCallback.handleMessage(msg)) {
- return;
- }
- }
- handleMessage(msg);
- }
- }
到此整个handler的流程就结束了。最后附上一张handler的时序图。
