现在的位置: 首页 > 综合 > 正文

Android4.0 input touch解析

2013年10月16日 ⁄ 综合 ⁄ 共 7141字 ⁄ 字号 评论关闭

前言

在网上看到好多关于android input device流程分析,但是都不全,有的只是从linux内核那边分析,有的从android上层分析,而且分析的代码也比较老,都是在android2.3以下,最近在做android4.0下的多点触摸以及校准程序,多点触摸的驱动很好写,在linux内核里面都有现成的例子,照着改就可以了。但是android下的校准程序比较复杂,一种是在android

Framework层进行,一种是在linux 内核层进行。

对于校准程序来说,需要全屏校准。但是在android4.0下面,下面的导航栏是systemui画的,无法去掉,因此在校准程序里面通过display得到分辨率高度比实际的小,差的那部分就是导航栏的高度。如果以小的高度进行校准,但使用实际的高度进行触摸坐标到屏幕坐标转换,就会导致触摸点偏下的问题。

为了解决这个问题,在网上找了很多资料,第一种就是想办法在校准程序里面得到整个屏幕的分辨率,进而让校准程序全屏显示,即把导航栏隐藏,在网上看到又网友用下面例子实现:

//for phone

getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);

//for pad View.SYSTEM_UI_FLAG_SHOW_FULLSCREEN= 4

getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_SHOW_FULLSCREEN);

经过自己实验,这两个都无法隐藏下面的导航栏,而且在最新的sdk里面也没有SYSTEM_UI_FLAG_SHOW_FULLSCREEN的定义。第二种就是在jni种通过fb0得到系统的分辨率,这个是真实的分辨率,这种方法需要apkroot或者graphics组权限,才能打开fb0,而且android4.0根据触摸屏类型是否使用外部显示分辨率,如果使用外部display的话,那么就不能用fb0的分辨率。为了解决这个问题,把整个input touch流程都看了一边。废话少说,进入正题。

1 android input touch流程

Android inout touch流程分两部分,一部分是从android framework开始,如何读取touch设备的事件并分发。一部分是从linux 内核开始,如何从触摸屏读取触摸坐标并送给touch设备。

2 android framework层

2.1 文件结构

首先看看Event Input文件结构吧,在frameworks/base/services/input之下

2.2 模块介绍

Eventhub

它是系统中所有事件的中央处理站。它管理所有系统中可以识别的输入设备的输入事件,此外,当设备增加或删除时,EventHub将产生相应的输入事件给系统。EventHub通过getEvents函数,给系统提供一个输入事件流。它也支持查询输入设备当前的状态(如哪些键当前被按下)。而且EventHub还跟踪每个输入调入的能力,比如输入设备的类别,输入设备支持哪些按键。

InputReader

InputReader从EventHub中读取原始事件数据(RawEvent),并由各个InputMapper处理之后输入对应的input listener.InputReader拥有一个InputMapper集合。它做的大部分工作在InputReader线程中完成,但是InputReader可以接受任意线程的查询。为了可管理性,InputReader使用一个简单的Mutex来保护它的状态。InputReader拥有一个EventHub对象,但这个对象不是它创建的,而是在创建InputReader时作为参数传入的。

InputDispatcher

InputDispatcher负责把事件分发给输入目标,其中的一些功能(如识别输入目标)由独立的policy对象控制。

InputManager

InputManager是系统事件处理的核心,它虽然不做具体的事,但管理工作还是要做的,比如接受我们客户的投诉和索赔要求,或者老板的出筒。

InputManager使用两个线程:

 1)InputReaderThread叫做"InputReader"线程,它负责读取并预处理RawEvent,applies policy并且把消息送入DispatcherThead管理的队列中。

2)InputDispatcherThread叫做"InputDispatcher"线程,它在队列上等待新的输入事件,并且异步地把这些事件分发给应用程序。

InputReaderThread类与InputDispatcherThread类不共享内部状态,所有的通信都是单向的,从InputReaderThread到InputDispatcherThread。两个类可以通过InputDispatchPolicy进行交互。

InputManager类从不与Java交互,而InputDispatchPolicy负责执行所有与系统的外部交互,包括调用DVM业务。

看看下图理解input下面几个模块的关系

2.3线程创建

SystemServer大家熟悉吧,它是android init进程启动的,它的任务就是启动android里面很多服务,并管理起来,如果大家不熟悉,请参考andorid启动流程分析

SystemServer.java (frameworks\base\services\java\com\android\server)里面

ServerThread::run调用

    Slog.i(TAG, "Window Manager");

            wm = WindowManagerService.main(context, power,

                    factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL,

                    !firstBoot);

            ServiceManager.addService(Context.WINDOW_SERVICE, wm);

WindowManagerService.java (frameworks\base\services\java\com\android\server\wm)里面

WindowManagerService main调用

WMThread thr = new WMThread(context, pm, haveInputMethods, allowBootMsgs);

        thr.start();

接着调用

WMThread:: run调用

WindowManagerService s = new WindowManagerService(mContext, mPM,

                    mHaveInputMethods, mAllowBootMessages);

接着在WindowManagerService里面调用

mInputManager = new InputManager(context, this);

至此我们创建了一个javainput设备管理器

InputManager.java (frameworks\base\services\java\com\android\server\wm)里面

