面试官让我手写一个Handle | 七日打卡在冬日一个阳光明媚的下午,我骑着我的心爱的小黄车,去面试。面试前多次想,接下
前言
在冬日一个阳光明媚的下午,我骑着我的心爱的小黄车,去面试。面试前多次想,接下的面试官是什么样子的呢。
到了面试地点公司,在前台简单填了一份个人信息,信息里面要填自己身份证号码,父母姓名和电话号码,我想这等敏感且完善的信息, 要是泄漏出去,我想我父母大概率被骗,所以我一般都不填。(这里我也不建议大家填) 我在面试接待室,没过多久,面试官就过来了,手里拿着笔试题。面试官把笔试题递了给我,好家伙,我一看,模糊记得里面有一道二分查找算法题,一个操作系统中断机制的理解,还有就是让我模仿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");
}
}
}
完整代码地址
总结
总结就是多看多写。
本人知识有限,如有描述错误之处,愿虎正。 故事是虚构,如有雷同纯粹巧合。
你看这个像不像你欠我的赞。
谢谢大家。你的赞就像冬日暖阳,温暖心窝。
转载自:https://juejin.cn/post/6916415042019835918