一. Handler的主要作用
我们都知道,在android里是不能在子线程里更新UI的, 简单说一下原因: 更新UI时会调用ViewRootImpl.checkThread() 检查当前线程是否与mThread (控件初始化时所在的线程)是否一致,假如不一致,则抛出异常,ViewRootImpl.java关键代码如下:
public ViewRootImpl(Context context, Display display) {
mThread = Thread.currentThread();
}
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
Handler的主要作用:在子线程中发送消息, 在主线程中更新UI。
二. Handler的基本使用
1. Handler.sendMessage(msg)
@SuppressLint("HandlerLeak")
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
mTextView.setText(msg.obj + ""); //更新UI
}
};
//子线程发送消息
new Thread(new Runnable() {
@Override
public void run() {
Message message = new Message();
message.obj = "6666";
mHandler.sendMessage(message);
}).start();
2. Handler.post(msg)
@SuppressLint("HandlerLeak")
private Handler mHandler = new Handler();
//子线程发送消息
new Thread(new Runnable() {
@Override
public void run() {
mHandler.post(new Runnable() { //切换到主线程更新UI
@Override
public void run() {
mTextView.setText("123456");
}
});
}).start();
二. Handler的运行机制(API28源码解读)
1. Handler.sendMessage(msg)做了些什么?
Handler.java里面的方法(部分关键代码):
sendMessage()→sendMessageDelayed()→sendMessageAtTime()→enqueueMessage;
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 boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
return queue.enqueueMessage(msg, uptimeMillis);
}
从上面来看,Handler最终调用了MessageQueue.enqueueMessage()
注意: msg.target = this; msg.target 表示当前对象Handler, 后面Looper.loop()用到
MessageQueue.enqueueMessage()
关键代码如下:
boolean enqueueMessage(Message msg, long when) {
msg.when = when;
Message p = mMessages;
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
} else {
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
}
msg.next = p;
prev.next = msg;
}
}
return true;
}
从代码中慢慢分析,我们可以得出结论:其实MessageQueue.enqueueMessage()方法就是把传进来的Message消息对象,按照时间顺序、队列的结构 保存起来。
在这里,我们并没有看到Handler.handleMessage()方法的执行,继续往下看。
2. 从ActivityThread.main()分析
我们就不慢慢从问题引进来了,直奔主题。
public static void main(String[] args) {
Looper.prepareMainLooper(); //初始化
Looper.loop();
}
2.1.首先我们看Looper.prepareMainLooper()做了些什么
prepareMainLooper()→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(); //赋值
}
}
//有且只有一个ThreadLocal对象
static final ThreadLocal sThreadLocal = new ThreadLocal();
private static void prepare(boolean quitAllowed) {
//进入ThreadLocal源码里set()和get()可以看出,每个线程只保存一个Looper对象
//所以每个线程只能调用一次prepare(),否则会抛异常
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//ThreadLocal保存Looper对象
sThreadLocal.set(new Looper(quitAllowed));
}
注意:prepare()方法 这里有2个关键点:
(1) sThreadLocal.set() 和 get()方法
(2) Looper.java构造方法new Looper().
(1). ThreadLocal.java 的set()和get()
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();
}
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
set():给当前线程赋值一个:Loop对象
get():获取当前线程的Loop对象
(2). Looper.java构造方法
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
主要初始化消息队列MessageQueue,即:一个Loop有一个MessageQueue消息队列对象
总结:Looper.prepareMainLooper() 主要作用:
初始化Looper.sThreadLocal.set(new Loop()当前线程唯一对象)
初始化 MessageQueue对象, new Loop() 对应一个MessageQueue对象
2.2. Looper.loop();
下面是部分的关键代码
public static void loop() {
final Looper me = myLooper(); //获取唯一的Loop对象
if (me == null) { //假如sThreadLocal.set()没有赋值,则会抛出异常
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue; //获取队列消息对象
for (;;) {
Message msg = queue.next();
if (msg == null) {
return;
}
try {
// 从Handler.enqueueMessage() 方法里面可以看出,msg.target就是当前的Handler对象
msg.target.dispatchMessage(msg);
}
msg.recycleUnchecked(); //资源缓存和回收
}
}
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
代码里面都有注释,最核心代码是msg.target.dispatchMessage(msg),msg.target就是当前的Handler对象,
接下来我们来看Handler.dispatchMessage().
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
这里就执行了handleMessage()方法
2.3. 资源回收处理 msg.recycleUnchecked()
private static int sPoolSize = 0;
private static final int MAX_POOL_SIZE = 50;
void recycleUnchecked() {
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
结论:Msg 对象范围0到50个
最后图解,如下图:
三. Handler 常见问题
1. 在子线程中创建Handler对象会抛出异常。
原因:Looper.sThreadLocal.set()没有初始化ThreadLocalMap对象,导致Handler.mLooper = null;
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()");
}
}
解决方法:
1.先执行Looper.prepare()初始化,一个线程只能执行一次。
- 创建Handler对象。
- Looper.loop(); //开始循环 发送队列消息 ,如果没有这一句,是接收不到消息的。
2. handler.post() 和 sendMessage的区别。
- 写法不一样
- post()的msg对象是去缓存里面找的,假如缓存没有就创建一个新的msg对象,
sendMessage的msg对象是从外面传进来的,可以自定义带参数。
public final boolean post(Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}