Handler机制,是Android系统的消息队列,一般用于处理主线程的页面绘制等消息处理。
前言
Handler除了Java层外,还有Native的部分,但我能力有限,只能仿写一个Java层的Handler,并且支持延时消息。项目地址:MiniHandler
注:由于消息机制需要使用一个while死循环来让线程不能结束,所以直接将MiniHandler
在Android主线程中使用会卡住主线程,所以需要用一个子线程来运行MiniHandler
,一般使用HandlerThread
,对应到MiniHandler
的MiniHandlerThread
简单介绍
- 延时消息功能:使用
DelayQueue
实现,它需要队列中的消息实体实现一个Delayed
接口,它继承于Comparable
接口,它要求复写一个getDelay()
方法,返回延时多长毫秒。以及需要复写compareTo()
方法,用于比较2个消息谁先谁后,来决定队列中的消息排序。
类结构介绍
- Looper,轮训器,内部包含一个
MessageQueue
消息队列,并且使用ThreadLocal
将每个线程绑定一个Looper
。所以一个线程只有一个Looper
,一个Looper
也只有一个MessageQueue
消息队列 - MessageQueue,消息队列,使用
DelayQueue
实现队列功能,支持延时消息 - Message,消息实体类,使用享元模式支持消息对象复用
- MiniHandler,消息处理器以及消息发送器
- MiniHandlerThread,原自Android源码中的HandlerThread,改用MiniHandler实现,就是一个带有Handler事件循环的子线程
原理
-
Handler
,发送Message
消息到MessageQueue
,会被Looper
轮训器取出 -
Looper
轮训器中,有一个while循环,不断从MessageQueue
消息队列中取出消息进行处理,如果队列中没有消息,MessageQueue
会阻塞当前线程 - Handler处理完消息,
Message
消息回收到池中,复用消息能提高效率,以及减少对象创建
基本使用
MiniHandler的API和Handler的一致,2者的Java层API只是类名不同。
/**
* Toast任务
*/
public static final int ACTION_TOAST = 1;
/**
* 延时任务
*/
public static final int ACTION_DELAY = 2;
////事件处理线程
MiniHandlerThread handlerThread = new MiniHandlerThread("handler-thread");
handlerThread.start();
//创建MiniHandler实例,并绑定到MiniHandlerThread
MiniHandler eventHandler = new MiniHandler(mHandlerThread.getLooper()) {
@Override
public void handleMessage(Message message) {
super.handleMessage(message);
long action = message.what;
if (action == ACTION_TOAST) {
String msg = message.obj.toString();
ToastUtil.toast(getApplicationContext(), msg);
} else if (action == ACTION_DELAY) {
String delayMsg = message.obj.toString();
ToastUtil.toast(getApplicationContext(), delayMsg);
}
}
};
//发消息到立即执行事件线程
eventHandler.sendMessage(Message.obtain(ACTION_TOAST, "Toast~"));
//或者
eventHandler.post(new Runnable() {
@Override
public void run() {
ToastUtil.toast(getApplicationContext(), 延时消息~);
}
});
//发送延时消息到事件线程
eventHandler.sendMessageDelayed(Message.obtain(ACTION_DELAY, "延时消息~"), 2000);
//或者
eventHandler.postDelayed(new Runnable() {
@Override
public void run() {
ToastUtil.toast(getApplicationContext(), 延时消息~);
}
}, 1000);
//退出MiniHnadlerThread
handlerThread.quitSafely();
源码
Message消息实体
消息实体类,使用享元模式支持消息对象复用。为了支持延时消息,消息实体,需要实现一个Delayed
接口,它继承于Comparable
接口,它要求复写一个getDelay()
方法,返回延时多长毫秒。以及需要复写compareTo()
方法,用于比较2个消息谁先谁后,来决定队列中的消息排序。
/**
* 消息事件实体
*/
public class Message implements Delayed {
/**
* 使用中的标志位
*/
static final int FLAG_IN_USE = 1;
int flags;
/**
* 为了形成消息链表
*/
Message next;
/**
* 对象锁
*/
public static final Object sPoolSync = new Object();
/**
* 消息链表的头节点
*/
private static Message sPool;
/**
* 当前链表中数据的个数
*/
private static int sPoolSize = 0;
/**
* 总共可使用的消息链表大小
*/
private static final int MAX_POOL_SIZE = 50;
/**
* 消息的标识
*/
public long what;
/**
* 消息的附件
*/
public Object obj;
/**
* 消息的处理器
*/
public MiniHandler target;
/**
* 要执行的任务,可为null
*/
public Runnable callback;
/**
* 指定的执行时间,毫秒值
*/
public long workTimeMillis;
/**
* 回收Message
*/
void recycleUnchecked() {
//把标记设置为使用中
flags = FLAG_IN_USE;
//清理所有字段
what = 0;
obj = null;
target = null;
callback = null;
workTimeMillis = 0;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
/**
* 创建一个Message对象
*/
public static Message obtain() {
synchronized (sPoolSync) {
//判断头节点是否null
if (sPool != null) {
//取出头节点
Message m = sPool;
//将头节点的下一个作为最新的头节点
sPool = m.next;
//设置需要返回的消息的next为空
m.next = null;
//清除使用中的标志位
m.flags = 0;
sPoolSize--;
return m;
}
}
//如果消息链表为空 创建新的message
return new Message();
}
/**
* 创建一个Message对象,并绑定处理它的Handler
*/
public static Message obtain(MiniHandler handler) {
return obtain(handler, 0);
}
/**
* 创建一个Message对象,并绑定处理它的Handler、消息标识what
*/
public static Message obtain(MiniHandler handler, long what) {
return obtain(handler, what, null);
}
/**
* 创建一个Message对象,绑定消息标识what
*/
public static Message obtain(long what) {
return obtain(what, null);
}
/**
* 创建一个Message对象,绑定消息标识what、消息附件obj
*/
public static Message obtain(long what, Object obj) {
return obtain(null, what, obj);
}
/**
* 创建一个Message对象,并绑定处理它的Handler、消息标识what、消息附件obj
*/
public static Message obtain(MiniHandler handler, long what, Object obj) {
Message message = obtain();
message.target = handler;
message.what = what;
message.obj = obj;
return message;
}
@Override
public long getDelay(TimeUnit unit) {
return unit.convert(workTimeMillis
- System.currentTimeMillis(), TimeUnit.MILLISECONDS);
}
@Override
public int compareTo(Delayed o) {
return (int) (this.getDelay(TimeUnit.MILLISECONDS) - o.getDelay(TimeUnit.MILLISECONDS));
}
}
MessageQueue消息队列
消息队列,使用DelayQueue
实现队列功能,支持延时消息
public class MessageQueue {
/**
* 是否退出
*/
volatile boolean isQuit;
/**
* 消息队列
*/
private final BlockingQueue<Message> mMessageQueue = new DelayQueue<>();
/**
* 消息入队
*/
public boolean enqueueMessage(Message message, long uptimeMillis) {
try {
message.workTimeMillis = uptimeMillis;
mMessageQueue.put(message);
return true;
} catch (InterruptedException e) {
e.printStackTrace();
}
return false;
}
/**
* 拿出一条消息
*/
public Message next() {
try {
//如果队列中没有消息,就会阻塞在这里
return mMessageQueue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
/**
* 移除队列中指定Handler的未执行的回调和消息
*/
public final void removeCallbacksAndMessages(MiniHandler handler) {
if (handler == null) {
return;
}
List<Message> targetMessages = new ArrayList<>();
for (Message message : mMessageQueue) {
if (message.target == handler) {
targetMessages.add(message);
}
}
mMessageQueue.removeAll(targetMessages);
}
public void quit() {
mMessageQueue.clear();
isQuit = true;
}
}
Looper轮训器
轮训器,内部包含一个MessageQueue
消息队列,并且使用ThreadLocal
将每个线程绑定一个Looper
。所以一个线程只有一个Looper
,一个Looper
也只有一个MessageQueue
消息队列
public class Looper {
private static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<>();
/**
* 消息队列
*/
final MessageQueue mMessageQueue;
private Looper() {
//一个线程,只有一个Looper,一个Looper也只有一个消息队列
mMessageQueue = new MessageQueue();
}
/**
* 把当前线程和Looper进行绑定
*/
public static void prepare() {
Looper looper = sThreadLocal.get();
if (looper != null) {
throw new RuntimeException("一个线程只能绑定一个Looper,请确保prepare方法在一个线程中只调用一次");
}
sThreadLocal.set(new Looper());
}
/**
* 获取当前线程的Looper
*/
public static Looper myLooper() {
return sThreadLocal.get();
}
/**
* 开始循环从队列中取出消息
*/
public static void loop() {
//获取当前线程的轮询器
Looper looper = myLooper();
MessageQueue queue = looper.mMessageQueue;
while (!queue.isQuit) {
Message message = queue.next();
try {
message.target.dispatchMessage(message);
} finally {
//回收Message对象
message.recycleUnchecked();
}
}
}
/**
* 安全退出,会等所有事件都执行完,再关闭
*/
public void quitSafely() {
mMessageQueue.quit();
}
}
MiniHandler消息处理器
消息处理器以及消息发送器
public class MiniHandler {
/**
* 消息队列
*/
private final MessageQueue mMessageQueue;
/**
* 事件回调,可以选择构造时传入一个Callback,就可以不需要复写handleMessage()方法进行处理
*/
private MiniHandler.Callback mCallback;
public MiniHandler() {
this(Looper.myLooper());
}
public MiniHandler(Callback callback) {
Looper looper = Looper.myLooper();
//Looper没有绑定
if (looper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mMessageQueue = looper.mMessageQueue;
mCallback = callback;
}
public MiniHandler(Looper looper) {
//Looper没有绑定
if (looper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mMessageQueue = looper.mMessageQueue;
}
public interface Callback {
/**
* @param msg A {@link android.os.Message Message} object
* @return True if no further handling is desired
*/
boolean handleMessage(Message msg);
}
/**
* 移除队列中指定Handler的未执行的回调和消息
*/
public final void removeCallbacksAndMessages() {
mMessageQueue.removeCallbacksAndMessages(this);
}
/**
* 发送消息到消息队列中
*/
public boolean sendMessage(Message message) {
return sendMessageDelayed(message, 0);
}
/**
* 在主线程执行一个Runnable
*/
public final boolean post(Runnable task) {
return sendMessageDelayed(getPostMessage(task), 0);
}
/**
* 延迟一段时间后,在主线程执行一个Runnable
*
* @param delayMillis 延时时间,毫秒值
*/
public final boolean postDelayed(Runnable task, long delayMillis) {
return sendMessageDelayed(getPostMessage(task), delayMillis);
}
/**
* 获取一个带Runnable任务的Message
*/
private static Message getPostMessage(Runnable task) {
Message m = Message.obtain();
m.callback = task;
return m;
}
/**
* 发送延时消息
*
* @param message 消息
* @param delayMillis 延时时间
*/
public final boolean sendMessageDelayed(Message message, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(message, System.currentTimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(Message message, long uptimeMillis) {
MessageQueue queue = this.mMessageQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, message, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message message, long uptimeMillis) {
message.target = this;
return queue.enqueueMessage(message, uptimeMillis);
}
/**
* 分发消息给Handler进行处理
*/
void dispatchMessage(Message message) {
//如果是post()或postDelayed()发出的任务,则执行这个任务
if (message.callback != null) {
handleCallback(message);
} else {
//如果在Handler构造时传入了Callback,则回调这个Callback
if (mCallback != null) {
if (mCallback.handleMessage(message)) {
return;
}
}
//都没有,则调用handleMessage(),Handler的子类可以复写该方法进行事件处理
handleMessage(message);
}
}
/**
* 执行Message绑定的任务
*/
private static void handleCallback(Message message) {
message.callback.run();
}
/**
* 子类重写该方法进行处理消息
*/
public void handleMessage(Message message) {
}
}
MiniHandlerThread
原自Android源码中的HandlerThread,改用MiniHandler实现,就是一个带有Handler事件循环的子线程
/**
* A {@link Thread} that has a {@link Looper}.
* The {@link Looper} can then be used to create {@link MiniHandler}s.
* <p>
* Note that just like with a regular {@link Thread}, {@link #start()} must still be called.
*/
public class MiniHandlerThread extends Thread {
int mPriority;
int mTid = -1;
Looper mLooper;
private MiniHandler mHandler;
public MiniHandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
/**
* Constructs a HandlerThread.
*
* @param name
* @param priority The priority to run the thread at. The value supplied must be from
* {@link android.os.Process} and not from java.lang.Thread.
*/
public MiniHandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
/**
* Call back method that can be explicitly overridden if needed to execute some
* setup before Looper loops.
*/
protected void onLooperPrepared() {
}
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
/**
* This method returns the Looper associated with this thread. If this thread not been started
* or for any reason isAlive() returns false, this method will return null. If this thread
* has been started, this method will block until the looper has been initialized.
*
* @return The looper.
*/
public Looper getLooper() {
if (!isAlive()) {
return null;
}
// If the thread has been started, wait until the looper has been created.
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
/**
* @return a shared {@link MiniHandler} associated with this thread
* @hide
*/
public MiniHandler getThreadHandler() {
if (mHandler == null) {
mHandler = new MiniHandler(getLooper());
}
return mHandler;
}
/**
* Quits the handler thread's looper safely.
* <p>
* Causes the handler thread's looper to terminate as soon as all remaining messages
* in the message queue that are already due to be delivered have been handled.
* Pending delayed messages with due times in the future will not be delivered.
* </p><p>
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
* For example, the {@link MiniHandler#sendMessage(Message)} method will return false.
* </p><p>
* If the thread has not been started or has finished (that is if
* {@link #getLooper} returns null), then false is returned.
* Otherwise the looper is asked to quit and true is returned.
* </p>
*
* @return True if the looper looper has been asked to quit or false if the
* thread had not yet started running.
*/
public boolean quitSafely() {
Looper looper = getLooper();
if (looper != null) {
looper.quitSafely();
return true;
}
return false;
}
/**
* Returns the identifier of this thread. See Process.myTid().
*/
public int getThreadId() {
return mTid;
}
}
网友评论