面试的时候经常会问handler原理啥的,前段时间刚好看了一个老师讲handle机制,老师讲得很仔细清晰,这里我自己也用代码模拟安卓handler实现一个基本线程通信。
说到handler就不得不说消息处理的五大组成部分:Message,Handler,Message Queue,Looper和ThreadLocal。首先简要的了解这些对象的概念;
Message:message就是一个数据模型吧,它的作用仅限于线程之间通信的时候传递消息,他可以携带少量数据,用于线程之间传递信息,常用的四个字段target,what,obj,arg;
target:消息回调后的作用域类,通常是一个handler。
what:是一个区分不同消息的标识符。
obj:这是obj是一个对象类型,可以携带自定义的类。
arg:int类型,携带的参数。
下面贴出Message代码:
public class Message {
Handler target;
public int what;
public Object obj;
@Override
public String toString() {
return obj.toString();
}
}
handler:它主要用于发送和接收消息,有三个主要方法,这里实现基本功能,不探讨具体细节
sendMessage();
dispatchMessage();
handleMessage();
它主要用于发送和处理消息的发送消息一般使用sendMessage()方法,还有其他的一系列sendXXX的方法,但最终都是调用了sendMessageAtTime方法,把消息压入消息队列中。
而发出的消息经过一系列的辗转处理后,最终会传递到Handler的handleMessage方法中。这里我们handleMessage()方法在外部重写,内部实现调用。
public class Handler {
private MessageQueue mQueue;
private Looper mLooper;
// Handler的初始化在主线程中完成
public Handler(){
//获取主线程的looper对象
mLooper = Looper.myLooper();
this.mQueue = mLooper.mQueue;
}
// 发送消息,压入队列
public void sendMessage(Message msg){
msg.target = this;
mQueue.enqueueMessage(msg);
}
//内部调用,外部实现
public void handleMessage(Message msg){
}
// 转发
public void dispatchMessage(Message msg){
handleMessage(msg);
}
}
MessageQueue是消息队列,主要存放所有handler发送过来的消息,这些消息会一直存放消息队列中,等待被处理,每一个线程只有一直MessageQueue队列。
这里实现的消息队列里面有一个入栈和出栈函数,这两个函数的关系是一个生产者和消费者的关系,我们在Looper.loop方法中通过while死循环方法不断检测生产者方法,一旦消息队列不为空,立即取出消息并处理,同时消息的总数量也相应需要改变。
public class MessageQueue {
//通过数组的结构存储message对象
Message[] items;
//入队和出队元素索引位置
int putIndex;
int takeIndex;
// 计数器
int count;
// synchronized (msg){} 代码块加锁
// 互斥锁
Lock lock;
// 条件变量
Condition notEmpty;
Condition notFull;
public MessageQueue(){
this.items = new Message[50];
this.lock = new ReentrantLock(); //这个锁和sychronize还是有些区别的,ReentrantLock 类实现了 Lock ,它拥有与synchronized 相同的并发性和内存语义,但是添加了类似轮询锁、定时锁等候和可中断锁等候的一些特性。此外,它还提供了在激烈争用情况下更佳的性能。(换句话说,当许多线程都想访问共享资源时,JVM 可以花更少的时候来调度线程,把更多时间用在执行线程上。)
this.notEmpty = lock.newCondition();
this.notFull = lock.newCondition();
}
// 加入队列
// 生产
public void enqueueMessage(Message msg){
System.out.println("加入队列");
try {
lock.lock();
//消息队列满了,子线程停止发送消息,阻塞
while (count == items.length){
try {
notFull.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
items[putIndex] = msg;
//循环取值
putIndex = (++ putIndex == items.length)?0:putIndex;
count ++;
//生产出来新的message对象,通知主线程
notEmpty.signalAll();
}finally {
lock.unlock();
}
}
// 出队列
// 消费
public Message next(){
//消息队列空了,子线程停止发送消息,阻塞
Message msg = null;
try {
lock.lock();
while (count == 0){
try {
notEmpty.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
msg = items[takeIndex];
items[takeIndex] = null; //元素重置空
takeIndex = (++takeIndex == items.length) ? 0 : takeIndex;
count --;
//使用列一个message对象,通知子线程,可以继续生产
notFull.signalAll();
}finally {
lock.unlock();
}
return msg;
}
}
Looper:每个线程通过Handler发送的消息都保存在,MessageQueue中,Looper通过调用loop()的方法,就会进入到一个无限循环当中,然后每当发现Message Queue中存在一条消息,就会将它取出,并传递到Handler的handleMessage()方法中。每个线程中只会有一个Looper对象。
/**
* 一个线程对应一个looper对象,一个looper对应一个消息队列
*/
public final class Looper {
//每一个主线程都有一个looper对象
//Looper 对象保存在threadlocal中,保证列线程数据的隔离
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
// 一个looper对象 对应一个消息队列
MessageQueue mQueue;
private Looper(){
mQueue = new MessageQueue();
}
//Looper对象初始化
public static void prepare(){
if (sThreadLocal.get()!=null){
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}
//获取当前线程的looper对象
public static Looper myLooper(){
return sThreadLocal.get();
}
//轮询消息队列
public static void loop(){
Looper me = myLooper();
if (me == null){
throw new RuntimeException("not Looper; Looper.prepare() wait call");
}
MessageQueue queue = me.mQueue;
for (;;){
Message msg = queue.next();
if (msg == null){
continue;
}
// 转发给handler
msg.target.dispatchMessage(msg);
}
}
}
在Main方法中写个测试例子
public void test(){
//轮询器初始化
Looper.prepare();
// 主线程当中
final Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
System.out.println(Thread.currentThread().getName() + ",receive:"+msg.toString());
}
};
for (int i=0;i<10;i++){
new Thread(){
@Override
public void run() {
while (true){
Message msg = new Message();
msg.what = 1;
synchronized (UUID.class){
msg.obj = Thread.currentThread().getName()+",send message"+ UUID.randomUUID().toString();
}
System.out.println(msg);
handler.sendMessage(msg);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
//开启轮询
Looper.loop();
}
打印结果如下:
// 08-18 19:13:28.149 8051-8129/com.dcw.handler I/System.out: Thread-5,send message41e677cd-0eb0-467b-a633-36a2e9c0cdc0
// 08-18 19:13:28.149 8051-8129/com.dcw.handler I/System.out: 加入队列
// 08-18 19:13:28.149 8051-8136/com.dcw.handler I/System.out: Thread-12,send messageebd27bf8-b157-466a-bab5-8d6057334ed2
// 08-18 19:13:28.150 8051-8136/com.dcw.handler I/System.out: 加入队列
// 08-18 19:13:28.150 8051-8051/com.dcw.handler I/System.out: main,receive:Thread-5,send message41e677cd-0eb0-467b-a633-36a2e9c0cdc0
// 08-18 19:13:28.150 8051-8051/com.dcw.handler I/System.out: main,receive:Thread-12,send messageebd27bf8-b157-466a-bab5-8d6057334ed2
// 08-18 19:13:28.154 8051-8137/com.dcw.handler I/System.out: Thread-13,send message3c1ae78b-8940-4a12-bb60-815442917edc
// 08-18 19:13:28.155 8051-8137/com.dcw.handler I/System.out: 加入队列
// 08-18 19:13:28.155 8051-8051/com.dcw.handler I/System.out: main,receive:Thread-13,send message3c1ae78b-8940-4a12-bb60-815442917edc
// 08-18 19:13:28.159 8051-8134/com.dcw.handler I/System.out: Thread-10,send messagef6beb349-975c-4e17-a475-d3cc7c5bd94f
// 08-18 19:13:28.159 8051-8134/com.dcw.handler I/System.out: 加入队列
// 08-18 19:13:28.159 8051-8135/com.dcw.handler I/System.out: Thread-11,send message8d8ecf6e-3c76-4bad-b7af-dc916cb96b39
// 08-18 19:13:28.159 8051-8135/com.dcw.handler I/System.out: 加入队列
// 08-18 19:13:28.160 8051-8051/com.dcw.handler I/System.out: main,receive:Thread-10,send messagef6beb349-975c-4e17-a475-d3cc7c5bd94f
// 08-18 19:13:28.160 8051-8051/com.dcw.handler I/System.out: main,receive:Thread-11,send message8d8ecf6e-3c76-4bad-b7af-dc916cb96b39
// 08-18 19:13:29.149 8051-8130/com.dcw.handler I/System.out: Thread-6,send messagec7afaa49-e6b0-49a9
结果分析可以看到:只要子线程中一加入消息,那么looper就会轮询从massagequeue中取出消息并且通过dispatchMessage发送到主线程中取执行。我个人觉得所谓主线程,只不过比子线程中多了一个looper,我们的UI线程在只能在主线中刷新,就是应为线程的loop方法不断轮询绘制的原因,子线程之所有不能刷新UI,是因为子线程没有loop方法,如果我们把子线程中设置一个looper,那么子线程也是可以刷新绘制UI的。
以上是我个人见解,欢迎大家斧正。
网友评论