java实现安卓的Handler
Handler机制的写法非常巧妙,这种逻辑方式不仅仅对于安卓,对于其它的windows应用、Swing开发都有可以借鉴的地方,之前还在做安卓的时候就一直很想把代码分离出来。
这里,我就简单地实现了Handler的主要功能,并没有做很细致的配置。代码的整体结构完全参考了Handler(或者说是直接拷贝的),具体的实现中,消息队列使用的是PriorityQueue,消息元素继承自Delayed接口。
只要你用的还是Java,在JDK1.5之后任何地方都可以使用。
缺点显而易见,对象创建太多了!(可以优化)
PriorityQueue队列计时元素
这个类不仅局限于本次代码,同样适用于java.util.concurrent包下其它通过PriorityQueue实现的队列。
此元素标明了每个消息的延迟时间,模仿了Handler发送延迟消息的功能。
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* 延迟队列元素
*
* @author ChenSS on 2017年9月13日
* @param <T>
*/
public class DelayItem<T> implements Delayed {
private long timeout;
private long endTime;
private T item;
public DelayItem(T item, long timeout) {
this.item = item;
this.timeout = timeout;
this.endTime = System.nanoTime() + TimeUnit.NANOSECONDS.convert(timeout, TimeUnit.MILLISECONDS);
}
public DelayItem(T item, long timeout, TimeUnit unit) {
this.item = item;
this.timeout = timeout;
this.endTime = System.nanoTime() + TimeUnit.NANOSECONDS.convert(timeout, unit);
}
public T getItem() {
return this.item;
}
public long getTimeout() {
return timeout;
}
/**
* 重置结束时间
*/
public void reset() throws TimeoutException {
if (System.nanoTime() > endTime)
throw new TimeoutException("The current object has timeout, can not be reset");
this.endTime = System.nanoTime() + TimeUnit.NANOSECONDS.convert(timeout, TimeUnit.MILLISECONDS);
}
/**
* 预计结束时间-当前时间 = 剩余停滞时间(当数值小于零时此对象可用)
*
* @return 纳秒(未达到超时时间对象不可用)
*/
@Override
public long getDelay(TimeUnit unit) {
return unit.convert((endTime - System.nanoTime()), TimeUnit.NANOSECONDS);
}
/**
* 取出结束时间最早的
*/
@Override
public int compareTo(Delayed other) {
if (other == this)
return 0;
if (other instanceof DelayItem) {
long diff = endTime - ((DelayItem<?>) other).endTime;
return (diff == 0) ? 0 : ((diff < 0) ? -1 : 1);
}
long diff = (getDelay(TimeUnit.NANOSECONDS) - other.getDelay(TimeUnit.NANOSECONDS));
return (diff == 0) ? 0 : ((diff < 0) ? -1 : 1);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((item == null) ? 0 : item.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
DelayItem<?> other = (DelayItem<?>) obj;
if (item == null) {
if (other.item != null)
return false;
} else if (!item.equals(other.item))
return false;
return true;
}
}
Handler
源码直接拷贝自安卓的Handler,删除了对于我来说不需要的代码。
package com.yt.test.handler;
public abstract class Handler {
public Looper mLooper;
public MessageQueue mQueue;
public Handler() {
final Class<? extends Handler> klass = getClass();
// java.lang.Class.isAnonymousClass() 当且仅当底层类是匿名类,则返回true。
// java.lang.Class.isMemberClass() 返回true当且仅当底层类是成员类。
// java.lang.Class.isLocalClass()返回true,当且仅当基础类是局部类。
// 原本这是日志,说明Handler不要随便去new,容易内存溢出
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass())) {
System.out.println("The following Handler class should be static or leaks might" + " + klass.getCanonicalName()");
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
}
public Handler(Looper looper) {
}
public final boolean sendMessage(Message msg) {
return sendMessage(msg, 0);
}
public final boolean sendMessage(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessage(msg, delayMillis);
}
public final boolean sendMessage(Message msg, long delayMillis) {
if (delayMillis < 0)
delayMillis = 0;
return sendMessageAtTime(msg, delayMillis);
}
public boolean sendMessageAtTime(Message msg, long delayMillis) {
MessageQueue queue = mQueue;
if (queue == null)
throw new RuntimeException(this + " sendMessageAtTime() called with no mQueue");
return enqueueMessage(queue, msg, delayMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long delayMillis) {
msg.target = this;
return queue.enqueueMessage(msg, delayMillis);
}
public final void dispatchMessage(Message msg) {
handleMessage(msg);
}
abstract public void handleMessage(Message msg);
}
Looper
package com.yt.test.handler;
public class Looper {
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
public MessageQueue mQueue = new MessageQueue();
public Thread mThread;
private Looper() {
}
private Looper(boolean quitAllowed) {
mThread = Thread.currentThread();
}
public static Looper myLooper() {
return sThreadLocal.get();
}
public 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));
}
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
for (;;) {
try {
Message msg = queue.next();
if (msg == null)
return;
msg.target.dispatchMessage(msg);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Message
package com.yt.test.handler;
public class Message {
private static final Message MESSAGE = new Message();
public Handler target;
// 变量,用于定义此Message属于何种操作
public int what;
// 变量,用于定义此Message传递的信息数据,通过它传递信息
public Object obj;
// 变量,传递一些整型数据时使用
public int arg1;
// 变量,传递一些整型数据时使用
public int arg2;
public static Message obtain() {
return MESSAGE;
}
}
MessageQueue
使用了锁和优先级队列,
优先级队列的使用,对比每一个消息的延迟时间,取出最着急等待的消息;
锁主要用于阻塞线程,虽然取出了最着急等待的那个消息,如果系统时间没有达到可以处理这个消息的时间,那么就阻塞线程,等待时间的到达。
这是为了实现Handler发送延迟消息的功能。
package com.yt.test.handler;
import static java.util.concurrent.TimeUnit.NANOSECONDS;
import java.util.PriorityQueue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class MessageQueue {
private final transient ReentrantLock lock = new ReentrantLock();
private final PriorityQueue<DelayItem<Message>> q = new PriorityQueue<>();
private final Condition available = lock.newCondition();
private Thread leader = null;
public Message next() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
for (;;) {
DelayItem<Message> first = q.peek();
if (first == null)
available.await();
else {
long delay = first.getDelay(NANOSECONDS);
if (delay <= 0) {
first = q.poll();
return first.getItem();
}
first = null;
// don't retain ref while waiting
if (leader != null)
available.await();
else {
Thread thisThread = Thread.currentThread();
leader = thisThread;
try {
available.awaitNanos(delay);
} finally {
if (leader == thisThread)
leader = null;
}
}
}
}
} finally {
if (leader == null && q.peek() != null)
available.signal();
lock.unlock();
}
}
public boolean enqueueMessage(Message msg, long uptimeMillis) {
final ReentrantLock lock = this.lock;
DelayItem<Message> item = new DelayItem<Message>(msg, uptimeMillis);
lock.lock();
try {
q.offer(item);
if (q.peek() == item) {
leader = null;
available.signal();
}
return true;
} finally {
lock.unlock();
}
}
}
参照安卓的调用方式
如果你想写一个windows应用,写Swing啊什么的,就用这样的方式实现
package com.yt.test.handler;
public class Test {
public static Handler sMainThreadHandler;
public static void main(String[] args) {
// 启动looper
Looper.prepare(true);
// 创建一个子线程(工作线程,比如加载Activity啊,初始化一堆的对象等等)
startWorkThread();
// 创建一个子线程与主线程通讯的处理器,有需要主线程处理的事情,子线程就通过这个Handler发消息
sMainThreadHandler = initMainHandler();
// 启动主线程的无限轮询
Looper.loop();
}
public static Handler initMainHandler() {
return new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
//TODO: 处理不同的消息(安卓中是一堆类型强转跟消息传递的代码)
default:
// EG:创建一个新的Handler
Test.createActivity();
break;
}
}
};
}
public static void startWorkThread(){
new Thread(() -> {
try {
//延迟,确保Lopper进入循环
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+ " :send msg");
//假设说子线程的作用就是发送消息,要主线程new一个Handler
sMainThreadHandler.sendMessageAtTime(new Message(), 3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
public static void createActivity() {
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
System.out.println(Thread.currentThread().getName()+ " :get msg");
}
};
System.out.println(Thread.currentThread().getName()+ " :send msg");
handler.sendMessage(new Message());
}
}
其实还可以很多的事情
比如说你要一个记日志的Handler啊,输出SQL语句的Handler啊,都可以,Looper从没说非得跑在主线程,你可以使用Looper写一个专门的LooperThread。
package com.yt.test.handler;
public class Test2 {
static Handler handler1;
static Handler handler2;
public static void main(String[] args) {
Thread handlerThread = new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare(true);
handler1 = new Handler() {
@Override
public void handleMessage(Message msg) {
System.out.println("handler1 LOG:" + msg.obj);
}
};
handler2 = new Handler() {
@Override
public void handleMessage(Message msg) {
System.out.println("handler2 SQL:" + msg.obj);
}
};
Looper.loop();
}
});
handlerThread.setName("handlerThread");
handlerThread.setDaemon(true);
handlerThread.start();
try {
Thread.sleep(3000);
int count = 30;
while (count-- > 0) {
final int cn = count;
new Thread(() -> {
Message message = new Message();
String msg = "[" + Thread.currentThread().getName() + "]" + cn;
message.obj = msg;
if (cn % 2 == 0) {
handler1.sendMessage(message);
} else {
handler2.sendMessage(message);
}
}, "THREAD" + count).start();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}