Handler的主要作用是讲一个任务切换到某个指定的线程中工作,主要用于更新UI,因为Android规定访问UI只能在主线程中进行(Android的UI控件不是线程安全的,上锁机制会让UI的访问逻辑复杂,且锁机制会降低UI访问效率,所以最简单高效的方法就是用单线程模型来处理UI操作)。
Handler创建时会采用当前线程的Looper来构建内部的消息循环系统。
通过Handler的post方法将一个Runnable或通过send方法发一个消息到Looper中,post方法最终也是调到send,send方法调用MessageQueue的enqueueMessage方法将消息放入消息队列,然后Looper发现会进行处理,最终消息中的Runnable或者Handler的handleMessage会被调用,由于Looper是运行在创建Handler所在的线程中的,这样Handler中的业务逻辑就被切换到创建Handler的线程中去执行了。
ThreadLocal 是一个线程内部的数据存储类,通过它可以再指定的线程中存储数据,Looper、ActivityThread、AMS中都有用到。有 set 和 get 方法
使用场景分析:
1)对于Handler来说,它需要获取当前线程的looper,显然Looper的作用域就是线程并且不同的线程有不同的Looper,这时就可以使用ThreadLocal实现Looper在线程中的存取。
2)复杂逻辑下的对象传递,比如监听器的传递,采用ThreadLocal可以让监听器作为线程内的全局对象而存在。
是一个单链表
1)插入 enqueueMessage,单链表的插入
2)读取并删除 next,是一个无限循环的方法,如果消息队列中没有消息,next方法会一直阻塞在这里,当新消息到来时,next方法会返回这条消息并将其从单链表中移除
3)这里创建Message对象的时候,有三种方式:
Message msg1 = new Message(); // 就是直接初始化一个Message对象
Message msg2 = Message.obtain();
Message msg3 = handler.obtainMessage();
2、3种方式一样,从整个Messge池中返回一个新的Message实例,Messge池为空才创建新的对象,能避免重复Message创建对象,源码:
/**
* Return a new Message instance from the global pool. Allows us to
* avoid allocating new objects in many cases.
*/
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
不停的从MessageQueue中查看是否有新消息,如果有立刻处理,否则阻塞在那里。
1)构造方法:创建一个MessageQueue;将当前线程的对象保存起来
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
2)创建Looper:Looper.prepare()为当前线程创建一个Looper,Looper.loop() 开启消息循环
new Thread("123") {
@Override
public void run() {
super.run();
Looper.prepare();
Handler handler = new Handler();
Looper.loop();
};
}.start();
3)Looper.getMainLooper() 获取主线程Looper
4)退出:quit 和 quitSafely
quit直接退出,quitSafely设定一个退出标记,等消息队列中已有的消息处理完毕后退出。
Looper退出后,Handler发送消息会失败,send返回false。
子线程如果手动创建Looper在所有事情完成后记得用quit终止循环,退出Looper后,这个线程也立即终止。
5)loop方法:死循环,唯一跳出循环的方式是MessageQueue的next方法返回null
Looper的quit方法调用,会调用MessageQueue的quit或quitSafely方法通知消息队列退出,此时next返回null
6)loop 方法调用MessageQueue的next方法获取新消息,而next是一个阻塞操作,当没有消息时,next会一直阻塞在那里,导致loop也一直阻塞在那里;如果MessageQueue的next返回了新消息,Looper就会处理这条消息:
msg.target.dispatchMessage(msg),这里的msg.target 是发送这条消息的Handler对象,这样Handler发送的消息最终又交给它的dispatchMessage来处理了,注意,Handler的dispatchMessage方法是在创建 Handler时所使用的Looper中执行的,这样就成功将代码逻辑切换到指定的线程中去执行了。
发送消息和接收
1)发送通过post或者send,post最终也是通过send,发送消息的过程就是向消息队列中插入一条消息,MessageQueue的next方法会将这条消息给Looper
2)Looper收到消息后开始处理,最终交由Handler
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
首先检查 Message的callback是否为null,不为bukk就通过handleCallback 来处理消息,Message的callback 是一个 Runnable 对象,实际上就是Handler 的 post方法所传递的Runnable参数
private static void handleCallback(Message message) {
message.callback.run();
}
其次检查mCallback 是否为null,不为null就调用 mCallback 的 handleMessage 方法。
Callback 是一个接口,可以用来创建一个Handler的实例但不需要派生Handler的子类
Handler handler = new Handler(callback);
最后调用Handler 的 handleMessage 方法来处理消息。
3)Handler 还可以通过一个Looper来构造
public Handler(Looper looper) {
this(looper, null, false);
}
Activity 的 主线程就是 ActivityThread,入口为main,main方法中系统通过 Looper.prepareMainLooper() 来创建主线程的 Looper 以及 MessageQueue,并通过 Looper.loop() 来开启主线程的消息循环
ActivityThread.H 是其Handler,定义了一组消息类型,包含了四大组件的启动和停止等过程
ActivityThread 通过 ApplicationThread 和 AMS 进行进程间通信,AMS 以进程间通信的方式完成 ActivityThread 的请求后会回调 ApplicationThread 中的Binder方法,然后 ApplicationThread 会向 H发送消息,H收到消息后会将 ApplicationThread 中的逻辑切换到 ActivityThread中去执行,即切换到主线程中去执行,这个过程就是主线程的消息循环模型
1)在主线程创建
// 方法1 使用内部类方式创建Handler
class mHandler extends Handler {
// 复写handlerMessage()
@Override
public void handleMessage(Message msg) {
...// 需执行的UI操作
}
}
// 方法2 使用匿名内部类创建Handler
Handler handler = new Handler(){
// 复写handlerMessage()
@Override
public void handleMessage(Message msg) {
//msg就是子线程发送过来的消息。
}
};
//开启一个子线程 发消息方式1
new Thread(new Runnable() {
@Override
public void run() {
//在子线程发送一个消息。
Message msg = new Message();
handler.sendMessage(msg);
}
}).start();
//开启一个子线程 发消息方式2
new Thread(new Runnable() {
@Override
public void run() {
//在子线程post一个Runnable对象
handler.post(new Runnable() {
@Override
public void run() {
//这里是消息处理的方法
//这里运行在主线程。
}
});
}
}).start();
注意:Handler的内存泄漏
as不建议我们用上述方式生成Handler,这是应为非静态内部类会持有外部内的引用。那么Handler将会持有Activity的引用,我们知道handler是会被msg.target持有的,而msg又在MessageQueue队列中,那么当消息队列中拥有未消费的Message时,会导致Activity即使finish了也无法被GC回收,最终导致内存泄漏。为了避免这个问题我们可以将Handler写成外部类或者静态的内部类,并且传递的Activity引用可以用WeakReference弱引用来持有,同时可以在Activity的onDestory中使用Handler.removeCallbacksAndMessages(null);来清空消息队列
2)在子线程创建方式1 使用子线程的Looper 消息处理在子线程
// 声明Handler
Handler handler;
new Thread(new Runnable() {
@Override
public void run() {
// 创建当前线程的Looper
Looper.prepare();
// 在子线程创建handler对象
handler = new Handler() {
@Override
public void handleMessage(Message msg) {
// 这里是消息处理,它是运行在子线程的
}
};
// 开启Looper的消息轮询
Looper.loop();
}
}).start();
mBanner.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 在主线程发送一个消息到子线程
Message msg = new Message();
handler.sendMessage(msg);
}
});
3)在子线程创建方式2 使用主线程的Looper 消息处理在主线程
new Thread(new Runnable() {
@Override
public void run() {
// 获取主线程的Looper
Looper looper = Looper.getMainLooper();
// 用主线程的Looper创建Handler
handler = new Handler(looper) {
@Override
public void handleMessage(Message msg) {
// 这里是运行在主线程的
}
};
}
}).start();
4)使用 Callback接口
public class MyClass implements Handler.Callback {
...
private Handler handler;
// 主线程的Looper
handler = new Handler(Looper.getMainLooper(), this);
// 子线程的Looper
HandlerThread handlerThread = new HandlerThread("subthread");
handlerThread.start();
handler = new Handler(handlerThread.getLooper(), this);
@Override
public boolean handleMessage(Message msg) {
...
}
}
**1)Android已经提供了很多实现了Handler的类和方法,方便我们使用。**如Activity类的runOnUiThread()方法,View的post()方法,HandlerThread类等,关于这些知识,有待补充
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
postInvalidate底层是使用了Handler,向主线程发消息更新UI
HandlerThread 是Android API提供的一个方便、便捷的类,使用它我们可以快速的创建一个带有Looper的线程。Looper可以用来创建Handler实例
2)Android中为什么主线程不会因为Looper.loop()里的死循环卡死
3)一个线程只能有一个Looper
public static void prepare() {
prepare(true);
}
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));
}
本文参考:
《Android开发艺术探索》第十章
https://juejin.im/post/5910533dac502e006cfe01cd