一、概述
IMS分为Java层与Native层两个部分,其启动过程是从Java部分的初始化开始,进而完成Native部分的初始化。 IMS在SystemServer.startOtherServices()方法中启动的。IMS的诞生分为两个阶段:
- · 创建新的IMS对象。
- 调用IMS对象的start()函数完成启动。
我们先看下整个启动过程的序列图,然后根据序列图来一步步分析。
二、系统启动IMS服务过程
1、 SystemServer.startOtherServices()
[->frameworks/base/services/java/com/android/server/SystemServer.java]
private void startOtherServices() {
......
try {
......
// ① 新建IMS对象。
traceBeginAndSlog("StartInputManagerService");
inputManager = new InputManagerService(context);
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
traceBeginAndSlog("StartWindowManagerService");
wm = WindowManagerService.main(context, inputManager,
mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
!mFirstBoot, mOnlyCore);
//将WindowManagerService加入到ServiceManager中
ServiceManager.addService(Context.WINDOW_SERVICE, wm);
//将InputManagerService加入到ServiceManager中
ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
mActivityManagerService.setWindowManager(wm);
// 设置向WMS发起回调的callback对象
inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
// ② 正式启动IMS
inputManager.start();
}
}
在SystemServer中先构造了一个InputManagerService对象和一个WindowManagerService对象,然后将InputManagerService对象传给WindowManagerService对象,WindowManagerService中初始化了一个InputMonitor对象,调用InputManagerService.setWindowManagerCallbacks函数将InputMonitor传进去,后面native层回调时会调用到该InputMonitor对象。
2、 InputManagerService()
[->frameworks/base/services/core/java/com/android/server/input/InputManagerService.java]
public InputManagerService(Context context) {
this.mContext = context;
//注意这里拿了DisplayThread的Handler,意味着IMS中的消息队列处理都是在单独的DisplayThread中进行的。
//它是系统中共享的单例前台线程,主要用作输入输出的处理用。这样可以使用户体验敏感的处理少受其它工作的影响,减少延时。
this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());
//调用nativeInit来执行C++层的初始化操作
mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
LocalServices.addService(InputManagerInternal.class, new LocalService());
}
3、 InputManagerService.nativeInit()
[->frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp]
static jlong nativeInit(JNIEnv* env, jclass /* clazz */,
jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
......
// 新建了一个NativeInputManager对象,NativeInputManager,此对象将是Native层组件与
//Java层IMS进行通信的桥梁
NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
messageQueue->getLooper());
im->incStrong(0);
// 返回了NativeInputManager对象的指针给Java层的IMS,IMS将其保存在mPtr成员变量中
return reinterpret_cast<jlong>(im);
}
这个函数主要作用是创建一个NativeInputManager实例,并将其作为返回值保存在InputManagerService.java中的mPtr字段中。
4、NativeInputManager()
[->frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp]
NativeInputManager::NativeInputManager(jobject contextObj,
jobject serviceObj, const sp<Looper>& looper) :
mLooper(looper), mInteractive(true) {
// 出现重点了, NativeInputManager创建了EventHub
//构造一个EventHub对象,最原始的输入事件都是通过它收集并且粗加工然后给到InputReader对象
sp<EventHub> eventHub = new EventHub();
// 接着创建了Native层的InputManager
mInputManager = new InputManager(eventHub, this, this);
}
NativeInputManager构造函数中创建了一个EventHub实例(稍后会详细介绍),并且将这个实例作为参数来创建一个InputManager对象,这个对象会做一些初始化的操作。
5、InputManager()
[->frameworks/native/services/inputflinger/InputManager.cpp]
InputManager::InputManager(
const sp<EventHubInterface>& eventHub,
const sp<InputReaderPolicyInterface>& readerPolicy,
const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
mDispatcher = new InputDispatcher(dispatcherPolicy);
mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
initialize();
}
这里创建了InputDispatcher对象用于分发按键给当前focus的窗口的,同时创建了一个InputReader用于从EventHub中读取事件。
6、InputManager.initialize()
[->frameworks/native/services/inputflinger/InputManager.cpp]
void InputManager::initialize() {
// 创建供InputReader运行的线程InputReaderThread
mReaderThread = new InputReaderThread(mReader);
// 创建供InputDispatcher运行的线程InputDispatcherThread
mDispatcherThread = new InputDispatcherThread(mDispatcher);
}
这里创建了一个InputReaderThread和InputDispatcherThread对象,前面构造函数中创建的InputReader实际上是通过InputReaderThread来读取事件,而InputDispatcher实际通过InputDispatcherThread来分发事件,至此,IMS的创建完成了。在这个过程中,输入系统的重要参与者均完成创建。
IMS的成员关系
三、IMS启动
IMS启动主要是将前面创建的InputReaderThread和InputDispatcherThread启动起来
1、InputManagerService.start()
[->frameworks/base/services/core/java/com/android/server/input/InputManagerService.java]
public void start() {
Slog.i(TAG, "Starting input manager");
nativeStart(mPtr);
...
}
该函数主要调用了nativeStart进入native层启动
2. InputManagerService.nativeStart()
[->frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp]
static void nativeStart(JNIEnv* env, jclass /* clazz */, jlong ptr) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
status_t result = im->getInputManager()->start();
}
进入native层InputManager的start函数
3、InputManager.start()
[->frameworks/native/services/inputflinger/InputManager.cpp]
status_t InputManager::start() {
status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
return OK;
}
这个函数实际启动了一个InputReaderThread和InputDispatcherThread来从读取和分发键盘消息,调用它们的run方法后,就会进入threadLoop函数中,只要threadLoop函数返回true,该函数就会循环执行。
InputReaderThread不断调用InputReader的pollOnce()->getEvents()函数来得到事件,这些事件可以是输入事件,也可以是由inotify监测到设备增减变更所触发的事件,稍后会详细介绍。
4、InputReaderThread.threadLoop()
[->frameworks/native/services/inputflinger/InputReader.cpp]
bool InputReaderThread::threadLoop() {
mReader->loopOnce();
return true;
}
这里调用前面创建的InputReaderThread对象的loopOnce进行一次线程循环
5、InputReaderThread.loopOnce()
[->frameworks/native/services/inputflinger/InputReader.cpp]
void InputReader::loopOnce() {
......
/* ① 通过EventHub抽取事件列表。读取的结果存储在参数mEventBuffer中,返回值表示事件的个数
当EventHub中无事件可以抽取时,此函数的调用将会阻塞直到事件到来或者超时 */
size_tcount = mEventHub->getEvents(timeoutMillis,mEventBuffer, EVENT_BUFFER_SIZE);
{
AutoMutex _l(mLock);
......
if(count) {
// ② 如果有抽得事件,则调用processEventsLocked()函数对事件进行加工处理
processEventsLocked(mEventBuffer, count);
}
......
}
......
/* ③ 发布事件。 processEventsLocked()函数在对事件进行加工处理之后,便将处理后的事件存储在
mQueuedListener中。在循环的最后,通过调用flush()函数将所有事件交付给InputDispatcher */
mQueuedListener->flush();
}
InputReader的一次线程循环的工作思路比较清晰,一共三步:
- 首先从EventHub中抽取未处理的事件列表。这些事件分为两类,一类是从设备节点中读取的原始输入事件,另一类则是输入设备可用性变化事件,简称为设备事件。
- 通过processEventsLocked()对事件进行处理。对于设备事件,此函数对根据设备的可用性加载或移除设备对应的配置信息。对于原始输入事件,则在进行转译、封装与加工后将结果暂存到mQueuedListener中。
- 所有事件处理完毕后,调用mQueuedListener.flush()将所有暂存的输入事件一次性地交付给InputDispatcher。
这便是InputReader的总体工作流程。而我们接下来将详细讨论这三步的实现。
6、InputDispatcherThread.threadLoop()
InputDisptacher的主要任务是把收到的输入事件发送到PhoneWIndowManager或App端的焦点窗口上,稍后详细介绍。
[->frameworks/native/services/inputflinger/InputDispatcher.cpp]
bool InputDispatcherThread::threadLoop() {
mDispatcher->dispatchOnce();
return true;
}
这里调用前面创建的InputDispatcher对象的dispatchOnce函数进行一次按键分发
7、InputDispatcher.dispatchOnce()
[->frameworks/native/services/inputflinger/InputDispatcher.cpp]
void InputDispatcher::dispatchOnce() {
nsecs_t nextWakeupTime = LONG_LONG_MAX;
{ // acquire lock
AutoMutex _l(mLock);
mDispatcherIsAliveCondition.broadcast();
if (!haveCommandsLocked()) {
dispatchOnceInnerLocked(&nextWakeupTime);
}
if (runCommandsLockedInterruptible()) {
nextWakeupTime = LONG_LONG_MIN;
}
} // release lock
// Wait for callback or timeout or wake. (make sure we round up, not down)
nsecs_t currentTime = now();
int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);
mLooper->pollOnce(timeoutMillis);
}
上述函数主要是调用dispatchOnceInnerLocked来进行一次按键分发,当没有按键消息时会走到mLooper->pollOnce(timeoutMillis);这个函数会进入睡眠状态,当有按键消息发生时该函数会返回,然后走到dispatchOnceInnerLocked函数。这里mLooper->pollOnce为何会睡眠涉及到Android的Handler机制。
四、小结:
完成IMS的创建之后,InputManagerService.start()函数以启动IMS。InputManager的创建过程分别为InputReader与InputDispatcher创建了承载它们运行的线程,然而并未将这两个线程启动,因此IMS的各员大将仍处于待命状态。此时start()函数的功能就是启动这两个线程,使得InputReader于InputDispatcher开始工作。
当两个线程启动后,InputReader在其线程循环中不断地从EventHub中抽取原始输入事件,进行加工处理后将加工所得的事件放入InputDispatcher的派发发队列中。InputDispatcher则在其线程循环中将派发队列中的事件取出,查找合适的窗口,将事件写入到窗口的事件接收管道中。窗口事件接收线程的Looper从管道中将事件取出,交由事件处理函数进行事件响应。整个过程共有三个线程首尾相接,像三台水泵似的一层层地将事件交付给事件处理函数。如下图所示。
InputManagerService.start()函数的作用,就像为Reader线程、Dispatcher线程这两台水泵按下开关,而Looper这台水泵在窗口创建时便已经处于运行状态了。自此,输入系统动力十足地开始运转,设备节点中的输入事件将被源源不断地抽取给事件处理者。