handler与命令模式

292 阅读2分钟

用命令模式,手动模拟一下handler

import java.util.LinkedList;
import java.util.List;

// 接收者,真正的命令执行对象
class Handler {

    public Handler(){}

    void handleMessage(Message msg){
       // TODO
    }

    void sendMsg(Message msg,Looper looper){
        looper.enQueue(msg);
    }
}

class Message {
        
    Handler target;

    public Message(){}
}

class Looper {

    Message msg;
    List<Message> MsgQueue = new LinkedList<>();

    public Looper(){
        // loop();
    }

    public void enQueue(Message msg){
        MsgQueue.add(msg);
        System.out.println("add success");
    }

    void loop(Handler handler){
        for(;;){
            if(!MsgQueue.isEmpty()){
                handler.handleMessage(MsgQueue.get(0));
                break;
            }
        }
    }
}

class Activity {
    public static void main(String[] args) {

        Looper looper =new Looper();
        
        looper.loop(handler);
        
        Message msg = new Message();

        Handler handler = new Handler(){
            @Override
            void handleMessage(Message msg) {
                System.out.println("handle Message!");
            };
        };

        handler.sendMsg(msg,looper);

    }
}

发现好像并不奏效,一直在那循环,就是不输出,好奇怪。好像又不是那么奇怪,死循环本身就把线程阻塞了,这样好像也正常。

我们试着把looper.loop(handler)放在最后一行,居然生效了。

那Android里为什么没能卡死呢,确实是有这个死循环的啊,还在主线程,怎么做的呢。怀着好奇的心,又重新回去读源码。发现messageQueu#next()中很显眼的躺着一句:nativePollOnce(ptr, nextPollTimeoutMillis)

for (;;) {
    if (nextPollTimeoutMillis != 0) {
        Binder.flushPendingCommands();
    }

    nativePollOnce(ptr, nextPollTimeoutMillis);

    synchronized (this) {
        // Try to retrieve the next message.  Return if found.
        final long now = SystemClock.uptimeMillis();
        Message prevMsg = null;
        Message msg = mMess

这里是个native方法,好像是这个问题的关键,但是又没办法继续看下去了。

Google一番才恍然大悟:或许可以把这个死循环看成socket,一直连接等待消息。其实实际上比socket要复杂一点,借助了Linux的epoll机制。简单理解一下,本质上就是多个socket发起连接,用一个程序都放在socket,程序响应之后,去查找是哪个socket这样的。

参考:如果这篇文章说不清epoll的本质,那就过来掐死我吧!)

其中还提到了select,最初发明的,程序响应后,再去一个一个遍历,看看是哪个socket响应了。而epoll高明之处就在于在这个程序中用地址对应socket,就不用挨个遍历了。