likes
comments
collection
share

面试官让我手写一个Handle | 七日打卡在冬日一个阳光明媚的下午,我骑着我的心爱的小黄车,去面试。面试前多次想,接下

作者站长头像
站长
· 阅读数 46

前言

在冬日一个阳光明媚的下午,我骑着我的心爱的小黄车,去面试。面试前多次想,接下的面试官是什么样子的呢。

到了面试地点公司,在前台简单填了一份个人信息,信息里面要填自己身份证号码,父母姓名和电话号码,我想这等敏感且完善的信息, 要是泄漏出去,我想我父母大概率被骗,所以我一般都不填。(这里我也不建议大家填) 我在面试接待室,没过多久,面试官就过来了,手里拿着笔试题。面试官把笔试题递了给我,好家伙,我一看,模糊记得里面有一道二分查找算法题,一个操作系统中断机制的理解,还有就是让我模仿Android Handle,去写一个简化版的Handle,其他记不清了。

我用商量的语气问:这个能不能不手写?

面试官有点不屑:你不想写是吧!

接着他起身,没等我反应过来,他就把面试室的灯关了,我瞬间懵逼了,一下子没反应过来。他还顺手把我的水拿走了,原来不写面试题,水都不配喝。

我像一个在马路上,被行人抢走糖的孩子,在冷风中不知所措.....

好吧让我们开始吧!

这里我就不说Handle源码分析了,毕竟源码分析网上一搜一大堆。我们直接开始吧!

你将学到如下知识

1,对Handle流程有一定了解。重点是这个\color{blue}{1,对Handle流程有一定了解。重点是这个}1,Handle流程有一定了解。重点是这个

2,了解ThreadLocal,它用于线程间数据隔离。这个下面只会简单说一下。

3,顺带了解线程阻塞为何不消耗资源。

前期准备

类的大概介紹,有这个几个类就足够了。

Handler:控制消息的发送和消息的回调。
Looper:主要是循环去取消息.
Message:消息类型定义.
MessageQueue:消息队列,取出消息和消息入队
LooperTestMain:测试入口,模仿 android.app.ActivityThread.

下面是类代码的介绍,我相信你一定早已对handle很了解了。

Handler

控制消息的发送和消息的回调,好像这里没啥好说的,手动微笑。

sendMessage 发送消息

handleMessage 处理消息

有没有注意到,Handler是消息处理者,也是消息发送者。

部分代码如下:

public class Handler {

    public void sendMessage(int what, String msg) {
        queue.enqueueMessage(new Message(what, msg, this));
    }
    public void handleMessage(Message message) {
        if(callback!=null){
            callback.handleMessage(message);
        }
    }
   
}


Looper-循环去取消息

主要是循环去取消息,还有注意一下这里的ThreadLocal,ThreadLocal的作用是每一个线程创建一个变量副本,里面有一个ThreadLocalMap,key 以自身ThreadLocal为值,线程调用get(),返回当前线程中的成员变量。

loop()函数中的queue.next(),这里如果没有消息就会一直阻塞,有消息就取出来。

阻塞会消耗系统资源?

阻塞会释放系统资源的,这里要涉及到一些操作系统知识,等我先去补习几天操作系统的知识先。

进程状态四个

执行 就绪,阻塞(或叫等待),死亡。不同的系统可能不同,一般都大同小异。

阻塞状态:进程因发生某种事件(例如I/O请求、申请缓冲空间等)而暂停执行时的状态,亦即进程的执行受到阻塞,

故称这种状态为阻塞状态,有时也称“等待”状态或“睡眠”状态。通常将处于阻塞状态的进程排成一个队列, 称为阻塞队列。在有的系统中,按阻塞的原因不同而将处于阻塞状态的进程排成多个队列。

这里用单核操作系统说明:

把进程比作人

比如你6个人用一个洗手间,为啥是一个洗手间呢,因为多进程在微观上面是交替执行的。所以6个人轮流使用一个洗手间,这就叫多线程发行\color{blue}{多线程发行}多线程发行, 由于系统多进程是时间切片,即每个人只能用一会,比如这里是30秒,就得换下一个了。这个时候拉到一半怎么办?这个时候就需要你夹断了 (这就是系统中断机制\color{blue}{系统中断机制}系统中断机制,进程管理块PCB,会保存这个人拉到那里了,等待下次再上洗手间)。然后接着下一个,以此类推。

