Android输入事件读入流程

来源:互联网 发布:球体运动js效果大全 编辑:程序博客网 时间:2024/05/29 15:39

 

一.从SystemService.java中启动服务

     代码路径:frameworks/base/services/java/com/android/server/SystemServer.java


      public class SystemServer

      {


         .....

         native public static void init1(String[] args);

        

         public static void main(String[] args) {

         .........

         init1(args);

       }

       public static final void init2() {
        Slog.i("yaojuntaoSystemServer", "Entered the Android system server!");
        Thread thr = new ServerThread();
        thr.setName("android.server.ServerThread");
        thr.start();
      }
      }
调用init1(args),初始化显示系统,音频等,init1通过JNI调用到com_android_server_SystemServer.cpp
的static void android_server_SystemServer_init1(JNIEnv* env, jobject clazz),而android_server_SystemServer_init1会调用system_init();
system_init()将调用在 System_init.cpp 中
extern "C" status_t system_init()
{
......
runtime->callStatic("com/android/server/SystemServer",
SystemService.java 中的 init2。
......
}

 

在init2中会建立一个服务线程,我们可以看一下:

class ServerThread extends Thread {

...

public void run()
{

...

wm = WindowManagerService.main(context, power,
                    factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL);
            ServiceManager.addService(Context.WINDOW_SERVICE, wm);

            ((ActivityManagerService)ServiceManager.getService("activity"))
                    .setWindowManager(wm);
...

}

}

在这个线程中调用了很多的service,我们主要关注的是WindowMangerService接下来跳转到WindowMangerService分析。

 

二.WindowMangerService

代码路径:services/java/com/android/server/WindowManagerService.java

 

SystemService.java调到WindowManagerService 的main方法:

public static WindowManagerService main(Context context,
            PowerManagerService pm, boolean haveInputMethods) {
        WMThread thr = new WMThread(context, pm, haveInputMethods);
        thr.start();
        Slog.e("yaojuntaoWindowManagerService","WindowManagerService thr ok");

        synchronized (thr) {
            while (thr.mService == null) {
                try {
                    thr.wait();
                } catch (InterruptedException e) {
                }     
            }     
        }     

      
        return thr.mService;
    }
在main中建立了线程:

static class WMThread extends Thread {

..

 public void run() {

...

WindowManagerService s = new WindowManagerService(mContext, mPM,
                    mHaveInputMethods);
...

}

建立了 WindowManagerService的对象,将跳到构造函数:

 private WindowManagerService(Context context, PowerManagerService pm,
            boolean haveInputMethods) {

........

int max_events_per_sec = 35;
        try {
            max_events_per_sec = Integer.parseInt(SystemProperties
                    .get("windowsmgr.max_events_per_sec"));
            if (max_events_per_sec < 1) {
                max_events_per_sec = 35;
            }     
        } catch (NumberFormatException e) {
        }     
        mMinWaitTimeBetweenTouchEvents = 1000 / max_events_per_sec;

         private WindowManagerService(Context context, PowerManagerService pm,
            boolean haveInputMethods) {
mQueue = new KeyQ();

        mInputThread = new InputDispatcherThread();

        PolicyThread thr = new PolicyThread(mPolicy, this, context, pm);
        thr.start();
.........

}

在该段代码中我们看到mQueue = new KeyQ(),该句建立了输入的队列,通过这个去读底部传上来的数据,我们暂时不向下看,在mInputThread = new InputDispatcherThread()一句中建立了数据的分发线程,我们来看一下:

private final class InputDispatcherThread extends Thread {

......

public void run() {

while (true) {
                try {
                    process();
                } catch (Exception e) {
                    Slog.e(TAG, "Exception in input dispatcher", e);
                }
            }
        }
....

}

调用了 process(),好,继续看 process():

private void process() {

....

while (true) {

....

 

QueuedEvent ev = mQueue.getEvent(
                    (int)((!configChanged && curTime < nextKeyTime)
                            ? (nextKeyTime-curTime) : 0));
......

}

}

我们看到在process()中进入了循环,在循环中调用mQueue.getEvent从queue中获得数据并分发,mQueue这个对象在上面建立的,我们会看到这个对象的方法与属性,在此我们可以看到输入的上层的流程,紧接着向下走。


三.KeyInputQueue.java

 

代码:services/java/com/android/server/KeyInputQueue.java

在上次提到mQueue = new KeyQ();这行,而在WindowMangerService中有:

private class KeyQ extends KeyInputQueue,然后我们找到 KeyInputQueue的构造函数:

KeyInputQueue(Context context, HapticFeedbackCallback  hapticFeedbackCallback) {

.....

mThread.start();

....

}

我们看一下mThread这个线程:

Thread mThread = new Thread("InputDeviceReader") {
        public void run() {
...

while (true) {
                try {
                    InputDevice di;

                    // block, doesn't release the monitor
                    readEvent(ev);
....

}

}

}}

可以看到在该线程中循环通过readEvent(ev)去读事件,而private static native boolean readEvent(RawInputEvent outEvent)说明调用为JNI中的函数,另为我们发现QueuedEvent getEvent(long timeoutMS) 方法被调用时,值传了上去。

 

四.JNI

代码:

services/jni/com_android_server_KeyInputQueue.cpp

com_android_server_KeyInputQueue.cpp中我们发现了:

static jboolean
android_server_KeyInputQueue_readEvent(JNIEnv* env, jobject clazz,
                                          jobject event)
{

....

bool res = hub->getEvent(&deviceId, &type, &scancode, &keycode,
            &flags, &value, &when);
.....

}

我们来看hub->getEven,代码路径在libs/ui/EventHub.cpp

 

bool EventHub::getEvent(int32_t* outDeviceId, int32_t* outType,
        int32_t* outScancode, int32_t* outKeycode, uint32_t *outFlags,
        int32_t* outValue, nsecs_t* outWhen)
{

....

 if (!mOpened) {
        mError = openPlatformInput() ? NO_ERROR : UNKNOWN_ERROR;
        mOpened = true;
    }  
......

}

penPlatformInput()将扫描/dev/input 下的所有 event 并打开它
bool EventHub::openPlatformInput(void)
{
......
res = scan_dir(device_path);//其中 static const char *device_path = "/dev/input";
......
}


int EventHub::scan_dir(const char *dirname)
{
char devname[PATH_MAX];
char *filename;
DIR *dir;
struct dirent *de;
dir = opendir(dirname);
if(dir == NULL)
return -1;
strcpy(devname, dirname);
filename = devname + strlen(devname);
*filename++ = '/';
//扫描/dev/input 下的所有 event 并打开它
while((de = readdir(dir))) {
if(de->d_name[0] == '.' &&
(de->d_name[1] == '/0' ||
(de->d_name[1] == '.' && de->d_name[2] == '/0')))
continue;
strcpy(filename, de->d_name);
open_device(devname);//打开 event 设备
}
closedir(dir);
return 0;
}
int EventHub::open_device(const char *deviceName)
{
......
fd = open(deviceName, O_RDWR);
......
}
如果上面的操作都成功则把所有设备都打开了,现回到 EventHub::getEvent。
release_wake_lock(WAKE_LOCK_ID);
pollres = poll(mFDs, mFDCount, -1);
acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
在这边 poll,如果没有新事件将在这等待,如果有则开始下面的读事件
res = read(mFDs[i].fd, &iev, sizeof(iev));
到此整个从上面开始的读过程结束。

 

到此Android的输入流程已结束,接下来会详细刨析输入的细节。