출처 : http://blog.naver.com/osk1004?Redirect=Log&logNo=50069078782


==========================================================================================

원문 : http://letsgoustc.spaces.live.com/?_c11_BlogPart_BlogPart=blogview&_c=BlogPart&partqs=amonth%3d12%26ayear%3d2008

[First written by Steve Guo, please keep the mark if forwarding.]

Input Event Detect and Dispatch

input event dispatch engine 은 WindowManagerService.java 안에 위치한다.

WindowManagerService.java 는 KeyInputQueuq.java 로 부터 input event 를 read 하기 위한 하나의 thread 를 생성한다.

그리고, 그 event 를 binder 를 통해서 현재 focus 가 있는 window 로 dispatch 시키는 일을 한다.

                // Retrieve next event, waiting only as long as the next

                // repeat timeout.  If the configuration has changed, then

                // don't wait at all -- we'll report the change as soon as

                // we have processed all events.

                QueuedEvent ev = mQueue.getEvent(

                    (int)((!configChanged && curTime < nextKeyTime)

                            ? (nextKeyTime-curTime) : 0));

input event 가 읽어 지면,  input event type 을 결정한다.현재는 3개의 input event type 을 지원한다.

1. Key

2. Trackball

3. Pointer

binder 를 통해 현재 fucus 를 가지고 있는 window 로 event type 에 매핑되는 dispatch fucntion 을 호출한다.(아~ 해석이 어색하다)

 

key event 를 예를 들면, 아래의 code 를 호출하게 된다.

            focus.mClient.dispatchKey(event); 

 

가장 아랫단에서,  Android 는 Linux input device 로 부터 실제 input event(keyboard,mouse,touch 등)를 읽어들인다.

그에 상응하는 소스는 EventHub,cpp 이다.

Key input event 를 위해서, Android 는 key lyout map file로 부터 key code 를 scen 한다.

OEM 회사들은 필요하다면, 그들의 device 에 match 시키기 위해 key lay map file 을 customize 할 필요가 있다.

그것은 key layout map file 에서 찾기 위해 아래 함수를 이용한다.

 

        // a more descriptive name

        ioctl(mFDs[mFDCount].fd, EVIOCGNAME(sizeof(devname)-1), devname);

        devname[sizeof(devname)-1] = 0;

        device->name = devname;

 

        // replace all the spaces with underscores

        strcpy(tmpfn, devname);

        for (char *p = strchr(tmpfn, ' '); p && *p; p = strchr(tmpfn, ' '))

            *p = '_';

 

        // find the .kl file we need for this device

        const char* root = getenv("ANDROID_ROOT");

        snprintf(keylayoutFilename, sizeof(keylayoutFilename),

                 "%s/usr/keylayout/%s.kl", root, tmpfn);

        bool defaultKeymap = false;

        if (access(keylayoutFilename, R_OK)) {

            snprintf(keylayoutFilename, sizeof(keylayoutFilename),

                     "%s/usr/keylayout/%s", root, "qwerty.kl");

            defaultKeymap = true;

        }

        device->layoutMap->load(keylayoutFilename);

 

OEM 은 Android가 booting 하는 동안, key layout map file 이름을 가져올수 있다. 왜냐하면, Android 는 그 file name 을 기록할것이기 때문이다.

Java layer 단의 wrapper 는 WindowManagerService.java 에서 사용되어지는  KeyInputQueue.java 파일이다.

그것은 JNI 를 통해 EventHub.cpp 를 호출한다. com_android_server_KeyInputQueuq.cpp 는 JNI implementation 파일이다.

 

Input Event Processing

ativity 가 launch 되어질때, ActivityManagerService.java 는 activity 를 생성하기 위해서 ActivityThread.java 를 호출한다.

                activity.attach(appContext, this, getInstrumentation(), r.token, app,

                        r.intent, r.activityInfo, title, r.parent, r.embeddedID,

                        r.lastNonConfigurationInstance, config);

 

그러고 나서 Activity.java 가 activity 를 표시하기 위해 PhoneWindow.java 의 intance 를 생성한다. 각각의 PhoneWindow.java 는 activity 내의 view 들에 대한 Root 로써 DecorView.java 의 instance 를 포함하고 있다.

        mWindow = PolicyManager.makeNewWindow(this);

        mWindow.setCallback(this);

 

Activity 가 생성된 후에, Activity 를 resume 시키기 위해 ActivityManagerService.java 는 ActivityThread.java 를 호출한다.

