乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      Android按鍵消息處理

       昵稱2009261 2014-04-25
      在android系統(tǒng)中,鍵盤按鍵事件是由SystemServer服務(wù)來管理的;然后在以消息的形式分發(fā)給應(yīng)用程序處理。產(chǎn)生鍵盤按鍵事件則是有Linuxkernel的相關(guān)驅(qū)動來實現(xiàn)。

      鍵盤消息有別于其他類型的消息;需要從Linuxkerneldrivers產(chǎn)生由上層app來處理。同時按鍵有著不同的映射值,因此從模塊獨立性角度各個獨立的模塊應(yīng)該擁有不同的鍵盤映射。這樣以來,kernel產(chǎn)生的按鍵事件必然回經(jīng)過不同的映射才到app。



      1、kernel中同按鍵相關(guān)代碼

          Android 使用標(biāo)準(zhǔn)的 linux 輸入事件設(shè)備(/dev/input/)和驅(qū)動按鍵定義在 linux 內(nèi)核include/linux/input.h 中,按鍵的定義形式如下(僅以BACKHOME MENU為例):


          有了按鍵的定義,就需要產(chǎn)生相應(yīng)的按鍵事件了。在kernel/arch/arm/mach-msm/xxx/xxx/xxx.c會對BACK HOME和MENU進(jìn)行注冊。這里使用在屏幕上的坐標(biāo)來對按鍵進(jìn)行區(qū)分。這部分代碼會在系統(tǒng)啟動的時候,將相應(yīng)的數(shù)據(jù)存儲,以供framework查詢。

      (這里以xxx代替,是因為針對不同的硬件,需要的Linux kernel不同)


      當(dāng)然從核心板原理圖到kernel是屬于驅(qū)動范疇,不討論。

      2、framework針對鍵盤事件的處理

          上層對輸入事件的偵聽和分發(fā)是在InputManagerService 中實現(xiàn)

          首先來看看InputManagerService的創(chuàng)建,

      Step1

      在SystemServer.java

      點擊(此處)折疊或打開

      1. class ServerThread extends Thread {
      2.     //省略。。
      3.     public void run() {
      4.         // Create a handler thread just for the window manager to enjoy.
      5.         HandlerThread wmHandlerThread = new HandlerThread("WindowManager");
      6.         wmHandlerThread.start();
      7.         Handler wmHandler = new Handler(wmHandlerThread.getLooper());
      8.         //此處省略5k字。。
      9.         Slog.i(TAG, "Input Manager");
      10.         inputManager = new InputManagerService(context, wmHandler);
      11.     }
      12. }

      可以看到,在系統(tǒng)啟動的時候,會首先創(chuàng)建一個系統(tǒng)級別的Handler線程wmHandlerThread用于處理鍵盤消息(僅說明鍵盤消息)。然后在創(chuàng)建輸入管理服務(wù) inputManager,InputManagerService 的第二個參數(shù)就是用于處理按鍵消息的Handler。


      Step2

      在往下走到 InputManagerService.java的構(gòu)造函數(shù)。

      點擊(此處)折疊或打開

      1. public InputManagerService(Context context, Handler handler) {
      2.     this.mContext = context;
      3.     this.mHandler = new InputManagerHandler(handler.getLooper());

      4.     mUseDevInputEventForAudioJack =
      5.                 context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);
      6.     Slog.i(TAG, "Initializing input manager, mUseDevInputEventForAudioJack="
      7.                     + mUseDevInputEventForAudioJack);
      8.     mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
      9. }

      這里做了重要的兩件事情,第一:將SystemServer級別的Handler賦值給 InputManagerService自己的消息處理Handler;第二:調(diào)用nativeInit繼續(xù)進(jìn)行初始化。


      Step3

      com_android_server_InputManagerService.cpp

      點擊(此處)折疊或打開

      1. static jint nativeInit(JNIEnv* env, jclass clazz,
      2.     jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
      3.     sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
      4.     NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
      5.     messageQueue->getLooper());
      6.     im->incStrong(serviceObj);
      7.     return reinterpret_cast<jint>(im);
      8. }

      這里nativeInit直接調(diào)用了 NativeInputManager的構(gòu)造函數(shù)


      Step4

      點擊(此處)折疊或打開

      1. NativeInputManager::NativeInputManager(jobject contextObj,
      2.     jobject serviceObj, const sp<Looper>& looper) :
      3.     mLooper(looper) {
      4.     JNIEnv* env = jniEnv();

      5.     mContextObj = env->NewGlobalRef(contextObj);
      6.     mServiceObj = env->NewGlobalRef(serviceObj);

      7.     {
      8.         AutoMutex _l(mLock);
      9.         mLocked.systemUiVisibility = ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE;
      10.         mLocked.pointerSpeed = 0;
      11.         mLocked.pointerGesturesEnabled = true;
      12.         mLocked.showTouches = false;
      13.     }

      14.     sp<EventHub> eventHub = new EventHub();
      15.     mInputManager = new InputManager(eventHub, this, this);
      16. }

      這里需要特別注意最后兩行代碼。第一:創(chuàng)建了 EventHub;第二:創(chuàng)建 InputManager并將 EventHub作為參數(shù)傳入InputManager。


      Step5

      接下來繼續(xù)看看InputManager的構(gòu)造函數(shù)。

      點擊(此處)折疊或打開

      1. InputManager::InputManager(
      2.     const sp<EventHubInterface>& eventHub,
      3.     const sp<InputReaderPolicyInterface>& readerPolicy,
      4.     const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
      5.         mDispatcher = new InputDispatcher(dispatcherPolicy);
      6.         mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
      7.         initialize();
      8. }

      9. void InputManager::initialize() {
      10.     mReaderThread = new InputReaderThread(mReader);
      11.     mDispatcherThread = new InputDispatcherThread(mDispatcher);
      12. }



      創(chuàng)建了InputDispatcher 和InputReader,并調(diào)用了initialize函數(shù)創(chuàng)建了InputReaderThread和InputDispatcherThread。InputDispatcher類是負(fù)責(zé)把鍵盤消息分發(fā)給當(dāng)前激活的Activity窗口的,而InputReader類則是通過 EventHub類來實現(xiàn)讀取鍵盤事件的,InputReader實列mReader就是通過這里的 InputReaderThread線程實列mReaderThread來讀取鍵盤事件的,而InputDispatcher實例mDispatcher 則是通過這里的InputDispatcherThread線程實例mDisptacherThread來分發(fā)鍵盤消息的。

      到這里,相關(guān)的組件都已經(jīng)被創(chuàng)建了;


      Step6

      接下來看看他們是如何運行起來的。

      在systemServer.java中創(chuàng)建inputManager之后。將InputManagerServer進(jìn)行注冊,并運行start()

      點擊(此處)折疊或打開

      1. ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
      2.     inputManager.start();
      3.     //InputManager的start函數(shù):
      4.     public void start() {
      5.     Slog.i(TAG, "Starting input manager");
      6.     nativeStart(mPtr);
      7.     //省略。。
      8. }

      調(diào)用nativeStart繼續(xù)往下走。順帶說一下,這里的參數(shù)mPtr是指向nativeinputmanager service對象的,在InputManagerService構(gòu)造函數(shù)中由nativeInit賦值。


      Step7

      接下來又到了com_android_server_InputManagerService.cpp中。

      點擊(此處)折疊或打開

      1. static void nativeStart(JNIEnv* env, jclass clazz, jint ptr) {
      2.     NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);

      3.     status_t result = im->getInputManager()->start();
      4.     if (result) {
      5.         jniThrowRuntimeException(env, "Input manager could not be started.");
      6.     }
      7. }

      這里的im就是inputManager并且用到了上面?zhèn)飨聛淼膍Ptr來重新構(gòu)建。


      Step8

      繼續(xù)往下則會調(diào)用到InputManager.cpp 的start函數(shù)

      點擊(此處)折疊或打開

      1. status_t InputManager::start() {
      2.     status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
      3.     if (result) {
      4.     ALOGE("Could not start InputDispatcher thread due to error %d.", result);
      5.     return result;
      6.     }

      7.     result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
      8.     if (result) {
      9.         ALOGE("Could not start InputReader thread due to error %d.", result);

      10.         mDispatcherThread->requestExit();
      11.         return result;
      12.     }

      13.     return OK;
      14. }

      這個函數(shù)主要就是分別啟動一個InputDispatcherThread線程和一個InputReaderThread線程來讀取和分發(fā)鍵 盤消息的了。這里的InputDispatcherThread線程對象mDispatcherThread和InputReaderThread線程對 象是在前面的Step9中創(chuàng)建的,調(diào)用了它們的run函數(shù)后,就會進(jìn)入到它們的threadLoop函數(shù)中去,只要threadLoop函數(shù)返回true,函數(shù) threadLoop就會一直被循環(huán)調(diào)用,于是這兩個線程就起到了不斷地讀取和分發(fā)鍵盤消息的作用。


      Step9

      在下來繼續(xù)看loopOnce()這個函數(shù)。

      點擊(此處)折疊或打開

      1. void InputReader::loopOnce() {
      2.     //......
      3.     size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
      4.     //......
      5.     if (count) {
      6.         processEventsLocked(mEventBuffer, count);
      7.     }
      8.     //......
      9.     // Flush queued events out to the listener.
      10.     // This must happen outside of the lock because the listener could potentially call
      11.     // back into the InputReader's methods, such as getScanCodeState, or become blocked
      12.     // on another thread similarly waiting to acquire the InputReader lock thereby
      13.     // resulting in a deadlock. This situation is actually quite plausible because the
      14.     // listener is actually the input dispatcher, which calls into the window manager,
      15.     // which occasionally calls into the input reader.
      16.     mQueuedListener->flush();
      17. }

      這里面需要注意像神一樣的函數(shù) mEventHub->getEvents()。其實現(xiàn)原理,還有點不是很清楚;但是其功能就是負(fù)責(zé)鍵盤消息的讀取工作,如果當(dāng)前有鍵盤事件發(fā)生或者有鍵盤事件等待處理,通過mEventHub的 getEvent函數(shù)就可以得到這個事件,然后交給processEventsLocked 函數(shù)進(jìn)行處理。同樣需要特別注意最后一行;后面回解釋。我們還會回來的~~~


      點擊(此處)折疊或打開

      1. /*
      2.      * Wait for events to become available and returns them.
      3.      * After returning, the EventHub holds onto a wake lock until the next call to getEvent.
      4.      * This ensures that the device will not go to sleep while the event is being processed.
      5.      * If the device needs to remain awake longer than that, then the caller is responsible
      6.      * for taking care of it (say, by poking the power manager user activity timer).
      7.      *
      8.      * The timeout is advisory only. If the device is asleep, it will not wake just to
      9.      * service the timeout.
      10.      *
      11.      * Returns the number of events obtained, or 0 if the timeout expired.
      12.      */
      13.     virtual size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize)

      函數(shù)原型!

      在成功獲取input Event之后,就會用到 processEventsLocked函數(shù)來處理Event

      然后在調(diào)用到 processEventsForDeviceLocked(deviceId,rawEvent, batchSize);

      最后在void InputDevice::process(constRawEvent* rawEvents, size_t count)

      我就在想:問什么不直接到process函數(shù)呢?其實我覺得這里體現(xiàn)了設(shè)計模式中的單一職責(zé)原則;這種設(shè)計可以有效的控制函數(shù)粒度(有個類粒度,這里自創(chuàng)函數(shù)粒度)的大小,函數(shù)承擔(dān)的職責(zé)越多其復(fù)用的可能性就越小,并且當(dāng)期中某一個職責(zé)發(fā)生變化,可能會影響其他職責(zé)的運作!


      Step 10

      接下來繼續(xù)看InputDevice::process函數(shù)。

      點擊(此處)折疊或打開

      1. void InputDevice::process(const RawEvent* rawEvents, size_t count) {
      2.     //。。。。
      3.     InputMapper* mapper = mMappers[i];
      4.     mapper->process(rawEvent);
      5. }

      走到這里才算是真真正正的知道了有按鍵發(fā)生了,調(diào)用 KeyboardInputMapper::process(const RawEvent*)處理input event; KeyboardInputMapper 繼承自 InputMapper。那為什么調(diào)用的是 KeyboardInputMapper而不是SwitchInputMapper等等。。

      請留意

      點擊(此處)折疊或打開

      1. InputDevice* InputReader::createDeviceLocked(int32_t deviceId,
      2.                                                 const InputDeviceIdentifier& identifier, uint32_t classes)

      函數(shù)中的片段:

      點擊(此處)折疊或打開

      1. if (keyboardSource != 0) {
      2.         device->addMapper(new KeyboardInputMapper(device, keyboardSource, keyboardType));
      3.     }

      這里Event Type有必要提一下,以下是一些常用的Event。在kernel/Documentation/input/event-codes.txt中有詳細(xì)的描述。

      * EV_SYN:

        - Used as markers to separate events. Eventsmay be separated in time or in space, such as with the multitouch protocol.

      * EV_KEY:

        - Used to describe state changes ofkeyboards, buttons, or other key-like devices.

      * EV_REL:

        - Used to describe relative axis value changes,e.g. moving the mouse 5 units to the left.

      * EV_ABS:

        - Used to describe absolute axis valuechanges, e.g. describing the coordinates of a touch on a touchscreen.

      * EV_MSC:

        - Used to describe miscellaneous input datathat do not fit into other types.

      * EV_SW:

      -           Used to describe binary stateinput switches.

      Step 11

      點擊(此處)折疊或打開


      1. void KeyboardInputMapper::process(const RawEvent* rawEvent) {
      2.     switch (rawEvent->type) {
      3.     case EV_KEY: {
      4.         int32_t scanCode = rawEvent->code;
      5.         int32_t usageCode = mCurrentHidUsage;
      6.         mCurrentHidUsage = 0;

      7.         if (isKeyboardOrGamepadKey(scanCode)) {
      8.             int32_t keyCode;
      9.             uint32_t flags;
      10.             if (getEventHub()->mapKey(getDeviceId(), scanCode, usageCode, &keyCode, &flags)) {
      11.                 keyCode = AKEYCODE_UNKNOWN;
      12.                 flags = 0;
      13.             }
      14.             processKey(rawEvent->when, rawEvent->value != 0, keyCode, scanCode, flags);
      15.         }
      16.         break;
      17.     }
      18.     }
      19. }

      在這里,先判斷isKeyboardOrGamepadKey(scanCode),然后在用getEventHub()->mapKey()檢測 提供的key是否正確,在然后就開始處理了processKey

      Step 12

      點擊(此處)折疊或打開

      1. void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t keyCode,
      2.         int32_t scanCode, uint32_t policyFlags) {
      3.     //忽略到所有的。。只看最后兩行。。
      4.     NotifyKeyArgs args(when, getDeviceId(), mSource, policyFlags,
      5.         down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
      6.          AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, newMetaState, downTime);

      7.      getListener()->notifyKey(&args);
      8. }


          不用多解釋了,直接notifyKey了。。但需要注意,這里的notifyKey 僅僅是 NotifyKeyArgs  push到消息隊列中去;并沒有通知上層!那到底在那兒通知的呢?


      還記不記得在void InputReader::loopOnce()這個函數(shù)的最后一行代碼,其實質(zhì)是在這個函數(shù)中通知上層有按鍵事件發(fā)生。

      這個flush()很明顯,notify了之后,就delete,不存在了。問什么不是在getListener()->notifyKey(&args);的時候就真正的notify?我覺得可以做如下角度予以考慮:


      第一:線程是最小的執(zhí)行單位;因此每當(dāng)inputThread.start()的時候,如果不flush,回造成數(shù)據(jù)混亂。

      第二:flush操作是必須的,同時在loopOnce的最后操作也是最恰當(dāng)?shù)?。其實這里的Listener也就是充當(dāng)了一個事件分發(fā)者的角色。

      這說明,到這里已經(jīng)完全識別了按鍵了,并按照自己的鍵盤映射映射了一個值保存在args中,notifyKey給上層應(yīng)用了。。


      Step 13

               其實針對BACK  HOME MENU這三個按鍵來說,其實質(zhì)就是TouchScreen;因此在inputReader.cpp中獲取Touch映射是在函數(shù)boolTouchInputMapper::consumeRawTouches(nsecs_t when, uint32_t policyFlags)  中。這里同上面的Step 12相同。


      首先檢測不是多點Touch。然后使用const TouchInputMapper::VirtualKey*TouchInputMapper::findVirtualKeyHit( int32_t x, int32_t y)依據(jù)坐標(biāo)值查找出Touch的映射值。
      到最后了啊。。。
      呵呵,看看是怎么實現(xiàn)的。。





        本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多