Android输入事件从读取到分发一:是谁在读取输入事件
2017-10-19 11:13
543 查看
零.写在最前
第一次尝试阅读android输入系统的代码,免不了理解错误,如有错误,欢迎指正。一.提出问题
android是基于linux kernel的,linux的事件获取需要读/dev/input下的设备文件节点。对android系统而言,是谁在读这些设备文件节点?读到以后又是怎么把它发送给view的?二.猜测与验证
事件是一种看不到的东西,在android下,看不见的东西一般交给service来处理,系统service在系统启动的时候注册。android的输入事件的管理,应该是在系统启动的时候,注册成为系统的服务的。系统服务的注册在framworks/base/services/java/com/android/server/SystemServer.java中,使用addService来注册,在这个文件中搜索Input,很容易就发现有个InputManagerService类,从类名上看应该是输入事件管理服务类的意思,和我猜测的差不多。进去这个类看看:[java]
view plain
copy
print?
/*
* Wraps the C++ InputManager and provides its callbacks.
*/
public class InputManagerService extends IInputManager.Stub
implements Watchdog.Monitor {
/* * Wraps the C++ InputManager and provides its callbacks. */ public class InputManagerService extends IInputManager.Stub implements Watchdog.Monitor {看一个类先看它的注释,注释中说:包装C++的inputManager 并且提供它的回调。再看这个类是继承于IInputManager.Stub的,就大概知道它实现来远程系统调用的接口,其实所有的服务类都会继承于IInputManager.Stub,因为stub继承自binder类,而binder是android用于进程间通信的。stub类的声明如下:
[java]
view plain
copy
print?
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements android.hardware.input.IInputManager
{
/** Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements android.hardware.input.IInputManager {回过头来,既然输入系统以服务的形式进行管理,那先看看把InputManagerService注册位服务的代码,就在SystemServer.java中往下索InputManagerService,就会发现:
[java]
view plain
copy
print?
Slog.i(TAG, "Input Manager");
inputManager = new InputManagerService(context);
Slog.i(TAG, "Window Manager");
wm = WindowManagerService.main(context, inputManager,
mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
!mFirstBoot, mOnlyCore);
ServiceManager.addService(Context.WINDOW_SERVICE, wm);
ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
mActivityManagerService.setWindowManager(wm);
inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
inputManager.start();
Slog.i(TAG, "Input Manager"); inputManager = new InputManagerService(context); Slog.i(TAG, "Window Manager"); wm = WindowManagerService.main(context, inputManager, mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL, !mFirstBoot, mOnlyCore); ServiceManager.addService(Context.WINDOW_SERVICE, wm); ServiceManager.addService(Context.INPUT_SERVICE, inputManager); mActivityManagerService.setWindowManager(wm); inputManager.setWindowManagerCallbacks(wm.getInputMonitor()); inputManager.start();
这里首先创建了一个InputManagerService的实例,然后把它传给了一个main函数,这里是个疑问,不知道是干什么的,暂时不管,然后就是重点了,可以看到调用了
ServiceManager的addService方法注册一个service.这里注册的当然是InputManagerService。注册后,给inputManager设置了一些回调函数,然后,就调用了start函数。
那当然显示从InputManagerSercie的构造函数入手了,毕竟构造函数最先杯调用。start方法暂时放置,这个方法应该很重要,从名字可以猜测它是启动事件输入框架的。
三.InputManagerService的构造函数
[java]view plain
copy
print?
<span style="font-family: Arial, Helvetica, sans-serif;">
</span>
<span style="font-family: Arial, Helvetica, sans-serif;"> </span>
[java]
view plain
copy
print?
<span style="font-family: Arial, Helvetica, sans-serif;"> public InputManagerService(Context context) {</span>
<span style="font-family: Arial, Helvetica, sans-serif;"> public InputManagerService(Context context) {</span>
[java]
view plain
copy
print?
this.mContext = context;
this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());
mUseDevInputEventForAudioJack =
context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);
Slog.i(TAG, "Initializing input manager, mUseDevInputEventForAudioJack="
+ mUseDevInputEventForAudioJack);
mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
LocalServices.addService(InputManagerInternal.class, new LocalService());
}
this.mContext = context; this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper()); mUseDevInputEventForAudioJack = context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack); Slog.i(TAG, "Initializing input manager, mUseDevInputEventForAudioJack=" + mUseDevInputEventForAudioJack); mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue()); LocalServices.addService(InputManagerInternal.class, new LocalService()); }
它只有一个构造函数,看起来也不是很难。首先创建了一个Handler,其次去读mUseDevInputEventForAudioJack变量的值,这个值应该实在某个地方配置好的,暂且
不管它,然后就注册了一个handler,那看下这个handler是做什么的:
[java]
view plain
copy
print?
</pre><pre code_snippet_id="1720818" snippet_file_name="blog_20160618_11_1111284" name="code" class="java"> public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_DELIVER_INPUT_DEVICES_CHANGED:
deliverInputDevicesChanged((InputDevice[])msg.obj);
break;
case MSG_SWITCH_KEYBOARD_LAYOUT:
handleSwitchKeyboardLayout(msg.arg1, msg.arg2);
break;
case MSG_RELOAD_KEYBOARD_LAYOUTS:
reloadKeyboardLayouts();
break;
case MSG_UPDATE_KEYBOARD_LAYOUTS:
updateKeyboardLayouts();
break;
case MSG_RELOAD_DEVICE_ALIASES:
reloadDeviceAliases();
break;
}
</pre><pre code_snippet_id="1720818" snippet_file_name="blog_20160618_11_1111284" name="code" class="java"> public void handleMessage(Message msg) { switch (msg.what) { case MSG_DELIVER_INPUT_DEVICES_CHANGED: deliverInputDevicesChanged((InputDevice[])msg.obj); break; case MSG_SWITCH_KEYBOARD_LAYOUT: handleSwitchKeyboardLayout(msg.arg1, msg.arg2); break; case MSG_RELOAD_KEYBOARD_LAYOUTS: reloadKeyboardLayouts(); break; case MSG_UPDATE_KEYBOARD_LAYOUTS: updateKeyboardLayouts(); break; case MSG_RELOAD_DEVICE_ALIASES: reloadDeviceAliases(); break; }
具体看不懂,表面上看好像是处理输入时间变动之类的东西,暂且不管,先看它的nativeInit方法,这份方法应该比较重要吧,要不然下一如看什么地方呀?先做好思想准备,nativeInit这个函数应该比较重要,可是这个方法在哪里呢?应该在一个向InputManagerService中注册方法的本地.cpp文件中,在android的framwork下搜索:
find -name *InputManagerService* 发现
base/services/core/jni/com_android_server_input_InputManagerService.cpp
进去搜nativeInit果然存在:
[java]
view plain
copy
print?
static jlong nativeInit(JNIEnv* env, jclass clazz,
jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
if (messageQueue == NULL) {
jniThrowRuntimeException(env, "MessageQueue is not initialized.");
return 0;
}
NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
messageQueue->getLooper());
im->incStrong(0);
return reinterpret_cast<jlong>(im);
}
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject serviceObj, jobject contextObj, jobject messageQueueObj) { sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj); if (messageQueue == NULL) { jniThrowRuntimeException(env, "MessageQueue is not initialized."); return 0; } NativeInputManager* im = new NativeInputManager(contextObj, serviceObj, messageQueue->getLooper()); im->incStrong(0); return reinterpret_cast<jlong>(im); }
这里面创建了一个NativeInputManager类的实例,并且调用了它的incStrong方法。NativeInputManager应该是InputManager的C++部分,Android系统的代码是很规范的,从名字可以猜出个一二。那先看看NativeInputManager的构造函数吧。
三.NativeInputManager的构造函数与incStrong方法。
NativeInputManager同样定义在base/services/core/jni/com_android_server_input_InputManagerService.cpp中。[java]
view plain
copy
print?
NativeInputManager::NativeInputManager(jobject contextObj,
jobject serviceObj, const sp<Looper>& looper) :
mLooper(looper), mInteractive(true) {
JNIEnv* env = jniEnv();
mContextObj = env->NewGlobalRef(contextObj);
mServiceObj = env->NewGlobalRef(serviceObj);
{
AutoMutex _l(mLock);
mLocked.systemUiVisibility = ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE;
mLocked.pointerSpeed = 0;
mLocked.pointerGesturesEnabled = true;
mLocked.showTouches = false;
}
sp<EventHub> eventHub = new EventHub();
mInputManager = new InputManager(eventHub, this, this);
}
NativeInputManager::NativeInputManager(jobject contextObj, jobject serviceObj, const sp<Looper>& looper) : mLooper(looper), mInteractive(true) { JNIEnv* env = jniEnv(); mContextObj = env->NewGlobalRef(contextObj); mServiceObj = env->NewGlobalRef(serviceObj); { AutoMutex _l(mLock); mLocked.systemUiVisibility = ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE; mLocked.pointerSpeed = 0; mLocked.pointerGesturesEnabled = true; mLocked.showTouches = false; } sp<EventHub> eventHub = new EventHub(); mInputManager = new InputManager(eventHub, this, this); }
它的构造函数有个代码块,大概是做一些初始化的工作,然后创建了两个看着很重要的东西,eventHub和mInputManager,可以看到eventHub作为参数传给了
InputManager的构造函数,所以,这里着重看一下InputManager的构造函数。
四.InputManager的构造函数
[java]view plain
copy
print?
InputManager::InputManager(
const sp<EventHubInterface>& eventHub,
const sp<InputReaderPolicyInterface>& readerPolicy,
const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
mDispatcher = new InputDispatcher(dispatcherPolicy);
mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
initialize();
}
InputManager::InputManager( const sp<EventHubInterface>& eventHub, const sp<InputReaderPolicyInterface>& readerPolicy, const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) { mDispatcher = new InputDispatcher(dispatcherPolicy); mReader = new InputReader(eventHub, readerPolicy, mDispatcher); initialize(); }这里又创建了InputReader, InputDispatcher两个类的实例,然后调用initialize();用来初始化。代码到这里就越来越复杂了,头绪也越来越多,但是不能忘记我们的目标,也就是我们搞懂一开始提出的两个疑问,我们先想想第一个问题:“是谁在读/dev/input/下的文件节点?”,说到读,那这的InputReader是不是就是读的呢?从名字上
来看好像是的。所以,这里,我们还是要牢牢的抓住主干,先搞明白是谁在读/dev/input/下的文件节点,然后再思考其他的部分。那么,这里暂时对InputDisPatcher和initialize置之不理,着重看下InputReader到底做了什么?
五.InputReader的构造函数。
[java]view plain
copy
print?
InputReader::InputReader(const sp<EventHubInterface>& eventHub,
const sp<InputReaderPolicyInterface>& policy,
const sp<InputListenerInterface>& listener) :
mContext(this), mEventHub(eventHub), mPolicy(policy),
mGlobalMetaState(0), mGeneration(1),
mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX),
mConfigurationChangesToRefresh(0) {
mQueuedListener = new QueuedInputListener(listener);
{ // acquire lock
AutoMutex _l(mLock);
refreshConfigurationLocked(0);
updateGlobalMetaStateLocked();
} // release lock
}
InputReader::InputReader(const sp<EventHubInterface>& eventHub, const sp<InputReaderPolicyInterface>& policy, const sp<InputListenerInterface>& listener) : mContext(this), mEventHub(eventHub), mPolicy(policy), mGlobalMetaState(0), mGeneration(1), mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX), mConfigurationChangesToRefresh(0) { mQueuedListener = new QueuedInputListener(listener); { // acquire lock AutoMutex _l(mLock); refreshConfigurationLocked(0); updateGlobalMetaStateLocked(); } // release lock }这里面就调用了两个函数,首先看第一个函数:refreshConfigurationLocked(0);
[java]
view plain
copy
print?
void InputReader::refreshConfigurationLocked(uint32_t changes) {
mPolicy->getReaderConfiguration(&mConfig);
mEventHub->setExcludedDevices(mConfig.excludedDeviceNames);
if (changes) {
ALOGI("Reconfiguring input devices. changes=0x%08x", changes);
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
if (changes & InputReaderConfiguration::CHANGE_MUST_REOPEN) {
mEventHub->requestReopenDevices();
} else {
for (size_t i = 0; i < mDevices.size(); i++) {
InputDevice* device = mDevices.valueAt(i);
device->configure(now, &mConfig, changes);
}
}
}
}
void InputReader::refreshConfigurationLocked(uint32_t changes) { mPolicy->getReaderConfiguration(&mConfig); mEventHub->setExcludedDevices(mConfig.excludedDeviceNames); if (changes) { ALOGI("Reconfiguring input devices. changes=0x%08x", changes); nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); if (changes & InputReaderConfiguration::CHANGE_MUST_REOPEN) { mEventHub->requestReopenDevices(); } else { for (size_t i = 0; i < mDevices.size(); i++) { InputDevice* device = mDevices.valueAt(i); device->configure(now, &mConfig, changes); } } } }这个函数也是看的一脸懵逼,可以看到出现了InputDevice 这个类,从名字上看这不就是输入设备吗?然后调用了这个类的configure函数,这不就是配置一些参数吗?看来好像没错,这个类就是对/dev/input/下设备文件类进行读写的类。再看第二个函数:
[java]
view plain
copy
print?
void InputReader::updateGlobalMetaStateLocked() {
mGlobalMetaState = 0;
for (size_t i = 0; i < mDevices.size(); i++) {
InputDevice* device = mDevices.valueAt(i);
mGlobalMetaState |= device->getMetaState();
}
}
void InputReader::updateGlobalMetaStateLocked() { mGlobalMetaState = 0; for (size_t i = 0; i < mDevices.size(); i++) { InputDevice* device = mDevices.valueAt(i); mGlobalMetaState |= device->getMetaState(); } }
可以看到也是操作这个InputDevice。这个类到底是何方圣神,它到底是不是代表/dev/input/那些设备文件节点?
六.InputDevice
InputDevice::InputDevice(InputReaderContext* context, int32_t id, int32_t generation, int32_t controllerNumber, const InputDeviceIdentifier& identifier, uint32_t classes) : mContext(context), mId(id), mGeneration(generation), mControllerNumber(controllerNumber), mIdentifier(identifier), mClasses(classes), mSources(0), mIsExternal(false), mDropUntilNextSync(false) { }
InputDevice的构造函数是空的,那看看它有什么方法:
1.int32_t
InputDevice::getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc)--获得状态?
2.boolInputDevice::markSupportedKeyCodes(uint32_t
sourceMask, size_t numCodes,
const int32_t* keyCodes, uint8_t* outFlags) --支持的按键码?
3void InputDevice::getDeviceInfo(InputDeviceInfo* outDeviceInfo) --获得设备的信息?
4.boolisKeyPressed(int32_t code) {
return getEventHub()->getScanCodeState(mId, code) == AKEY_STATE_DOWN; } --判断按键是否按下吧?
。。。
可以看到它确实与/dev/input/下的设备文件节点密切相关。但并没有直接操作,而是借助getEventHub返回的EventHubInterface进行操作的。
七.EventHubInterface和EventHub
EventHubInterface是个虚函数,它的实现类是EventHub.
那我们想一下,要怎么读/dev/input/设备文件节点,怎么读呢?一般是多路复用吧?select,poll,epoll等,那我们在EventHub中搜搜看。
搜索的结果是select没找到,poll没出现,但是出现了epoll.搜索epoll_ctl等试试:
[html] view plain copy print?if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem)) {
mEpollFd = epoll_create(EPOLL_SIZE_HINT);
int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem)) { mEpollFd = epoll_create(EPOLL_SIZE_HINT); int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);重要的三个函数都出现了,所以这里应该就是对/dev/input读写的地方了。
具体的代码就不继续分析了,确实看的很累,这里是不是就找到了第一个问题的答案了呢?别忘了第一个问题:“是谁在读这些设备文件节点?”。
是的,就是这个EventHub类。
仔细回想一下,现在只是根据猜测加阅读,找到了读写/dev/input/设备文件节点的位置。可以想象一下,如果是自己写这部分代码,应该要一直监听这些输入设备吧,
应该有一个线程,一直循环监听这些输入设备,有事件的时候就去处理,没有事件的时候就睡眠,等待事件的到来。那么,这部分的代码是怎么样的呢?这次的分析知识找到了
一开始猜想的第一个答案,第二个答案还没有找到。要找到第二个答案,就需要更加细致的分析前面的流程,找到更多的内容,尤其要找到一个循环监听输入设备的线程的地方,
我相信它肯定存在,至于它是在什么地方创建,怎么调用的,下一节继续分析。
相关文章推荐
- Android输入事件从读取到分发三:InputDispatcherThread线程分发事件的过程
- Android输入事件从读取到分发五:事件分发前的拦截过程
- Android输入事件从读取到分发五:事件分发前的拦截过程
- Android输入事件从读取到分发三:InputDispatcherThread线程分发事件的过程
- Android输入事件从读取到分发二:谁在循环监听事件的到来
- Android输入事件从读取到分发四:InputDispatcherThread发送事件到View结构树的过程
- StevGuo系列文章翻译之Android中的输入事件如何分发
- Android系统--事件读取及分发
- Android 输入管理服务-输入事件向详细应用的分发
- StevGuo系列文章翻译之Android中的输入事件如何分发
- Android 输入管理服务-输入事件向具体应用的分发
- Android系统--事件读取及分发
- Android的输入事件分发机制笔记
- Android系统--事件读取及分发
- Android系统--事件读取及分发
- Android事件分发机制详解:史上最全面、最易懂
- Android:View的事件分发与消费机制
- Android ViewGroup事件分发机制
- Android自定义View的事件分发机制(二)
- 一个demo让你彻底理解Android中触摸事件的分发