其实这样很浪费资源的, 如果其中三个人其实是不急的或者根本没有需求,进去也是,浪费资源。所以这个时候就出现优化,没有需求,先在外面等着。(这就是阻塞状态\color{blue}{阻塞状态}阻塞状态了), 把这个三个人放到阻塞队列,这时洗手间压力减轻一半,可以愉快上个洗手间了。等他们有需求再从新排队,在接着.....就是阻塞状态变成就绪状态,排队,等待系统分配时间片-执行状态。)

加入线程概念,现代系统中一个进程可以有多个线程,多核计算机(多个洗手间~_~)是可以同时执行多个线程的,这样大大增加效率。(cup直接打满呀!) 切换进程是开销大的,而切换线程开销小,好比,你把三个人放到厕所里面,即马桶边上,这个过程少了开关门的步骤,是不是更加有效率了,手动微笑。 这里过了,不说了。留在下一篇文章说吧!

public class Looper {
    private static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<>();
    public MessageQueue queue ;
    private Looper() {
        queue = new MessageQueue();
    }
    public static void initLooper() {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper());
    }
    public static Looper getLooper() {
        return sThreadLocal.get();
    }
    public static void loop() {
        Looper me = getLooper();
        //参考Looper中取消息的写法
        for (; ; ) {
            //没有消息,会阻塞。
            Message message = me.queue.next();
            if (message.target != null) {
                message.target.handleMessage(message);
            }
        }
    }
}

Message 消息实体类

消息实体类

public class Message {
    public Handler target;
    public int what;
    public String message;
    public Message(int what, String massage,Handler target) {
        this.what = what;
        this.message = massage;
        this.target=target;
    }

}

MessageQueue 消息队列

一个消息阻塞队列,用现成的BlockingQueue。

两个函数:取出消息和消息入队。

public class MessageQueue {
    //阻塞队列
    private final BlockingQueue<Message> queue = new ArrayBlockingQueue<Message>(10);
    //取消息
    public Message next() {
        try {
            return queue.take();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return null;
    }
    //消息入队
    public void enqueueMessage(Message msg) {
        try {
            queue.put(msg);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

LooperTestMain 测试入口

目前我们的代码已经完毕了,现在我们要开始测试了。我们建立测试的main方法。 步骤

1,Looper.initLooper()

2,新建一个Handle,并添加一个消息拦截器,如果拦截器return true会拦截消息,handleMessage为接收不到消息。

3,通过子线程发消息!sendMessage

4,Looper.loop();阻塞取消息, 不能让线程退出。如果线程退出,就无法处理后面的事件了。App ActivityThread同理。

public class LooperTestMain {

    public static void main(String[] args) {
        Looper.initLooper();
        Handler handler1 = new Handler(msg -> {
            //如果这里为true会拦截消息
            return false;
        }) {
            @Override
            public void handleMessage(Message message) {
                super.handleMessage(message);
                System.out.println("received a massage from:" + Looper.getLooper());
                System.out.println("this message info:" + message.message);
                System.out.println("Main Thread id:" + Thread.currentThread().getId());
            }
        };

        //通过子线程发消息!
        new Thread() {
            @Override
            public void run() {
                super.run();
                int i = 0;
                    while (true) {
                        try {
                            Thread.sleep(3000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println("Sub Thread id:" + Thread.currentThread().getId());

                        handler1.sendMessage(1, "send a massage:" + i++);
                    }
            }
        }.start();

        //阻塞取消息, 如果线程退出,就无法处理后面的事件了。App ActivityThread同理
        Looper.loop();

        //如果这里被执行了,循环取消息发生异常。android.app.ActivityThread.java中 main 方法中最后一行也是这样写的。
        throw new RuntimeException("Main thread loop unexpectedly exited");

    }

}
}
完整代码地址

代码地址

总结

总结就是多看多写。

本人知识有限,如有描述错误之处,愿虎正。 故事是虚构,如有雷同纯粹巧合。

你看这个面试官让我手写一个Handle | 七日打卡在冬日一个阳光明媚的下午,我骑着我的心爱的小黄车,去面试。面试前多次想,接下像不像你欠我的赞。 谢谢大家。你的赞就像冬日暖阳,温暖心窝。

转载自:https://juejin.cn/post/6916415042019835918
评论
请登录