来说说Looper的副业



epoll_wait返回后,下面处理事件,总共eventCount个事件,每个事件都有一个fd,
fd == mWakeEventFd: 表示有新消息,另外的一个线程往这个线程的queue里发送消息时会往eventFd里写东西
fd != mWaveEventFd: Looper的副业, 别的FD有事件
别的FD是上面时候添加到epoll的呢?

Looper的 addFd允许添加别的fd,Looper的epoll统一监听他们的事件,这就是Looper的副业
参数 fd: Fd本身
参数 events: 监听这个fd的什么事件
参数 callback: 事件触发了的话回调这个callback
addOnFileDescriptorEventListener: java层的方法,MessageQueue里
framework里有哪些地方用到了这个副业

这个fd是SurfaceFlinger创建的,将读的fd跨进程传到应用,在Choreographer所在的线程里将fd添加到Looper的epoll里,SurfaceFlinger通过这个Fd通知(往fd里写东西)应用VSync信号来 了

系统服务通知应用进程的两种方案:
epoll_wait+fd方案:
应用端通过epoll_wait,系统服务发消息通知,消息发出去后,应用端什么时候处理,在什么线程处理完全有应用端自己决定,这样就很灵活,整个过程对两方来说都是异步的,
binder调用:
openConnection连到系统服务并将binder对象(bpBinder)传到SurfaceFlinger, surfaceFlinger有什么需要通知应用端就通过bpBinder发起binder调用,在应用端的,整个过程的处理都是在binder线程里,如果这个binder调用不是oneway的话,会阻塞系统服务,是oneway的话系统服务就不会阻塞,对系统服务来说是异步的过程,但是对应用端来说就是一个同步的过程,因为在应用端,一个binder请求完成后binder驱动才会将下一个binder请求给到应用端。
结论:
对于简单的通知,epoll_wait+fd方案比较好,对于跨进程函数调用还是binder调用比较好。
demo工程,TestPipeFd,测试Looper的副业

MainActivity和MyService运行在不同进程
MainActivity bindService -> MyService onServiceConnected 将binder返回给MainActivity -> MainActiviy 创建一个 管道, 这个管道有一对描述符(读写), MainActivity通过 binder调用 pushlishReadFd将 读描述符 传到 MyService进程 -> MyService 通过epoll_wait 监听这个 读 FD -> MainActivity往这个FD里写了个消息 -> MyService epoll_wait 就监听到了 读Fd事件,将消息读出来 通过 binder调用 sendReply 返回给MainActivity -> MainActivity将其显示出来
Demo代码部分

mFds[0]: 读描述符, mFds[1]: 写描述符, 往写描述符里写一个消息,读描述符就能收到事件,并将消息读出来

这里通过bindService回调binder调用publish将mFds[0]发送到MyService,而不是startService通过Intent 将 mFds[0]传给MyService,是因为Intent不能传递Fd

点击MainActivity的一个按钮触发writePipe,


MyService 端的代码
queue.addOnFileDescriptorEventListener(fd, EVENT_INPUT, this): 监听fd的可读事件,当这个描述符有可读事件时,就会回调
this(onFileDescriptorEvents方法, MessageQueue类里)

mCallback.onRecv: binder调用将消息返回给MainActivity
总结:
1. Looper里可以监听其他描述符
Looper不仅可以监听MessageQueue里的描述符,还可以监听应用端传给他的描述符,这个描述符可以是文件,管道,Socket, 监听方法: MessageQueue.addOnFileDescriptorEventListener
2. 创建管道,跨进程传数据,用looper监听描述符事件
可以在应用层创建管道, ParcelFileDescriptor.createPipe 返回 描述符数组fd[0]:读,fd[1]:写, 将其中一个描述符传到另外一个进程(通过binder调用),在另外一个进程监听这个描述符的事件,在一个进程往fd写消息,另外一个进程就能读到这个消息
网友评论