上一节完成了焦点窗口关联,现在可以分析如何将输入事件继续从 InputDispatcher 继续分发。InputChannel 的 sendMessage 将消息发送出去。实际是调用 socket 的 send 接口来发送消息的。具体一点其实使用的是 socketpair。所以我们先来学习 Linux 如何使用 socketpair,然后“破解” InputChannel 通信,最后再去分析输入事件窗口分发。
一、socketpair 使用
用于创建一对无名的、相互连接的套接字。
#include <sys/types.h>
#include <sys/socket.h>
int socketpair(int domain, int type, int protocol, int sv[2]);
参数
domain: 协议家族
type: 套接字类型
protocol: 协议类型
sv: 返回套接字对
返回值
成功返回 0,失败返回 -1
UNIX 域套接字用于在同一台计算机上运行的进程之间的通信。虽然因特网域套接字可用于 同一目的,但 UNIX 域套接字的效率更高。UNIX 域套接字仅仅复制数据,它们并不执行协议处理,不需要添加或删除网络报头,无需计算校验和,不要产生顺序号,无需发送确认报文。
UNIX 域套接字提供流和数据报两种接口。UNIX 域数据报服务是可靠的,既不会丢失报文也不会传递出错。UNIX 域套接字就像是套接字和管道的混合。可以使用它们面向网络的域套接字接口或者使用 socketpair 函数來创建一对无命名的、相互连接的 UNIX 域套接字。
虽然接口足够通用,允许 socketpair 用于其他域,但一般来说操作系统仅对 UNIX 域提供支持。
下面是一个例程,父进程和子进程相互发送消息。父进程调用 sleep 休息 1 秒是为了子进程有机会执行。
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <stdio.h>
int main () {
int fd[2];
int r = socketpair(AF_UNIX, SOCK_STREAM, 0, fd);
if (r < 0) {
perror("socketpair()");
exit(1);
}
if (fork()) {
/* Parent process: echo client */
int val = 0;
close(fd[1]);
while (1) {
sleep(1);
++val;
printf("Parent process Sending data: %d\n", val);
write(fd[0], &val, sizeof(val));
read(fd[0], &val, sizeof(val));
printf("Parent process Data received: %d\n", val);
}
} else {
/* Child process: echo server */
int val;
close(fd[0]);
while (1) {
read(fd[1], &val, sizeof(val));
printf("Child process Data received: %d\n", val);
++val;
printf("Child process Sending data: %d\n", val);
write(fd[1], &val, sizeof(val));
}
}
}
运行结果
Parent process Sending data: 1
Child process Data received: 1
Child process Sending data: 2
Parent process Data received: 2
Parent process Sending data: 3
Child process Data received: 3
Child process Sending data: 4
Parent process Data received: 4
Parent process Sending data: 5
Child process Data received: 5
Child process Sending data: 6
Parent process Data received: 6
Parent process Sending data: 7
Child process Data received: 7
Child process Sending data: 8
Parent process Data received: 8
Parent process Sending data: 9
Child process Data received: 9
Child process Sending data: 10
Parent process Data received: 10
Parent process Sending data: 11
Child process Data received: 11
Child process Sending data: 12
Parent process Data received: 12
Parent process Sending data: 13
Child process Data received: 13
Child process Sending data: 14
Parent process Data received: 14
Parent process Sending data: 15
Child process Data received: 15
Child process Sending data: 16
Parent process Data received: 16
......
二、InputChannel 通信
回顾《Android 源码 输入系统之窗口关联》一节,InputChannel 通信得从 ViewRootImpl 类 setView 继续分析。
frameworks/base/core/java/android/view/ViewRootImpl.java
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
......
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view;
......
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
// 创建客户端 InputChannel 对象
mInputChannel = new InputChannel();
}
try {
......
// Binder 机制调用 Session 对象 addToDisplay 方法,注意入参包括 InputChannel 对象
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
} catch (RemoteException e) {
......
} finally {
......
}
......
if (mInputChannel != null) {
if (mInputQueueCallback != null) {
// 创建 InputQueue 对象
mInputQueue = new InputQueue();
mInputQueueCallback.onInputQueueCreated(mInputQueue);
}
// 接收输入事件,创建 WindowInputEventReceiver 对象
mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
Looper.myLooper());
}
......
}
}
}
......
}
输入通道(InputChannel)指定用于将输入事件发送到另一个进程中的窗口的文件描述符。它是可打包的,因此可以将其发送到接收事件的进程。每次只应该有一个线程从 InputChannel 中读取数据。
frameworks/base/core/java/android/view/InputChannel.java
public final class InputChannel implements Parcelable {
......
// 创建一个未初始化的输入通道。
// 它可以通过从一个 Parcel 读取数据或将另一个输入通道的状态传输到这个输入通道来初始化。
public InputChannel() {
}
......
}
Session 类 addToDisplay 方法实际工作由 WindowManagerService 类 addWindow 方法完成。
frameworks/base/services/core/java/com/android/server/wm/Session.java
final class Session extends IWindowSession.Stub
implements IBinder.DeathRecipient {
final WindowManagerService mService;
......
@Override
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
Rect outOutsets, InputChannel outInputChannel) {
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
outContentInsets, outStableInsets, outOutsets, outInputChannel);
}
......
}
- 创建 InputChannel 对
- 将服务端 InputChannel 赋给服务端的 WindowState
- 将客户端 InputChannel 传递到 outInputChannel, 最终返回客户端应用进程
- 将服务端 InputChannel 注册到 InputDispatcher
frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
public class WindowManagerService extends IWindowManager.Stub
implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
......
final InputMonitor mInputMonitor = new InputMonitor(this);
......
public int addWindow(Session session, IWindow client, int seq,
WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
InputChannel outInputChannel) {
......
synchronized(mWindowMap) {
......
// WindowState 代表窗口管理器中的窗口。
WindowState win = new WindowState(this, session, client, token,
attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent);
......
if (outInputChannel != null && (attrs.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
String name = win.makeInputChannelName();
// 创建 InputChannel 对
InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
// 将服务端 InputChannel 赋给服务端的 WindowState
win.setInputChannel(inputChannels[0]);
// 将客户端 InputChannel 传递到 outInputChannel, 最终返回客户端应用进程
inputChannels[1].transferTo(outInputChannel);
// 将服务端 InputChannel 注册到 InputDispatcher
mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);
}
......
}
......
}
......
}
创建 InputChannel 对,实际是使用 socketpair 来完成的。openInputChannelPair 简单做了 name 判空后,实际工作由 nativeOpenInputChannelPair 方法完成。
frameworks/base/core/java/android/view/InputChannel.java
public final class InputChannel implements Parcelable {
......
private static native InputChannel[] nativeOpenInputChannelPair(String name);
......
public static InputChannel[] openInputChannelPair(String name) {
if (name == null) {
throw new IllegalArgumentException("name must not be null");
}
if (DEBUG) {
Slog.d(TAG, "Opening input channel pair '" + name + "'");
}
return nativeOpenInputChannelPair(name);
}
......
}
不难看出 nativeOpenInputChannelPair 只是一个 jni 接口,实际工作由 jni native 层对应函数(android_view_InputChannel_nativeOpenInputChannelPair)完成。
- name 转化(从 java String 对象转化为 C++ String8)
- 调用 Native InputChannel 对象 openInputChannelPair 方法
- 构建 NativeInputChannel 对象,并将 NativeInputChannel 对象转化为 Java InputChannel 对象返回
注意 Native InputChannel 对象和 NativeInputChannel 对象,前者为 C++ 类 InputChannel,后者为 C++ 类 NativeInputChannel。
frameworks/base/core/jni/android_view_InputChannel.cpp
static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,
jclass clazz, jstring nameObj) {
const char* nameChars = env->GetStringUTFChars(nameObj, NULL);
String8 name(nameChars);
env->ReleaseStringUTFChars(nameObj, nameChars);
sp<InputChannel> serverChannel;
sp<InputChannel> clientChannel;
status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
if (result) {
String8 message;
message.appendFormat("Could not open input channel pair. status=%d", result);
jniThrowRuntimeException(env, message.string());
return NULL;
}
jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL);
if (env->ExceptionCheck()) {
return NULL;
}
jobject serverChannelObj = android_view_InputChannel_createInputChannel(env,
new NativeInputChannel(serverChannel));
if (env->ExceptionCheck()) {
return NULL;
}
jobject clientChannelObj = android_view_InputChannel_createInputChannel(env,
new NativeInputChannel(clientChannel));
if (env->ExceptionCheck()) {
return NULL;
}
env->SetObjectArrayElement(channelPair, 0, serverChannelObj);
env->SetObjectArrayElement(channelPair, 1, clientChannelObj);
return channelPair;
}
- 调用 socketpair 创建一对无名的、相互连接的套接字
- setsockopt() 函数,用于任意类型、任意状态套接口的设置选项值
SO_RCVBUF int 为接收确定缓冲区大小。
SO_RCVBUF int 为接收确定缓冲区大小。
- 分别创建服务端和客户端 Native InputChannel 对象
frameworks/native/libs/input/InputTransport.cpp
status_t InputChannel::openInputChannelPair(const String8& name,
sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
int sockets[2];
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
status_t result = -errno;
ALOGE("channel '%s' ~ Could not create socket pair. errno=%d",
name.string(), errno);
outServerChannel.clear();
outClientChannel.clear();
return result;
}
int bufferSize = SOCKET_BUFFER_SIZE;
setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
String8 serverChannelName = name;
serverChannelName.append(" (server)");
outServerChannel = new InputChannel(serverChannelName, sockets[0]);
String8 clientChannelName = name;
clientChannelName.append(" (client)");
outClientChannel = new InputChannel(clientChannelName, sockets[1]);
return OK;
}
NativeInputChannel 对象将传递来的 InputChannel 指针保存在自己的成员变量 mInputChannel 中。
frameworks/base/core/jni/android_view_InputChannel.cpp
NativeInputChannel::NativeInputChannel(const sp<InputChannel>& inputChannel) :
mInputChannel(inputChannel), mDisposeCallback(NULL) {
}
再来分析 android_view_InputChannel_createInputChannel 函数。
- 调用 jni 方法 NewObject 创建一个 Java InputChannel 对象
- 调用 android_view_InputChannel_setNativeInputChannel 将 nativeInputChannel 对象指针强转为 jlong,然后将其设置到 Java InputChannel 类 mPtr 成员上。
frameworks/base/core/jni/android_view_InputChannel.cpp
static jobject android_view_InputChannel_createInputChannel(JNIEnv* env,
NativeInputChannel* nativeInputChannel) {
jobject inputChannelObj = env->NewObject(gInputChannelClassInfo.clazz,
gInputChannelClassInfo.ctor);
if (inputChannelObj) {
android_view_InputChannel_setNativeInputChannel(env, inputChannelObj, nativeInputChannel);
}
return inputChannelObj;
}
InputChannel 类 transferTo 函数,将 InputChannel 内部状态的所有权转移到另一个实例,并使该实例无效。这用于在 binder 调用中将输入通道作为 out 参数传递。
frameworks/base/core/java/android/view/InputChannel.java
public final class InputChannel implements Parcelable {
......
public void transferTo(InputChannel outParameter) {
if (outParameter == null) {
throw new IllegalArgumentException("outParameter must not be null");
}
nativeTransferTo(outParameter);
}
......
}
otherObj 即对应的入参,调用 android_view_InputChannel_getNativeInputChannel 方法可将 Java InputChannel 对象的 mPtr 成员变量强转为 NativeInputChannel 对象,由于传递过来的 Java InputChannel 对象是个“空壳”,所以强转以后一定为空,否则抛出异常。
接下来调用 android_view_InputChannel_getNativeInputChannel 将 obj 转化为 NativeInputChannel 对象。并将其(实际是指针)设置到 otherObj mPtr 成员变量上。
最后将 obj mPtr 成员变量设置为 0,即表示 NativeInputChannel 对象为 NULL。
frameworks/base/core/jni/android_view_InputChannel.cpp
static void android_view_InputChannel_nativeTransferTo(JNIEnv* env, jobject obj,
jobject otherObj) {
if (android_view_InputChannel_getNativeInputChannel(env, otherObj) != NULL) {
jniThrowException(env, "java/lang/IllegalStateException",
"Other object already has a native input channel.");
return;
}
NativeInputChannel* nativeInputChannel =
android_view_InputChannel_getNativeInputChannel(env, obj);
android_view_InputChannel_setNativeInputChannel(env, otherObj, nativeInputChannel);
android_view_InputChannel_setNativeInputChannel(env, obj, NULL);
}
下面是 android_view_InputChannel_getNativeInputChannel 和 android_view_InputChannel_setNativeInputChannel 函数,主要调用 jni 方法 GetLongField 和 SetLongField,实现 Native 层操作 java 对象成员变量。
frameworks/base/core/jni/android_view_InputChannel.cpp
static NativeInputChannel* android_view_InputChannel_getNativeInputChannel(JNIEnv* env,
jobject inputChannelObj) {
jlong longPtr = env->GetLongField(inputChannelObj, gInputChannelClassInfo.mPtr);
return reinterpret_cast<NativeInputChannel*>(longPtr);
}
static void android_view_InputChannel_setNativeInputChannel(JNIEnv* env, jobject inputChannelObj,
NativeInputChannel* nativeInputChannel) {
env->SetLongField(inputChannelObj, gInputChannelClassInfo.mPtr,
reinterpret_cast<jlong>(nativeInputChannel));
}
回到 WindowManagerService addWindow 方法,继续分析将服务端 InputChannel 注册到 InputDispatcher 的过程。registerInputChannel 函数实际调用了 nativeRegisterInputChannel 完成注册。
frameworks/base/services/core/java/com/android/server/input/InputManagerService.java
public class InputManagerService extends IInputManager.Stub
implements Watchdog.Monitor {
......
private static native void nativeRegisterInputChannel(long ptr, InputChannel inputChannel,
InputWindowHandle inputWindowHandle, boolean monitor);
......
public void registerInputChannel(InputChannel inputChannel,
InputWindowHandle inputWindowHandle) {
if (inputChannel == null) {
throw new IllegalArgumentException("inputChannel must not be null.");
}
nativeRegisterInputChannel(mPtr, inputChannel, inputWindowHandle, false);
}
......
}
- 将 ptr 强转为 NativeInputManager 对象
- 将 inputChannelObj jobject 转化为 Native InputChannel 对象
- 将 inputWindowHandleObj jobject 转化为 Native InputWindowHandle 对象
- 调用 NativeInputManager 类 registerInputChannel 方法完成实际注册工作
frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
static void nativeRegisterInputChannel(JNIEnv* env, jclass /* clazz */,
jlong ptr, jobject inputChannelObj, jobject inputWindowHandleObj, jboolean monitor) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
inputChannelObj);
if (inputChannel == NULL) {
throwInputChannelNotInitialized(env);
return;
}
sp<InputWindowHandle> inputWindowHandle =
android_server_InputWindowHandle_getHandle(env, inputWindowHandleObj);
status_t status = im->registerInputChannel(
env, inputChannel, inputWindowHandle, monitor);
if (status) {
String8 message;
message.appendFormat("Failed to register input channel. status=%d", status);
jniThrowRuntimeException(env, message.string());
return;
}
......
}
NativeInputManager 类 registerInputChannel 方法最终调用 InputDispatcher 类的 registerInputChannel 完成真正的注册。
frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
status_t NativeInputManager::registerInputChannel(JNIEnv* /* env */,
const sp<InputChannel>& inputChannel,
const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
return mInputManager->getDispatcher()->registerInputChannel(
inputChannel, inputWindowHandle, monitor);
}
- 检查 InputChannel 是否已经注册过
- 创建 Connection 对象,他表示客户端和服务端的一个输入数据通道
- 以 fd 为 key 将 Connection 添加到容器(mConnectionsByFd)中
- 将 fd 添加到 Looper 监听列表中。一旦对端的 Socket 写入数据,Looper 就会被唤醒,接着就会调用 handleReceiveCallback
frameworks/native/services/inputflinger/InputDispatcher.cpp
status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,
const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
......
{ // acquire lock
AutoMutex _l(mLock);
// 进入此 case 表示 inputChannel 已经注册过了
if (getConnectionIndexLocked(inputChannel) >= 0) {
ALOGW("Attempted to register already registered input channel '%s'",
inputChannel->getName().string());
return BAD_VALUE;
}
// Connection 表示客户端和服务端的一个输入数据通道
sp<Connection> connection = new Connection(inputChannel, inputWindowHandle, monitor);
// 获取 socketpair fd
int fd = inputChannel->getFd();
// 以 fd 为 key 将 Connection 添加到容器中
mConnectionsByFd.add(fd, connection);
......
// 将 fd 添加到 Looper 监听列表中,一旦对端的 socket 写入数据,Looper 就会被唤醒
// 接着就会调用 handleReceiveCallback
mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
} // release lock
// 唤醒 Looper,因为一些连接(Connection)已经改变
mLooper->wake();
return OK;
}
代码走到这里,InputDispatcher 就和 InputChannel 建立了关联。现在是时候回到 ViewRootImpl setView 方法中具体分析如何关联客户端 InputChannel。马上来分析创建 WindowInputEventReceiver 对象流程。
在 WindowInputEventReceiver 构造器中调用了 InputEventReceiver 构造器。
frameworks/base/core/java/android/view/ViewRootImpl.java
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
......
final class WindowInputEventReceiver extends InputEventReceiver {
public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
super(inputChannel, looper);
}
@Override
public void onInputEvent(InputEvent event) {
enqueueInputEvent(event, this, 0, true);
}
@Override
public void onBatchedInputEventPending() {
if (mUnbufferedInputDispatch) {
super.onBatchedInputEventPending();
} else {
scheduleConsumeBatchedInput();
}
}
@Override
public void dispose() {
unscheduleConsumeBatchedInput();
super.dispose();
}
}
......
}
InputEventReceiver 构造器中首先检查入参 inputChannel 和 looper 是否为 null,接着从 looper 中获取 MessageQueue (mMessageQueue),最后调用 nativeInit 进一步初始化。
frameworks/base/core/java/android/view/InputEventReceiver.java
public abstract class InputEventReceiver {
......
public InputEventReceiver(InputChannel inputChannel, Looper looper) {
if (inputChannel == null) {
throw new IllegalArgumentException("inputChannel must not be null");
}
if (looper == null) {
throw new IllegalArgumentException("looper must not be null");
}
mInputChannel = inputChannel;
mMessageQueue = looper.getQueue();
mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
inputChannel, mMessageQueue);
mCloseGuard.open("dispose");
}
......
}
- 转化 Java InputChannel 对象为 Native InputChannel 对象
- 转化 Java MessageQueue 对象为 Native MessageQueue 对象
- 构建 NativeInputEventReceiver 对象
- 调用 NativeInputEventReceiver 对象 initialize() 方法初始化
frameworks/base/core/jni/android_view_InputEventReceiver.cpp
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
jobject inputChannelObj, jobject messageQueueObj) {
sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
inputChannelObj);
if (inputChannel == NULL) {
jniThrowRuntimeException(env, "InputChannel is not initialized.");
return 0;
}
sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
if (messageQueue == NULL) {
jniThrowRuntimeException(env, "MessageQueue is not initialized.");
return 0;
}
sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
receiverWeak, inputChannel, messageQueue);
status_t status = receiver->initialize();
if (status) {
String8 message;
message.appendFormat("Failed to initialize input event receiver. status=%d", status);
jniThrowRuntimeException(env, message.string());
return 0;
}
receiver->incStrong(gInputEventReceiverClassInfo.clazz); // 保留对象的引用
return reinterpret_cast<jlong>(receiver.get());
}
NativeInputEventReceiver 构造函数将传递来的入参保存到对应的成员变量中。
frameworks/base/core/jni/android_view_InputEventReceiver.cpp
NativeInputEventReceiver::NativeInputEventReceiver(JNIEnv* env,
jobject receiverWeak, const sp<InputChannel>& inputChannel,
const sp<MessageQueue>& messageQueue) :
mReceiverWeakGlobal(env->NewGlobalRef(receiverWeak)),
mInputConsumer(inputChannel), mMessageQueue(messageQueue),
mBatchedInputEventPending(false), mFdEvents(0) {
if (kDebugDispatchCycle) {
ALOGD("channel '%s' ~ Initializing input event receiver.", getInputChannelName());
}
}
initialize() 函数仅仅调用了 setFdEvents() 函数,然后直接返回 OK。
frameworks/base/core/jni/android_view_InputEventReceiver.cpp
status_t NativeInputEventReceiver::initialize() {
setFdEvents(ALOOPER_EVENT_INPUT);
return OK;
}
由于 mFdEvents 初始化为 0, ALOOPER_EVENT_INPUT = 1 << 0,因此会赋值 mFdEvents = ALOOPER_EVENT_INPUT。然后取出 socket fd,就会向客户端 Looper 添加 fd 描述符,当有数据从服务端写入,就会唤醒 Looper。最终回调 NativeInputEventReceiver 的 handleEvent 方法。
frameworks/base/core/jni/android_view_InputEventReceiver.cpp
void NativeInputEventReceiver::setFdEvents(int events) {
if (mFdEvents != events) {
mFdEvents = events;
int fd = mInputConsumer.getChannel()->getFd();
if (events) {
mMessageQueue->getLooper()->addFd(fd, 0, events, this, NULL);
} else {
mMessageQueue->getLooper()->removeFd(fd);
}
}
}
老规矩时序图作为最后的总结。