一文带你深入理解Android-Handler机制(深入至native层)

  1. epfd 表示epoll句柄
  2. op 表示fd操作类型,有如下3种
  • EPOLL_CTL_ADD 注册新的fd到epfd中
  • EPOLL_CTL_MOD 修改已注册的fd的监听事件
  • EPOLL_CTL_DEL 从epfd中删除一个fd
  1. fd 是要监听的描述符
  2. event 表示要监听的事件,事件可以使如下如下几种宏的集合
  • EPOLLIN :表示对应的文件描述符可以读

  • EPOLLOUT:表示对应的文件描述符可以写

  • EPOLLERR:表示对应的文件描述符发生错误;

  • epoll_wait

  1. epfd 表示epoll句柄
  2. events 表示从内核得到的就绪事件集合,将该数组传入wait,wait返回时,这个数组就塞满了就绪事件
  3. 告诉内核events的大小,必须小于等于epoll_create时的maxevents
  4. timeout 表示等待的超时时间,-1则一直阻塞,直到有数据返回

我个人理解,wait返回只返回了就绪的文件描述符数量,而非文件描述符,也就是说我还是要遍历寻找,例如遍历events 看看都有哪些就绪事件,是有读的?还是写的?然后接着去真正调用IO,这个路子也和Java NIO的使用方式是相同的,即:阻塞返回了,说明有就绪事件,然后遍历events,找到自己关注的事件处理。

handler在native层使用到了epoll函数,我们在讲解native层时会用到上述知识。

handler工作原理

概述

其实这部分内容,但凡看过几篇相关文档的人都能背出来:以下描述基于对handler工作方式有一定理解:Lopper不断轮询MessageQueue中的消息,有消息就拿出来分给消息的target,也就是分给handler,handler调用dispatchMessage()处理;也是通过handler调用sendMessage()塞消息给messageQueue,说到这里,可能会有个疑问:为什么要通过Handler塞消息给MessageQueue(),同时handler重写一个handleMessage()方法,在这个方法中处理Looper轮询到的消息,折腾这一圈是干嘛呢?其实就是【基于消息驱动】:

  • 在一个线程中,可以创建多个handler,都往跑在这个线程的Looper中塞消息,Looper按照顺序去轮询到消息,进行处理。

  • 在线程A中,可以使用线程B中handler,发送消息到线程B,线程B的handler意思是其Looper是在线程B中轮询的。这个handler就像是线程B放出去的口子,谁向往线程B发消息,就通过我来塞。

个人认为这是handler的重要使命:线程间通信

延伸:线程间通信的其他方法

工作原理

架构

Tips:以上论述比较口头话,是基于对handler工作方式有一定理解来论述的,下面我们讲一下工作方式

一文带你深入理解Android-Handler机制(深入至native层)_第1张图片

从架构图中看出重要角色间的持有关系

  • Handler持有队列mQueue和Looper对象
  • Looper中持有队列mQueue
  • message中持有handler和next message
  • MessageQueue中持有当前message

流程

这里我们讲一下handler工作的流程,首先的角色是Looper,Looper的作用是不断轮询消息队列MessageQueue。Looper是和线程强关联的,Loop先在某个线程中跑起来。

我们以这样一个例子,来讲解handler代码

class LooperThread extends Thread {
public Handler mHandler;

public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
//TODO 定义消息处理
}
};
Looper.loop();
}
}

解释

  • 在LooperThread线程中创建Looper,并开启循环,我们知道线程执行完run()方法中的逻辑就结束了,但是在该例子中,开启Looper.loop()后会不断轮询,即使轮询不到消息会阻塞线程,就会将线程挂起,不会执行结束

  • 外部可以通过mHandler向该线程抛消息,那抛过来的消息最终会通过handleMessage处理,那么执行内容就跑在了LooperThread中

重点:一个线程有且只能有一个Looper

Looper

创建Looper

//门面方法创建
Looper.prepare();

//创建逻辑核心
private static void prepare(boolean quitAllowed) {
//每个线程只允许执行一次该方法,第二次执行时线程的TLS已有数据,则会抛出异常。
if (sThreadLocal.get() != null) {
throw new RuntimeException(“Only one Looper may be created per thread”);
}
//创建Looper对象,并保存到当前线程的TLS区域
sThreadLocal.set(new Looper(quitAllowed));
}

//Looper构造方法
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed); //创建MessageQueue对象. 【见4.1】
mThread = Thread.currentThread(); //记录当前线程.
}

//关键方法Loop
public static void loop() {
final Looper me = myLooper(); //获取TLS存储的Looper对象 【见2.4】
final MessageQueue queue = me.mQueue; //获取Looper对象中的消息队列

Binder.clearCallingIdentity();
//确保在权限检查时基于本地进程,而不是调用进程。
final long ide

你可能感兴趣的:(程序员,架构,移动开发,android)