diff --git a/blogs/Android/口水话/Binder.md b/blogs/Android/口水话/Binder.md index 8ce8bd9..b7f68fe 100644 --- a/blogs/Android/口水话/Binder.md +++ b/blogs/Android/口水话/Binder.md @@ -44,7 +44,7 @@ Binder 驱动就会把这个请求交给 Binder 实体对象去处理,也就 到这里,已经讲完了 Client 端如何和 Binder 驱动进行交互的了,下面就讲 Service 端是如何和 Binder 驱动进行交互的。 -Service 端首先会开启一个 Binder 线程来处理进程间通信消息,也就是通过 new Thread 然后把该线程 joinThreadPool 注册到 Binder 驱动。注册呢也就是通过 BC_ECTER_LOOPER 命令协议来做的,接下来就是在 do while 死循环中调用 getAndExecuteCommand。它里面做的就是不断从驱动读取请求,也就是 talkWithDriver,然后再处理请求 executeCommand。在 executeCommand 中,就会根据 BR_TRANSACTION 来调用 BBinder Binder 实体对象的 onTransact 函数来进行处理,然后在发送一个 BC_REPLY 把响应结构返回给 Binder 驱动。Binder 驱动在接收到 BC_REPLY 之后就会向 Service 发送一个 BR_TRANSACTION_COMPLETE 协议表示 Binder 驱动已经收到了,在此同时呢,也会向 Client 端发送一个 BR_REPLY把响应回写给 Client 端。 +Service 端首先会开启一个 Binder 线程来处理进程间通信消息,也就是通过 new Thread 然后把该线程 joinThreadPool 注册到 Binder 驱动。注册呢也就是通过 BC_ENTER_LOOPER 命令协议来做的,接下来就是在 do while 死循环中调用 getAndExecuteCommand。它里面做的就是不断从驱动读取请求,也就是 talkWithDriver,然后再处理请求 executeCommand。在 executeCommand 中,就会根据 BR_TRANSACTION 来调用 BBinder Binder 实体对象的 onTransact 函数来进行处理,然后在发送一个 BC_REPLY 把响应结构返回给 Binder 驱动。Binder 驱动在接收到 BC_REPLY 之后就会向 Service 发送一个 BR_TRANSACTION_COMPLETE 协议表示 Binder 驱动已经收到了,在此同时呢,也会向 Client 端发送一个 BR_REPLY把响应回写给 Client 端。 需要注意的是,上面的 onTransact 函数就是 Service 端 AIDL 生成的 Stub 类的 onTransact 函数,这时一次完整的 IPC 通信流程就完成了。 diff --git a/blogs/Android/口水话/Handler.md b/blogs/Android/口水话/Handler.md index 31d992c..c59347e 100644 --- a/blogs/Android/口水话/Handler.md +++ b/blogs/Android/口水话/Handler.md @@ -20,6 +20,6 @@ Handler 消息机制比较简单: 然后就是核心逻辑了,在 Looper 的 loop 方法,它是一个死循环,里面做了三件事,调用 MessageQueue 的 next 方法取消息,然后通过 Message 的 target 也就是 handler 去 dispatchMessage 分发消息,最后回收消息。MessageQueue 的 next 也是一个死循环,首先会调用 nativePollOnce 函数,如果没有消息或者处理时间未到,就会阻塞在 Native 层的 pollOnce 函数,睡眠时间就是从 Java 层传过来的。不过其核心实现是在 pollInner 中,如果监听的文件描述符没有发生 IO 读写事件,那么当前线程就会在 epoll_wait 中进入休眠。如果当前线程有新的消息需要处理,就会被唤醒,然后沿着之前的调用路径返回到 Java 层,最后就是对新消息进行处理。 -看完源码,有两点可以参考,第一是获取 Message 最好通过 Message 的 obtain 获取,不要直接 new,因为 Message.obtain 会从缓存里面去取。第二是可以使用 IdleHandler 在消息队列空闲时提前做一些操作。这个我在项目中也有用到,再点击消息中心图标时,会跳到 h5 页面,并且在 url 后面加密拼接 uid 和 phone number,这一加密操作是同步的,所以就可以通过 IdleHandler 提前做这一操作,并且返回 false,表示只做一次。 +看完源码,有两点可以参考,第一是获取 Message 最好通过 Message 的 obtain 获取,不要直接 new,因为 Message.obtain 会从缓存里面去取。第二是可以使用 IdleHandler 在消息队列空闲时提前做一些操作。这个我在项目中也有用到,在点击消息中心图标时,会跳到 h5 页面,并且在 url 后面加密拼接 uid 和 phone number,这一加密操作是同步的,所以就可以通过 IdleHandler 提前做这一操作,并且返回 false,表示只做一次。 下面在简单说下设置同步分隔栏,即 MessageQueue.postSyncBarrier()。同步分隔栏的原理其实很简单,本质上是通过创建一个 target 成员为 null 的 Message 并插入到消息队列中,这样在这个特殊的 Message 之后的消息就不会被处理了,只有当这个 Message被移除后才会继续执行之后的 Message。最经典的实现就 ViewRootImpl 调用 scheduleTraversals 方法进行视图更新时使用,它通过 Choreographer postCallback 把 Message 设置成异步消息避免被分割,把 doTraversal 封装成一个 Runnable,在真正执行时才会移除这个分隔栏。这样做的原因是,doTraversal 的操作是通过 Handler 进行处理的,然鹅这个消息队列却是主线程公用的,比如四大组件的各个生命周期的调用,显然视图绘制这个无疑是最高优先级的,所以在这之前,要确保队列中其他同步消息不会影响到它的执行。 \ No newline at end of file