이때, ActivityThread.java 는 DecorView.java 의 instance 를 add 시키기 위해서 WindowManagerImpl.java 를 호출한다.

                r.window = r.activity.getWindow();

                View decor = r.window.getDecorView();

                decor.setVisibility(View.INVISIBLE);

                ViewManager wm = a.getWindowManager();

                WindowManager.LayoutParams l = r.window.getAttributes();

                a.mDecor = decor;

                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;

                wm.addView(decor, l);

 

WindowManagerImpl.java 는 ViewRoot.java 의 instance 를 생성한다. ViewRoot.java 는 각각의 process 에서 오직 한번만 initialize 되는 static member 를 가진다.

그것은 WindowManagerService.java 에게 현재 link 된 process 가 있는지 여부를 알려줄때 사용되어진다.

            if (!mInitialized) {

                try {

                    sWindowSession = IWindowManager.Stub.asInterface(

                            ServiceManager.getService("window"))

                            .openSession(new Binder());

                    mInitialized = true;

                } catch (RemoteException e) {

                }

            }

 

ViewRoot.java instance 가 생성된 이후에, WindowManagerImpl.java 는 ViewRoot.java 를 binding 하기 위해 DecorView.java 에 대해 setView API 를 호출한다.

        // do this last because it fires off messages to start doing things

        root.setView(view, wparams, panelParentView);

 

setView API 안에서, 마침내 ViewRoot.java 는 DecorView.java 를 그리게 된다. 그리고 IWindow instance 를 WindowManagerService.java 에 register 시킨다.  

                    res = sWindowSession.add(mWindow, attrs,

                            getHostVisibility(), mCoveredInsets);

 

그이후에, WindowManagerService.java 는 ViewRoot.java 내에서 이 IWindow instance 와 직접적으로 통신한다.( Dispatching input event 를 포함해서...)

그러면, ViewRoot.java 는 input event 를 처리하기 위해서 View.java 를 호출한다.

Key event 를 예를 들면, View.java 안에 있는 dispatchKeyEvent() API 가 호출될 것이다.

    public boolean dispatchKeyEvent(KeyEvent event) {

        // If any attached key listener a first crack at the event.

        //noinspection SimplifiableIfStatement

        if (mOnKeyListener != null && (mViewFlags & ENABLED_MASK) == ENABLED

                && mOnKeyListener.onKey(this, event.getKeyCode(), event)) {

            return true;

        }

   

        return event.dispatch(this);

    }

 

만약 어떤 Key listener 가 이 view 에 register 되어져 있다면, View.java 는 그것을 찾는다.

Key listener 가 있다면, 그 key event 는 그 Key listener 에 의해 처리되어질 것이다.

그렇지 않으면,  대개 OnKeyDown/OnKeyUp 을 호출하게 된다.

 

모든 Key listener 구현은 frameworks/base/core/java/android/text/metod 에 있다.

 

MultiTapKeyListener.java     -    keypad 가 NUMERIC 면, 이 listener 는 숫자입력을 문자로 변환할것이다.

QwertyKeyListener.java        -     keypad 가 QWERTY 면, 이 listener 가 사용된다.

 

 

 

=============================================== 원 문 ===============================================

[First written by Steve Guo, please keep the mark if forwarding.]

Input Event Detect and Dispatch

The input event dispatch engine is in WindowManagerService.java. WindowManagerService.java creates a thread to read input event from KeyInputQueue.java and dispatches the event to the window which has current focus through binder.

                // Retrieve next event, waiting only as long as the next

                // repeat timeout.  If the configuration has changed, then

                // don't wait at all -- we'll report the change as soon as

                // we have processed all events.

                QueuedEvent ev = mQueue.getEvent(

                    (int)((!configChanged && curTime < nextKeyTime)

                            ? (nextKeyTime-curTime) : 0));

If an input event is read, it judges the input event type. Currently support three input event types: key, trackball and pointer. Then according to event type call corresponding dispatch function to the window which has current focus through binder. For example, for key event, it calls the following code.

            focus.mClient.dispatchKey(event);

