前言
之前写了文章分析了handler切换线程和Looper机制的源码,
然后就想,这套东西是不是自己可以实现一套?好奇心重的我呀~~撸起袖子就是干!
分析
首先,这套东西是完全自己实现,要和android之前的那套完全脱离,所以不能引入android sdk,只能建一个java library。
其次,自己撸的这东东,也要能切换线程,也要有消息的循环机制(入队列、出队列、线程阻塞和唤醒)。啧啧,好像功能也挺强大啊哈哈。
根据之前的分析,android源码里有几个角色:Handler、Looper、Message、MessageQueue是java层的,Handler负责往MessageQueue里加Message,Looper负责取;native层的Looper负责线程的阻塞和唤醒,java层MessageQueue为空的时候阻塞,有消息添加的时候唤醒。其实各个角色的职责已经很清楚了。
java层的代码我可以模仿着来一套,native我却不打算和android源码一样玩了,不然不就等于是复制粘贴了一遍嘛,程序员要有梦想的嘛~~。
native层说白了只负责阻塞和唤醒,只是这样我java大法也是搞的定的嘛。这个功能怎么看都像是一个BlockingQueue(阻塞队列)。当队列为空的时候阻塞,队列有内容的时候唤醒,另一头一直在取,取不到(队列为空)就一直等待,这似乎可以替代使用native。源码用native,估计是方便C++ 和java层都能复用吧。
类设计
MessageQueue.java
上面说了,使用BlockingQueue。具体的,我用了DelayQueue,它是BlockingQueue也是PriorityQueue,它是个链表式的阻塞队列,链表结构的好处就是不需要连续的内存空间,而且可以“无限大”,更重要的是,它本身就可以按时间排序,这也符合功能要求,可以不用自己写单链表排序。但是这个Queue 和 android源码中的MessageQueue不一样,它是个真正的“队列”,满足按优先输出时间小的消息的特征。
/**
*
* @author yueshaojun988
* @date 2017/10/24
* 消息队列类
*/
class MessageQueue {
private DelayQueue<Message> delayQueue = new DelayQueue<>();
MessageQueue(){
}
/**
* 取出消息
* @return
*/
Message next(){
Message msg = null;
try {
msg = delayQueue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
return msg;
}
/**
* 消息入队列
* @param message
*/
void enqueue(Message message){
enqueueDelay(message,0);
}
/**
* 消息入队列
* @param message
* @param delayTime 延迟时间
*/
void enqueueDelay(Message message, long delayTime){
if(message == null){
throw new RuntimeException("you cannot enqueue a null message!");
}
long now = now();
message.when = delayTime + now ;
delayQueue.offer(message);
}
private long now (){
return System.currentTimeMillis();
}
}
这个类包含了一个私有变量DelayQueue,并提供了入队列(enQueue)、延时入队列(enQueueDelay)和消息出队列(next)。delayTime设置为0,也就是enqueue方法。next方法是模仿了android源码,阻塞取,由于DelayQueue 是按照时间由小到大输出消息,取不到时,它会阻塞等待下一个满足时间条件的消息就绪,所以不需要再有额外的判断。
另外,MessageQueue不应该被使用者直接操作,所以都应该是包访问限制的(方法和变量都不加修饰符)
Message.java
由于使用DelayQueue,它里面的item要实现Delayed接口,Delayed接口是继承了Comparable的,所以Nessage类要实现getDelay 和 compareTo,这两个方法是DelayQueue能够按我们设想的以时间由小到大顺序输出的重要依据。另外,Message的实现没有再使用单链表。
/**
*
* @author yueshaojun988
* @date 2017/10/24
*/
public class Message implements Delayed{
public int what;
public Object obj;
long when;
Handler target;
Runnable callback;
private AtomicLong sequence = new AtomicLong(0);
private long sequenceNumber ;
/**
* 生成Message的方法,绑定handler
* @param target
* @return
*/
public static Message obtain(Handler target){
return obtain(target,null);
}
/**
* 生成Message的方法,绑定handler和Runnable
* @param target
* @param callback
* @return
*/
public static Message obtain(Handler target ,Runnable callback){
Message msg = new Message();
msg.target = target;
msg.callback = callback;
return msg;
}
public Message(){
sequenceNumber = sequence.getAndIncrement();
when = now();
}
/**
* 发送到指定的Handler
*/
public void sendToTarget(){
target.sendMessageDelay(this,0);
}
/**
* 延时发送到指定的handler
* @param delay
*/
public void sendToTargetDelay(long delay){
checkAccess();
when = System.currentTimeMillis() + delay;
target.sendMessageDelay(this,delay);
}
private void checkAccess(){
if(target == null){
throw new RuntimeException("target must not be null !");
}
}
@Override
public long getDelay(TimeUnit timeUnit) {
return timeUnit.convert(when - now(), TimeUnit.MILLISECONDS);
}
@Override
public int compareTo(Delayed other) {
if(this == other){
return 0;
}
if(other instanceof Message){
Message o = (Message) other;
long diff = when - o.when;
if(diff<0){
return -1;
}
if(diff>0){
return 1;
}
if(sequenceNumber < o.sequenceNumber){
return -1;
}
}
long d = (getDelay(TimeUnit.MILLISECONDS) - other.getDelay(TimeUnit.MILLISECONDS));
return (d == 0) ? 0 : ((d < 0) ? -1 : 1);
}
private long now(){
return System.currentTimeMillis();
}
}
注意,getDelay需要返回具体的延时时间,这个会作为阻塞超时时间(详细请看源码)。compareTo中,如果是同一个消息返回0,并按照message处理的预设时间when排序,如果when相同,则按照sequenceNumber的大小排,小的那个消息是排在前面优先输出的。
/**
*
* @author yueshaojun988
* @date 2017/10/24
*/
public final class Looper {
MessageQueue messageQueue;
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper;
private Thread mThread;
/**
* 绑定Looper和线程、MessageQueue,不能直接new Looper();
* 需要在使用Handler之前调用,否则会抛出异常。
*/
public static void prepare(){
if(sThreadLocal.get()!=null){
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}
private Looper(){
mThread = Thread.currentThread();
messageQueue = new MessageQueue();
}
/**
* 类似于prepare(),但是这个方法是用来绑定主线程的。
*/
public static void prepareMainLooper() {
prepare();
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = sThreadLocal.get();
}
}
/**
* 获取主线程的looper
* @return
*/
public static Looper mainLooper(){
return sMainLooper;
}
/**
* 开始循环取消息,必须要在prepare之后
*/
public static void loop(){
for(;;) {
checkAccess();
Message message = myLooper().messageQueue.next();
if (message == null) {
return;
}
System.out.println("looping message " + message.obj);
message.target.dispatchMessage(message);
}
}
/**
* 获取当前线程的looper
* @return
*/
public static Looper myLooper(){
return sThreadLocal.get();
}
private static void checkAccess() {
if(sThreadLocal.get()==null){
throw new RuntimeException("no looper!");
}
}
}
Looper类基本上是仿照源码Looper写的。不多说。
Handler.java
/**
*
* @author yueshaojun988
* @date 2017/10/24
*/
public class Handler {
private Looper mLooper;
protected void dispatchMessage(Message msg){
if(msg.callback!=null){
msg.callback.run();
}
}
public Handler(Looper looper){
mLooper = looper;
}
public Handler(){
mLooper = Looper.myLooper();
}
/**
* post 一个方法
* @param runnable
*/
public void post(Runnable runnable){
checkAccess();
Message msg = Message.obtain(this,runnable);
mLooper.messageQueue.enqueue(msg);
}
/**
* 延迟 DelayTime post 一个方法
* @param runnable
* @param delayTime
*/
public void postDelay(Runnable runnable,long delayTime){
checkAccess();
Message msg = Message.obtain(this,runnable);
msg.when = (now()+delayTime);
mLooper.messageQueue.enqueueDelay(msg,delayTime);
}
/**
* 消息延迟入队列
* @param message
* @param delayTime
*/
public void sendMessageDelay(Message message,long delayTime){
checkAccess();
mLooper.messageQueue.enqueueDelay(message,delayTime);
}
private void checkAccess(){
if(mLooper == null){
throw new RuntimeException("you must call Looper.prepare() first!");
}
}
private long now(){
return System.currentTimeMillis();
}
}
按照源码Handler 的功能,提供了入队列的方法。当然,我没有完全按照源码的方法来,省去了很多无关的代码(哈哈哈哈,少写很多代码)。
测试
最后来个测试类:
/**
*
* @author yueshaojun988
* @date 2017/10/25
*/
public class Test {
public static void main(String[] args){
long id = Thread.currentThread().getId();
System.out.println("current thread id = " + id);
ThreadUtil.bindMainThread(Thread.currentThread());
Looper.prepareMainLooper();
final Handler handler = new Handler(){
@Override
protected void dispatchMessage(Message msg) {
System.out.println("receive msg " + msg.obj);
super.dispatchMessage(msg);
}
};
Message message1 = Message.obtain(handler);
message1.obj = "message1";
Message message2 = Message.obtain(handler);
message2.obj = "message2";
Message message3 = Message.obtain(handler);
message3.obj = "message3";
message1.sendToTarget();
message2.sendToTargetDelay(5000);
message3.sendToTarget();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("before post thread id = " + Thread.currentThread().getId());
handler.postDelay(new Runnable() {
@Override
public void run() {
System.out.println("after post thread id = " + Thread.currentThread().getId());
}
}, 2000);
}
}).start();
Looper.loop();
}
}
输出:
current thread id = 1
before post thread id = 10
looping message message1
receive msg message1
looping message message3
receive msg message3
looping message null
receive msg null
after post thread id = 1
looping message message2
receive msg message2
可以看到,message按照时间先后输出,message2的延时时间最久,最后输出,并且线程ID由1-》10-》1完成了切换。至于是不是按照5s和2s,不试着跑一跑怎么知道呢?哈哈,我先给自己一波666~~