上一篇,详细分析了原理和handler机制的源码(传送门 -> https://blog.csdn.net/yaojie5519/article/details/100579131)
这一篇,将自己动手“丰衣足食”,尝试手写一套Handler代码
一、分析
按照上一篇内容,画出UML类图(只包含主要的部分方法)如下:
注意几个类的关系和主要方法。
上一篇内容有几个点需要补充一下:
1、在MessageQueue中,添加消息和取出消息,都有同步锁的存在,这是为了防止多个线程同时操作消息队列,防止同时读写,导致消息错乱。
所以我们在手写的时候也需要一个锁。
2、在activity中使用handler,若不是静态内部类,handler对象会隐式的持有一个外部类对象(一般是该activity)。这样就有内存泄漏的风险,容易导致OOM,那么解决方法是什么:
1)在关闭Activity的时候停掉你的后台线程。线程停掉了,就相当于切断了Handler和外部连接的线,Activity自然会在合适的时候被回收。
2)将handler声明为静态类
静态类不持有外部类对象,这时便不会有内存泄漏风险。不过此时就需要对activity有一个弱引用了
public static class MyHandler extends Handler {
WeakReference<Activity> mWeakReference;
public MyHandler(Activity activity) {
mWeakReference=new WeakReference<Activity>(activity);
}
@Override
public void handleMessage(Message msg){
final Activity activity=mWeakReference.get();
if(activity!=null){
if (msg.what == 1){
//刷新UI
}
}
}
}
3、loop()的无限循环会不会很消耗CPU资源?
答案是不会。当MessageQueue中无消息对象时,主线程会阻塞在loop的queue.next()中的nativePollOnce()方法里。此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒主线程工作。这里采用的epoll机制,是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质同步I/O,即读写是阻塞的。
二、上手
Handler:
public class Handler {
private Looper mlooper;
private MessageQueue mQueue;
public Handler() {
mlooper = Looper.myLooper();
mQueue = mlooper.messageQueue;
}
public void sendMessage(Message message){
message.target = this;
mQueue.enqueueMessage(message);
}
public void handlerMessage(Message message){
}
public void dispatchMessage(Message message){
handlerMessage(message);
}
}
Looper:
public class Looper {
final MessageQueue messageQueue;
private static ThreadLocal<Looper> threadLocal = new ThreadLocal<>();
public Looper() {
messageQueue = new MessageQueue();
}
public static void prepare() {
if (threadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
threadLocal.set(new Looper());
System.out.println("looper初始化");
}
public static Looper myLooper() {
return threadLocal.get();
}
public static void loop() {
Looper me = myLooper();
Message msg;
for (; ; ) {
msg = me.messageQueue.next();
if (msg == null || msg.target == null){
System.out.println("Looper:空消息");
continue;
}
System.out.println("Looper:looper轮询到了消息,发送消息");
msg.target.dispatchMessage(msg);
}
}
}
Message:
public class Message {
public int what;
public Object object;
public Handler target;
private static final Object sPoolSync = new Object();
private static Message sPool;
Message next;
int flags;
private static int sPoolSize = 0;
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();
}
@Override
public String toString() {
return object.toString();
}
}
MessageQueue:
public class MessageQueue {
Message[] mItems;
int mPutIndex;
private int mCount;
private int mTakeIndex;
Lock mlock;
Condition getCondition;
Condition addCondition;
public MessageQueue() {
mItems = new Message[50];
mlock = new ReentrantLock();
getCondition = mlock.newCondition();
addCondition = mlock.newCondition();
}
/**
* 取消息出列
* @return
*/
Message next(){
Message msg = null;
try {
mlock.lock();
while (mCount <= 0){
System.out.println("MessageQueue:队列空了,读锁阻塞");
getCondition.await();
}
msg = mItems[mTakeIndex];
mItems[mTakeIndex] = null;
mTakeIndex = (++mTakeIndex >= mItems.length)?0:mTakeIndex;
mCount--;
addCondition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
mlock.unlock();
}
return msg;
}
/**
* 添加消息入列
* @param message
*/
public void enqueueMessage(Message message){
try {
mlock.lock();
while (mCount >= mItems.length){
System.out.println("MessageQueue:队列空了,写锁阻塞");
addCondition.await();
}
mItems[mPutIndex] = message;
mPutIndex = (++ mPutIndex >= mItems.length)?0:mPutIndex;
mCount ++;
getCondition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
mlock.unlock();
}
}
}
三、测试
private void test(){
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
handler = new Handler(){
@Override
public void handlerMessage(Message message) {
super.handlerMessage(message);
System.out.println("Test:"+Thread.currentThread().getName()+"线程接收到:"+message.object);
}
};
Looper.loop();
}
}).start();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(new Runnable() {
@Override
public void run() {
Message message = new Message();
message.object = Thread.currentThread().getName() + "发送了消息";
handler.sendMessage(message);
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
Message message = new Message();
message.object = Thread.currentThread().getName() + "发送了消息";
handler.sendMessage(message);
}
}).start();
}
结果: