密码太多记不了,怎么办?
http://a.app.qq.com/o/simple.jsp?pkgname=com.wa505.kf.epassword
补充一下更详细的解说图,在输入输出系统中存在很多类,这些类很难分清进程边界。
从图中可以看出,整个输入输出采用的是双队列缓冲。
其中的进程间通信采用的是 共享内存+管道+信号量,整个通信过程没有涉及Binder。因为使用了android中的匿名共享内存,而共享内存不存在数据复制的问题,因此速度非常快。
先讲一下基本一般的输入处理方式的知识。一般的输入输出采用生产者,消费者模式,并构造队列进行处理,如下图
这种输入模型在android的系统中很多地方采用,先从最底层说起:
为了由于触屏事件频率很高,android设计者讲一个循环线程,拆分为两级循环,并做了个队列来进行缓冲。
InputDispatcherThread和InputReaderThread。InputDispatcherThread在自己的循环中对InputReaderThread请求同步,InputReaderThread收到同步信号后,把事件放入InputDispatcher的队列中。
具体代码如下:
InputReader.cpp中有很多InputMapper,有SwitchInputMapper,KeyBoardInputMapper,TrackballInputMapper,SingleTouchInputMapper,
MultiTouchInputMapper。当线程从EventHub读取到Event后,调用这些InputMapper的pocess方法:
文件InputReader.cpp中: bool InputReaderThread::threadLoop() { mReader->loopOnce(); return true; } void InputReader::loopOnce() { RawEvent rawEvent; mEventHub->getEvent(& rawEvent); #if DEBUG_RAW_EVENTS LOGD("Input event: device=0x%x type=0x%x scancode=%d keycode=%d value=%d", rawEvent.deviceId, rawEvent.type, rawEvent.scanCode, rawEvent.keyCode, rawEvent.value); #endif process(& rawEvent); }
process如下
void InputReader::process(const RawEvent* rawEvent) {
switch (rawEvent->type) {
default:
consumeEvent(rawEvent);
break;
}
}
void InputReader::process(const RawEvent* rawEvent) { switch (rawEvent->type) { consumeEvent(rawEvent); break; } }
consumeEvent(rawEvent);
方法是关键,下面继续跟进;
void InputReader::consumeEvent(const RawEvent* rawEvent) { int32_t deviceId = rawEvent->deviceId; { device->process(rawEvent); } // release device registry reader lock }
device->process(rawEvent)行, 跟进去:
void InputDevice::process(const RawEvent* rawEvent) { size_t numMappers = mMappers.size(); for (size_t i = 0; i < numMappers; i++) { InputMapper* mapper = mMappers[i]; mapper->process(rawEvent); } }
下面进入了IputMapper,InputMapper是个纯虚类,process是个纯虚方法,随便找个例子跟进去:
void SingleTouchInputMapper::process(const RawEvent* rawEvent) { switch (rawEvent->type) { case EV_KEY: switch (rawEvent->scanCode) { case BTN_TOUCH: mAccumulator.fields |= Accumulator::FIELD_BTN_TOUCH; mAccumulator.btnTouch = rawEvent->value != 0; // Don't sync immediately. Wait until the next SYN_REPORT since we might // not have received valid position information yet. This logic assumes that // BTN_TOUCH is always followed by SYN_REPORT as part of a complete packet. break; } break; case EV_SYN: switch (rawEvent->scanCode) { case SYN_REPORT: sync(rawEvent->when); break; } break; } }
最关键的是
sync(rawEvent->when);
展开如下:
void SingleTouchInputMapper::sync(nsecs_t when) { syncTouch(when, true); }
void TouchInputMapper::syncTouch(nsecs_t when, bool havePointerIds) { if (touchResult == DISPATCH_TOUCH) { detectGestures(when); dispatchTouches(when, policyFlags); } }
这两行,一个是虚拟键盘,一个是触摸屏。
TouchResult touchResult = consumeOffScreenTouches(when, policyFlags);
dispatchTouches
只说触摸屏,虚拟键类似,触摸屏调用的是
void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) { // Dispatch pointer down events using the new pointer locations. while (!downIdBits.isEmpty()) { dispatchTouch(when, policyFlags, &mCurrentTouch, activeIdBits, downId, pointerCount, motionEventAction); } } }
dispatchTouch(when, policyFlags, &mCurrentTouch, activeIdBits, downId, pointerCount, motionEventAction);
这个方法展开如下:
void TouchInputMapper::dispatchTouch(nsecs_t when, uint32_t policyFlags, TouchData* touch, BitSet32 idBits, uint32_t changedId, uint32_t pointerCount, int32_t motionEventAction) { int32_t pointerIds[MAX_POINTERS]; PointerCoords pointerCoords[MAX_POINTERS]; int32_t motionEventEdgeFlags = 0; float xPrecision, yPrecision; { getDispatcher()->notifyMotion(when, getDeviceId(), getSources(), policyFlags, motionEventAction, 0, getContext()->getGlobalMetaState(), motionEventEdgeFlags, pointerCount, pointerIds, pointerCoords, xPrecision, yPrecision, mDownTime); }
这样就到了InputDiaptcher的notifyMotion方法,这个方法很长,都再处理MOVE事件,将无用的删除后,留下如下关键代码:
void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, int32_t source, uint32_t policyFlags, int32_t action, int32_t flags, int32_t metaState, int32_t edgeFlags, uint32_t pointerCount, const int32_t* pointerIds, const PointerCoords* pointerCoords, float xPrecision, float yPrecision, nsecs_t downTime) { // Just enqueue a new motion event. MotionEntry* newEntry = mAllocator.obtainMotionEntry(eventTime, deviceId, source, policyFlags, action, flags, metaState, edgeFlags, xPrecision, yPrecision, downTime, pointerCount, pointerIds, pointerCoords); needWake = enqueueInboundEventLocked(newEntry); }
最后一句:
needWake = enqueueInboundEventLocked(newEntry);
bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) { bool needWake = mInboundQueue.isEmpty(); mInboundQueue.enqueueAtTail(entry); switch (entry->type) { case EventEntry::TYPE_KEY: { KeyEntry* keyEntry = static_cast<KeyEntry*>(entry); if (isAppSwitchKeyEventLocked(keyEntry)) { if (keyEntry->action == AKEY_EVENT_ACTION_DOWN) { mAppSwitchSawKeyDown = true; } else if (keyEntry->action == AKEY_EVENT_ACTION_UP) { if (mAppSwitchSawKeyDown) { #if DEBUG_APP_SWITCH LOGD("App switch is pending!"); #endif mAppSwitchDueTime = keyEntry->eventTime + APP_SWITCH_TIMEOUT; mAppSwitchSawKeyDown = false; needWake = true; } } } break; } } return needWake; }
mInboundQueue正是上面所说的队列。到此为止,从InputReader插入到队列就完成了。
那么InputDispatcher又是如何从队列中取出来的呢?累了。
InputDiapather的
dispatchOnce
方法如下:
void InputDispatcher::dispatchOnce() { nsecs_t keyRepeatTimeout = mPolicy->getKeyRepeatTimeout(); nsecs_t keyRepeatDelay = mPolicy->getKeyRepeatDelay(); nsecs_t nextWakeupTime = LONG_LONG_MAX; { // acquire lock AutoMutex _l(mLock); dispatchOnceInnerLocked(keyRepeatTimeout, keyRepeatDelay, & nextWakeupTime); if (runCommandsLockedInterruptible()) { nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately } } // release lock // Wait for callback or timeout or wake. (make sure we round up, not down) nsecs_t currentTime = now(); int32_t timeoutMillis; if (nextWakeupTime > currentTime) { uint64_t timeout = uint64_t(nextWakeupTime - currentTime); timeout = (timeout + 999999LL) / 1000000LL; timeoutMillis = timeout > INT_MAX ? -1 : int32_t(timeout); } else { timeoutMillis = 0; } mLooper->pollOnce(timeoutMillis); }
最关键的是
dispatchOnceInnerLocked(keyRepeatTimeout, keyRepeatDelay, & nextWakeupTime);
和
mLooper->pollOnce(timeoutMillis);//这个是个回调。
代码又长又臭
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t keyRepeatTimeout, nsecs_t keyRepeatDelay, nsecs_t* nextWakeupTime) { case EventEntry::TYPE_MOTION: { MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent); if (dropReason == DROP_REASON_NOT_DROPPED && isAppSwitchDue) { dropReason = DROP_REASON_APP_SWITCH; } done = dispatchMotionLocked(currentTime, typedEntry, &dropReason, nextWakeupTime); break; } }
dispatchMotionLocked
方法调用prepareDispatchCycleLocked,调用startDispatchCycleLocked,最终调用
// Publish the key event.
status = connection->inputPublisher.publishKeyEvent(keyEntry->deviceId, keyEntry->source,
action, flags, keyEntry->keyCode, keyEntry->scanCode,
keyEntry->metaState, keyEntry->repeatCount, keyEntry->downTime,
keyEntry->eventTime);
或者 // Publish the motion event and the first motion sample.
status = connection->inputPublisher.publishMotionEvent(motionEntry->deviceId,
motionEntry->source, action, flags, motionEntry->edgeFlags, motionEntry->metaState,
xOffset, yOffset,
motionEntry->xPrecision, motionEntry->yPrecision,
motionEntry->downTime, firstMotionSample->eventTime,
motionEntry->pointerCount, motionEntry->pointerIds,
firstMotionSample->pointerCoords);
然后// Send the dispatch signal.
status = connection->inputPublisher.sendDispatchSignal();
if (status) {
LOGE("channel '%s' ~ Could not send dispatch signal, status=%d",
connection->getInputChannelName(), status);
abortBrokenDispatchCycleLocked(currentTime, connection);
return;
}
至此,InputDisapatcher也结束了。
既然发布出去,必然有订阅者:在InputTransport.cpp中
status_t InputConsumer::consume(InputEventFactoryInterface* factory, InputEvent** outEvent) { #if DEBUG_TRANSPORT_ACTIONS LOGD("channel '%s' consumer ~ consume", mChannel->getName().string()); #endif *outEvent = NULL; int ashmemFd = mChannel->getAshmemFd(); int result = ashmem_pin_region(ashmemFd, 0, 0); if (result != ASHMEM_NOT_PURGED) { if (result == ASHMEM_WAS_PURGED) { LOGE("channel '%s' consumer ~ Error %d pinning ashmem fd %d because it was purged " "which probably indicates that the publisher and consumer are out of sync.", mChannel->getName().string(), result, ashmemFd); return INVALID_OPERATION; } LOGE("channel '%s' consumer ~ Error %d pinning ashmem fd %d.", mChannel->getName().string(), result, ashmemFd); return UNKNOWN_ERROR; } if (mSharedMessage->consumed) { LOGE("channel '%s' consumer ~ The current message has already been consumed.", mChannel->getName().string()); return INVALID_OPERATION; } // Acquire but *never release* the semaphore. Contention on the semaphore is used to signal // to the publisher that the message has been consumed (or is in the process of being // consumed). Eventually the publisher will reinitialize the semaphore for the next message. result = sem_wait(& mSharedMessage->semaphore); if (result < 0) { LOGE("channel '%s' consumer ~ Error %d in sem_wait.", mChannel->getName().string(), errno); return UNKNOWN_ERROR; } mSharedMessage->consumed = true; switch (mSharedMessage->type) { case AINPUT_EVENT_TYPE_KEY: { KeyEvent* keyEvent = factory->createKeyEvent(); if (! keyEvent) return NO_MEMORY; populateKeyEvent(keyEvent); *outEvent = keyEvent; break; } case AINPUT_EVENT_TYPE_MOTION: { MotionEvent* motionEvent = factory->createMotionEvent(); if (! motionEvent) return NO_MEMORY; populateMotionEvent(motionEvent); *outEvent = motionEvent; break; } default: LOGE("channel '%s' consumer ~ Received message of unknown type %d", mChannel->getName().string(), mSharedMessage->type); return UNKNOWN_ERROR; } return OK; }
也许我们最关心的是如何订阅的,不得不取看一下JNI的代码,文件android_view_InputQueue.cpp
聚焦到这里
status_t NativeInputQueue::registerInputChannel(JNIEnv* env, jobject inputChannelObj, jobject inputHandlerObj, jobject messageQueueObj) { sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env, inputChannelObj); if (inputChannel == NULL) { LOGW("Input channel is not initialized."); return BAD_VALUE; } #if DEBUG_REGISTRATION LOGD("channel '%s' - Registered", inputChannel->getName().string()); #endif sp<Looper> looper = android_os_MessageQueue_getLooper(env, messageQueueObj); { // acquire lock AutoMutex _l(mLock); if (getConnectionIndex(inputChannel) >= 0) { LOGW("Attempted to register already registered input channel '%s'", inputChannel->getName().string()); return BAD_VALUE; } uint16_t connectionId = mNextConnectionId++; sp<Connection> connection = new Connection(connectionId, inputChannel, looper); status_t result = connection->inputConsumer.initialize(); if (result) { LOGW("Failed to initialize input consumer for input channel '%s', status=%d", inputChannel->getName().string(), result); return result; } connection->inputHandlerObjGlobal = env->NewGlobalRef(inputHandlerObj); int32_t receiveFd = inputChannel->getReceivePipeFd(); mConnectionsByReceiveFd.add(receiveFd, connection); looper->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this); } // release lock android_view_InputChannel_setDisposeCallback(env, inputChannelObj, handleInputChannelDisposed, this); return OK; }
也许更想知道的是消息队列在什么地方,进入InputQueue.java来看
public static void registerInputChannel(InputChannel inputChannel, InputHandler inputHandler, MessageQueue messageQueue) { if (inputChannel == null) { throw new IllegalArgumentException("inputChannel must not be null"); } if (inputHandler == null) { throw new IllegalArgumentException("inputHandler must not be null"); } if (messageQueue == null) { throw new IllegalArgumentException("messageQueue must not be null"); } synchronized (sLock) { if (DEBUG) { Slog.d(TAG, "Registering input channel '" + inputChannel + "'"); } nativeRegisterInputChannel(inputChannel, inputHandler, messageQueue); } }
在ViewRoot.java中有这么几行
InputQueue.registerInputChannel(mInputChannel, mInputHandler, Looper.myQueue());
完毕。
这才牵涉到管道的问题,哪个Java中的Channel对应的正是linux系统的管道。有了管道,才能通过 跨进程方式回调回来,为什么是这个入口,上面进行了解释。具体参照INputQUEUE这个java类的JNI方法
int NativeInputQueue::handleReceiveCallback(int receiveFd, int events, void* data)
这个方法被InputQueue的RegisterInputChannel注册给了系统.系统通过回调,回调的是这个ALOOPER_EVENT_INPUT事件。
looper就是android中的【用户进程的循环】
注册的代码为 :
looper->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
回调的java函数为
回调的java代码的方法入口为:InputQueue.java中的。 @SuppressWarnings("unused") private static void dispatchMotionEvent(InputHandler inputHandler, MotionEvent event, long finishedToken) { Runnable finishedCallback = FinishedCallback.obtain(finishedToken); inputHandler.handleMotion(event, finishedCallback); }
这样就回调到了ViewRoot中
相关推荐
### MFC源码剖析之——MFC来龙去脉 #### 一、MFC与传统C/SDK程序对比 在Windows编程中,开发者通常有两种选择:一种是使用Microsoft Foundation Classes (MFC),另一种则是直接使用Windows API(通常称为C/SDK编程...
这是因为Android的内存管理机制——Generational Heap Memory模型,当对象在年轻代(Young Generation)存活一段时间后,会逐渐晋升到老年代(Old Generation)和永久代(Permanent Generation)。一旦发生内存泄露...
在“参考资料-斜坡补偿到的来龙去脉与实例.pdf”中,很可能会详细阐述斜坡补偿的发展历程,包括其早期的应用、理论基础,以及随着时间的推移和技术的进步,如何演变成现代控制系统中不可或缺的一部分。此外,该文档...
本章介绍Android的来龙去脉,让读者以最短的时间直观地了解到Android的基础及发展的历史和趋势,并能清晰地知到自己可以在Android上做什么事情。 第2章 工欲善其事 必先利其器—搭建Android开发环境。本章介绍了在...
本章介绍Android的来龙去脉,让读者以最短的时间直观地了解到Android的基础及发展的历史和趋势,并能清晰地知到自己可以在Android上做什么事情。 第2章 工欲善其事 必先利其器—搭建Android开发环境。本章介绍了在...
本章介绍Android的来龙去脉,让读者以最短的时间直观地了解到Android的基础及发展的历史和趋势,并能清晰地知到自己可以在Android上做什么事情。 第2章 工欲善其事 必先利其器—搭建Android开发环境。本章介绍了在...
### MFC源码剖析之——MFC来龙去脉 #### MFC的起源与核心概念 MFC(Microsoft Foundation Classes)是由微软开发的一套用于Windows平台的C++类库,它封装了Windows API,简化了GUI应用程序的开发过程。通过使用MFC...
我在集成环信SDK到我们自己开发的app之前,研究了一下环信demo的代码,看了两三天的样子,基本搞清楚来龙去脉,但是只是清楚来龙去脉,要说到里面的细节可能得深一步研究,但是这就够了,已经可以把demo里面的功能...
### MFC来龙去脉:深入理解MFC框架与Windows编程 #### MFC的历史与背景 MFC(Microsoft Foundation Classes)是微软为简化Windows应用程序开发而设计的一套类库,它基于C++语言,提供了对Windows API的封装,使得...
### X86指令编码的来龙去脉:深入解析与理解 #### 1、序言:初探X86指令编码 X86指令集架构是个人计算机领域中最广泛使用的指令集之一,其指令编码机制复杂而精细,是实现硬件与软件间桥梁的关键。在学习X86指令...
在魏晋至清末前,由于王朝国家特性的影响,民族意识相对淡薄,更注重的是本朝本宗之祖。然而,随着甲午战争后民族危机的加深,以及民族主义的广泛传播,“炎黄子孙”成为了表达民族认同的重要符号。不同的政治团体对...
构件化编程的来龙去脉.ppt,构件编程详细解释
在探讨丞相的来龙去脉时,我们不仅要了解其词源,更要从历史的发展脉络中,把握其职权的变迁和在中国政治史中的地位。 “丞相”一词中的“丞”在《说文解字》中解释为“帮助、辅助”。在古代官职中,“丞”往往是指...
Android例子源码多种图片渲染处理源码是一款图片处理器的项目源码,类似美颜相机的各种处理照片一样,本项目也有多种处理照片的形式,比美颜相机有过之而无不及,而且它更增加了变形,扭曲等等让照片更加丰富多彩。...
中华文明外来论的来龙去脉.docx
聊聊企业容灾的来龙去脉.docx