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();
        }
    }
}

发表评论

邮箱地址不会被公开。 必填项已用*标注