At the lowest level, Android reads the real input event (keyboard, mouse or touch) from Linux input device. The corresponding source code is EventHub.cpp. For key input event, Android maps scan code to key code according to a key layout map file. OEM needs customize the key layout map file to match the needs of his own device. It uses the following method to find out the key layout map file.

        // a more descriptive name

        ioctl(mFDs[mFDCount].fd, EVIOCGNAME(sizeof(devname)-1), devname);

        devname[sizeof(devname)-1] = 0;

        device->name = devname;

 

        // replace all the spaces with underscores

        strcpy(tmpfn, devname);

        for (char *p = strchr(tmpfn, ' '); p && *p; p = strchr(tmpfn, ' '))

            *p = '_';

 

        // find the .kl file we need for this device

        const char* root = getenv("ANDROID_ROOT");

        snprintf(keylayoutFilename, sizeof(keylayoutFilename),

                 "%s/usr/keylayout/%s.kl", root, tmpfn);

        bool defaultKeymap = false;

        if (access(keylayoutFilename, R_OK)) {

            snprintf(keylayoutFilename, sizeof(keylayoutFilename),

                     "%s/usr/keylayout/%s", root, "qwerty.kl");

            defaultKeymap = true;

        }

        device->layoutMap->load(keylayoutFilename);

OEM can get the key layout map file name during Android booting, because Android will log the name. The JAVA layer wrapper is KeyInputQueue.java which is used by WindowManagerService.java. It calls EventHub.cpp through JNI. com_android_server_KeyInputQueue.cpp is the JNI implementation.

 

Input Event Processing

When an activity is to be launched, ActivityManagerService.java calls ActivityThread.java for creating the activity.

                activity.attach(appContext, this, getInstrumentation(), r.token, app,

                        r.intent, r.activityInfo, title, r.parent, r.embeddedID,

                        r.lastNonConfigurationInstance, config);

Then Activity.java creates a PhoneWindow.java instance to represent the activity. Each PhoneWindow.java contains a DecorView.java instance as the root of any views in the activity.

        mWindow = PolicyManager.makeNewWindow(this);

        mWindow.setCallback(this);

After an activity is created, ActivityManagerService.java calls ActivityThread.java for resume the activity. At this time, ActivityThread.java calls WindowManagerImpl.java to add the DecorView.java instance.

                r.window = r.activity.getWindow();

                View decor = r.window.getDecorView();

                decor.setVisibility(View.INVISIBLE);

                ViewManager wm = a.getWindowManager();

                WindowManager.LayoutParams l = r.window.getAttributes();

                a.mDecor = decor;

                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;

                wm.addView(decor, l);

WindowManagerImpl.java news a ViewRoot.java instance. ViewRoot.java has a static member which will be only initialized once for each process. It’s used to let WindowManagerService.java know that there is a process linked now.

            if (!mInitialized) {

                try {

                    sWindowSession = IWindowManager.Stub.asInterface(

                            ServiceManager.getService("window"))

                            .openSession(new Binder());

                    mInitialized = true;

                } catch (RemoteException e) {

                }

            }

After ViewRoot.java instance is created, WindowManagerImpl.java calls its setView API to bind the ViewRoot.java for the DecorView.java.

        // do this last because it fires off messages to start doing things

        root.setView(view, wparams, panelParentView);

In setView API, ViewRoot.java finally draws the DecorView.java and registers an IWindow instance to WindowManagerService.java.

                    res = sWindowSession.add(mWindow, attrs,

                            getHostVisibility(), mCoveredInsets);

After that WindowManagerService.java directly communicates (including dispatching input event) with this IWindow instance in ViewRoot.java. Then ViewRoot.java calls View.java to process input event. For example, for key event, dispatchKeyEvent API in View.java will be called.

    public boolean dispatchKeyEvent(KeyEvent event) {

        // If any attached key listener a first crack at the event.

        //noinspection SimplifiableIfStatement

        if (mOnKeyListener != null && (mViewFlags & ENABLED_MASK) == ENABLED

                && mOnKeyListener.onKey(this, event.getKeyCode(), event)) {

            return true;

        }

   

        return event.dispatch(this);

    }

View.java detects if any key listener is registered for this view. If so, the key event will be handled by the key listener. Otherwise, it calls OnKeyDown/OnKeyUp as usual.

 

All the key listener implementations are under frameworks/base/core/java/android/text/method folder.

MultiTapKeyListener.java       - if the keypad is NUMERIC keypad, this listener is used to transform digit inputs to characters.

QwertyKeyListener.java – if the keypad is QWERTY keypad, this listener is used.

Posted by 행복한 프로그래머 궁금쟁이박

댓글을 달아 주세요

  1. BlogIcon rolex replica 2013.01.22 16:57  댓글주소  수정/삭제  댓글쓰기

    팁 주셔서 감사합니다, 아마도이 호리 호리한 내 순수 지성의 인식 작용 마케팅을 사용할 수 있습니다 그리고 상호 작용을 시도에 어떠한 인종의 미디어를 사용하고 있으며 사람들이 유효 그린 저에 큰 친근합니다.