欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android Handler的源码分析

程序员文章站 2022-05-12 14:57:38
...

1、简述

由于安卓中为了确保界面的更新同步,限制了只有UI(主)线程可以更新界面,但是UI线程不能执行耗时操作,相关的文件读取、数据加载和网络请求等操作,需要放置在子线程进行处理,但处理完成后怎样将子线程的处理结果及时的发送至主线程呢?
此时就需要引入Handler的运行机制,通过Hnadler的创建、发送和处理方式,实现跨线程的数据通信。
其实Handelr主要用于线程间通信的,但在安卓开发中一般用于子线程发送至主线程的操作。

2、Handelr的简单使用

2.1子线程发送至主线程

private final int TEST_HANDLER_NUM = 100;
    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case TEST_HANDLER_NUM:
                    Log.i(TAG, "handleMessage: 接收到处理结果");
                    break;
            }
        }
    };
  
  new Thread(new Runnable() {
            @Override
            public void run() {
                SystemClock.sleep(5 * 1000);
                Message message = mHandler.obtainMessage();
                message.what = TEST_HANDLER_NUM;
                mHandler.sendMessage(message);
                Log.i(TAG, "run: 消息处理完成,开始发送");
            }
        }).start();

2.2 主线程发送至子线程

//在子线程中创建Handler
new Thread(new Runnable() {
            @Override
            public void run() {
                Looper.prepare();
                subHandler = new Handler() {
                    @Override
                    public void handleMessage(Message msg) {
                        super.handleMessage(msg);
                        switch (msg.what) {
                            case TEST_SUB_HANDLER_NUM:
                                Log.i(TAG, "handleMessage: 子线程接收到消息");
                                break;
                        }
                    }
                };
                Looper.loop();

            }
        }).start();
//主线程发送消息
findViewById(R.id.TestA).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
				subHandler.sendEmptyMessageAtTime(TEST_SUB_HANDLER_NUM,5*1000);
                Log.i(TAG, "onCreate: 发送消息至子线程");
}

2.3 小结

Handler的使用中,除了上述使用的sendMessage/sendEmptyMessageAtTime,还包括post/postAtTime等,但是通过查看源码可以发现,其最终调用的还是sendMessage相关的方法,因此本章中主要是对sendMessage相关的源码分析。

3. Handler运行机制

3.1执行流程
Android Handler的源码分析针对上图中相关的类简介如下:

Handler相关的流程包括如下:创建Handler、发送消息、存储消息、取出消息、处理消息五个过程,其中包含的类如下:

  • Handler:负责发送和处理消息;

  • Looper:负责不断的轮询消息队列,并将消息 分发至Handler处理;

  • MessageQueue:存储消息的队列;

  • Message:消息载体;

3.1.1创建Handler

在创建Handler时,其执行的构造方法如下:

public Handler(Callback callback, boolean async) {
...
		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;
...
}

在创建Handler时,通过myLooper()获取到Looper对象,否则会抛出RuntimeException异常,而myLooper()的获取方式为通过ThreadLocal的get()方法。
既然时通过ge()方式获取,那么是什么时候调用的set()方法呢?

public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

查看2.2 主线程发送至子线程,在创建Handler前,先调用了Looper.prepare()方法,然后调用了 Looper.loop()方法,至于为何需要调用这个两个方法?2.1部分为何不需要调用?
为了回答以上问题,需要查看prepare()和loop()到底是处理了什么?

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方法后,先通过ThreadLocal.get()查看是否为空,如果不为空则抛出异常,表明一个线程中只能绑定一个Looper对象,如果为空,则新建一个Looper对象,并通过set方法赋值给ThreadLocal。
而loop()是消息的轮询机制,该部分在下面消息的轮询中会详细讲解。

为何2.1部分子线程发送消息至主线程,不需要调用prepare()方法?
查看源码注释,可以看出,在应用启动时,就调用了getMainLooper() 方法,为主线程创建了Looper对象。

 /**
     * Returns the application's main looper, which lives in the main thread of the application.
     */
    public static Looper getMainLooper() {
        synchronized (Looper.class) {
            return sMainLooper;
        }
    }

其中ThreadLocal用于线程的存储类,主要包含set和get方法,其内部维护这一个Map集合,其中key是当前的线程,value为需要存储的值。

  public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

3.1.2 发送消息

如第2部分所示为Handler的简单使用,无论是发送Message 还是Runable,但最终运行至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);
    }
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
	//设置消息的tagrget
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

执行至Handler的enqueueMessage后,开始调用MessageQueue 的enqueueMessage,开始消息的存储操作。

3.1.3 存储消息

MessageQueue主要用于消息的存取,通过next()获取消息信息,enqueueMessage()存储消息,本节主要讲解消息的添加,消息的分发处理在下面讲解。

Handler中的enqueueMessage处理:

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
		//在发送至MessageQueue 前,将当前的Handler赋值给Message 的target变量
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

MessageQueue 中的enqueueMessage处理:

boolean enqueueMessage(Message msg, long when) {
	//1、消息属性的判断
	   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.");
        }
	//2、更改消息属性 
			msg.markInUse();
            msg.when = when;
            //上次添加的消息信息
            Message p = mMessages;
            boolean needWake;
      //3、查看消息是否为空,上次消息是否添加成功
            if (p == null || when == 0 || when < p.when) {
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
}

3.1.4 消息分发

3.1.4.1 消息轮询