InputManager调用

       nativeInit(mContext, mCallbacks, looper.getQueue());

从下面开始就进入native空间

com_android_server_InputManager.cpp (frameworks\base\services\jni)里面

nativeInit对应android_server_InputManager_nativeInit调用

gNativeInputManager = new NativeInputManager(contextObj, callbacksObj, looper);

NativeInputManager里面调用

sp<EventHub> eventHub = new EventHub();

    mInputManager = new InputManager(eventHub, this, this);

这个函数创建一个EventHub对象,然后把它作为参数来创建InputManager对象。特别注意,InputManager是在C++里,具体在InputManager.cpp里。EventHub类在EventHub.cpp里,这个类和input事件获取有关。

至此我们创建了一个nativeinput设备管理器,具体作用见上面说明

首先是去InputManager.cpp (frameworks\base\services\input)文件里面

InputManager::InputManager调用

         mDispatcher = new InputDispatcher(dispatcherPolicy);

    mReader = new InputReader(eventHub, readerPolicy, mDispatcher);

    initialize();

它创建了InputDispatcher对象,同时也创建了InputReader对象。并分别暂存于mDispatcher和mReader变量中。注意eventHub和mDispatcher都作为参数创建InputReader对象。后面还用initialize来初始化。下面是initialize函数的定义:

void InputManager::initialize() {

    mReaderThread = new InputReaderThread(mReader);

    mDispatcherThread = new InputDispatcherThread(mDispatcher);

}

它创建两个线程对象,一个是InputReaderThread线程对象,负责input事件的获取;另一个是InputDispatcherThread线程对象,负责input消息的发送。

注:以上两个线程对象都有自己的threadLoop函数,它将在Thread::_threadLoop中被调用,这个Thread::_threadLoop是线程入口函数,线程在Thread::run中被真正地创建

InputDispatcher.cpp (frameworks\base\services\input)里面

InputDispatcher::InputDispatcher做一些准备工作

InputReader.cpp (frameworks\base\services\input)里面

InputReader::InputReader做一些准备工作

2.4线程启动

在上面讲到在WindowManagerService里面调用

mInputManager = new InputManager(context, this);

创建input 管理器

紧接着调用

mInputManager.start();

InputManager.java (frameworks\base\services\java\com\android\server\wm)里面

start调用

       Slog.i(TAG, "Starting input manager");

       nativeStart();

从下面开始就进入native空间

com_android_server_InputManager.cpp (frameworks\base\services\jni)里面

nativeStart对应android_server_InputManager_nativeStart调用

status_t result = gNativeInputManager->getInputManager()->start();

InputManager.cpp (frameworks\base\services\input)文件里面

InputManager::start调用

status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);

result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);

上面两个线程对象是Thread子类,于是继承它的run方法,在Thread::run中,调用createThreadEtc函数,并以Thread::_threadLoop作为入口函数,以上面的mDispatcherThread或mReaderThread作为userdata创建线程,然后会调用threadLoop(),在Thread类中它是虚函数,得由子类来复写

因此会调用InputReader.cpp (frameworks\base\services\input)里面的threadLoop InputReaderThread::threadLoop调用

mReader->loopOnce();

mReader就是上面创建的inputreader对象,作为参数传给mReaderThread

InputReader::loopOnce调用

count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);

得到input 输入事件

processEventsLocked

处理input输入事件

因此会调用InputDispatcher.cpp (frameworks\base\services\input)里面的threadLoop InputDispatcherThread::threadLoop调用

mDispatcher->dispatchOnce ();

mDispatcher就是上面创建的InputDispatcher对象,作为参数传给mDispatcherThread

InputDispatcher::dispatchOnce调用

     dispatchOnceInnerLocked(&nextWakeupTime)

dispatchOnceInnerLocked函数处理input输入消息,mLooper->pollOnce是等待下一次输入事件。

mLooper->pollOnce(timeoutMillis):

这个请看Looper.cpp文件中的Looper::pollOnce()函数。Looper里主要通过linux管道方式实现进程间通信,通过epoll机制实现外界事件请求作出响应。

至此整个android input event框架已经运转起来了,好像到现在还没有提到touch,别着急,且看下面的分析

2.5 event初始化

还记得android_server_InputManager_nativeInit里面创建

sp<EventHub> eventHub = new EventHub();

EventHub.cpp (frameworks\base\services\input)里面

EventHub::EventHub初始化

mOpeningDevices(0)  表示需要打开的设备链表,为NULL

mClosingDevices(0)  表示需要关闭的设备链表,为NULL

mNeedToSendFinishedDeviceScan(false) 表示需要发送设备扫描完成,默认为0

mNeedToReopenDevices(false) 表示需要重新打开设备,默认为0

mNeedToScanDevices(true) 表示需要扫描设备,默认为1

mPendingEventCount(0) 表示需要处理event个数,默认为0

mPendingEventIndex(0) 表示当前需要处理event的索引,默认为0

mPendingINotify(false) 表示需要处理的通知,默认为0

mEpollFd = epoll_create(EPOLL_SIZE_HINT);  epoll实例,在EventHub::EventHub中初始化此例,所有输入事件通过epoll_wait来获取

mINotifyFd = inotify_init();

int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);

创建mINotifyFd,用于监控/dev/input目录下删除和创建设备节点的事件

result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem)

抱歉!评论已关闭.