在2.2 主线程发送至子线程,在创建Handler后,需要手动调用 Looper.loop()方法,如果注释该代码,会发现及时发送了消息,但是在handleMessage接收不到消息的回调信息,下面看下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、获取当前线程对应的 MessageQueue
        final MessageQueue queue = me.mQueue;
        ....
        //3、死循环获取消息信息
         for (;;) {
            Message msg = queue.next(); 
            //4、如果去除的消息为null ,则跳出循环
            if (msg == null) {
                return;
            }
            ...
            //5、根据消息的target分发至对应的dispatchMessage方法中
             msg.target.dispatchMessage(msg);
		   ...
		  }        
 }
3.1.4.2 消息获取

以上完成消息的遍历取出和分发操作,其中消息的取出通过MessageQueue 的next()方法实现。

Message next(){
...
//1、死循环遍历
for (;;) {
...
synchronized (this) {
//2、获取当前时间戳;初始化上次消息;当前消息
				final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
  // 3、查找消息队列中的下一个消息
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
 //	4、判断当前消息是否准备完成
						if (now < msg.when) {
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
//5、消息属性变更和发送消息
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    }
				}
}

...

}
}
3.1.4.3 消息类型

在上述讨论中一直围绕着Message的处理来梳理,下面查看一下Message的类,其中最常用的属性如下,对于what、arg1、arg2和obj在Handler的应用处理中使用较多,而target属性用于标注消息,在消息的存储和分发中便于标注是基于那个Handler 进行处理的,这是由于针对一个Looper对象,我们可以创建多个Handler实例。

public final class Message implements Parcelable {
			public int what;
			public int arg1;
			public int arg2;
			public Object obj;
			...
			Handler target;
			...
}

3.1.5 消息分发

在3.1.4.1 消息轮询中,完成消息的分发处理,分发中通过Message的target判断所属的Handler,其实target就是Handler类型。

public void dispatchMessage(Message msg) {
//1、msg是否为Runable的形式
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
//2、是否重写了Callback方法        
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
//3、如果上述都为空,默认执行至重写的 handleMessage中,也就是常见的创建Handler的形式
            handleMessage(msg);
        }
    }

3.2 常见问题分析

1、Handler是怎么实现切换线程的?
2、handler.sendMessage()与handler.post()的区别?
3、MessageQueue是怎么增删消息的?
4、一个线程可以有几个Handler?几个Looper?几个MessageQueue?
5、A Handler发送的消息为什么不会跑到B Handler的handleMessage()方法中?
6、简述ThreadLoacal的原理?
7、Handler引起的内存泄漏?

1、Handler是怎么实现切换线程的?

  • 创建Handler:在创建时需要先获取该线程(一般为UI线程)对应的Looper信息;
  • 发送消息:在子线程数据处理完成后,发送消息,在发送消息前将该Handler作为target,将消息发送至MessageQueue中;
  • 消息处理:通过Looper的loop方法,不断获取消息信息,根据消息属性中的target分发消息,从而完成了子线程至主线程的消息处理。
    2、handler.sendMessage()与handler.post()的区别?
    查看Handler可以发现,最终发送至MessageQueue的方法都是Handler中的enqueueMessage(),但是对于Message的处理,在分发时,会将消息的结果返回至其callback 中(如果不为空)。
public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }
 private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

3、MessageQueue是怎么增删消息的?
增加:通过enqueueMessage方法,通过对消息添加时间和之前消息的判断,将新添加的消息放置在栈顶

msg.next = p; // invariant: p == prev.next
prev.next = msg;

删除:通过next方法取出消息,在loop()中进行消息的分发处理,在next返回消息前对消息属性进行修改

// MessageQueue中的next()方法
msg.next = null;
msg.markInUse();
// Message中的markInUse()方法,表明消息已经使用
void markInUse() {
        flags |= FLAG_IN_USE;
    }

4、一个线程可以有几个Handler?几个Looper?几个MessageQueue?
多个Handler?
**多个:**上述描述中,知道之所以消息可以准确的分发处理,是由于通过Message的target方法在添加和分发时标注Handler,实现准确的分发,因此同一个线程可以创建多个Handler对象,例如常见的UI线程中:

 private Handler mHandler= new Handler(Looper.myLooper()){...}

几个Looper?
一个:在2.2中主线程发送至子线程中,需要先调用Looper.prepare()方法,否则会抛出异常,那如果一个线程中包含多个Looper会怎样?
查看源码可以发现,如果包含多个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));
    }

几个MessageQueue?
一个:在调用Looper.prepare()时,会判断该线程的Looper是否为空,只有为空的情况才会调用Looper的构造方法,创建MessageQueue,因此只有一个。

private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

5、A Handler发送的消息为什么不会跑到B Handler的handleMessage()方法中?
参考问题1和4可知,一个线程中包含多个Handelr对象,而在消息的添加分发时,通过Message的target(Handler)标注。
6、简述ThreadLoacal的原理?
在3.1中新建Handler时,已对ThreadLoacal简述,通过set和get方法,其内部维护这一个Map集合,其中key是当前的线程,value为需要存储的值。
7、Handler引起的内存泄漏?
可参考Android中使用Handler为何造成内存泄漏?中相关的介绍,主要是由于在消息处理时,当前Activty已经关闭,但是仍有消息处理,导致当前Activty无法正常消耗出现内存泄漏的情况,解决方案:

  1. Activity销毁时及时清理消息队列;
  2. 自定义静态Handler类+软引用。

参考:Android Handler之原理解析