Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to use Input Subsystem to listen for Thread start in Android

2025-01-19 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)05/31 Report--

This article mainly explains "how to use the Input subsystem to monitor the start of threads in Android". The explanation in this article is simple and clear, easy to learn and understand. Please follow the ideas of Xiaobian slowly and deeply to study and learn "how to use the Input subsystem to monitor the start of threads in Android" together!

Overview of InputManagerService initialization

First of all, there are a few points we can all agree on:

Services (Java) at the Android Framework layer are created by the system_server process (since there is no fork, they all run in the system_server process)

Service is created and managed by ServiceManager running in the system_server process.

So for the creation of InputManagerService, we can find it in SystemServer's startOtherServices() method, which does the following:

Create an InputManagerService object

Leave it to ServiceManager to manage

Register the InputMonitor of WindowManagerService into InputManagerService as a callback after a window response event

Start InputManagerService after completing the above tasks.

SystemServer.javastartOtherServices(){ …… inputManager = new InputManagerService(context); …… inputManager.setWindowManagerCallbacks(wm.getInputMonitor()); inputManager.start(); …… }

Then we learn the corresponding processing part by part.

InputManagerService object creation

Creating an InputManagerService object does the following:

Create a Handler responsible for handling Messages in DisplayThread threads

Call nativeInit to initialize the InputManagerService of the native layer. When initializing, the message queue of DisplayThread is passed.

Save InputManagerService of native layer with mPtr

After initialization, add Service to LocalServices and store it as key-value pairs through Map

InputManagerService.javapublic InputManagerService(Context context) { this.mContext = context; this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper()); mUseDevInputEventForAudioJack = context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack); Slog.i(TAG, "Initializing input manager, mUseDevInputEventForAudioJack=" + mUseDevInputEventForAudioJack); mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue()); LocalServices.addService(InputManagerInternal.class, new LocalService()); }

Some people may ask, why is InputManagerService bound to DisplayThread? You may wish to think about it, no matter how InputEvent is acquired, classified, distributed, and ultimately processed, which means that the final processing results of it must be reflected on the UI, so InputManagerService naturally has to choose to be close to the UI.

But the problem comes again, the application is running in its own main thread, is it InputManagerService to bind one by one, or poll one by one? These practices are too inefficient, so instead, can you bind them together with a thread that manages or is very close to all application UI?

What is the answer, I will not say here, you can use your own knowledge to think.

Initialize InputManagerService of native layer

In the nativeInit function, convert Java layer MessageQueue to native layer MessageQueue, and then fetch Looper for initialization of NativeInputManager. The highlight here is the creation of NativeInputManager, which does the following:

Convert Context and InputManagerService of Java layer to Context and InputManagerService of native layer and store them in mContextObj and mServiceObj

initialize variables

Create EventHub

Creating an InputManager

com_android_server_input_InputManagerService.cpp NativeInputManager::NativeInputManager(jobject contextObj, jobject serviceObj, const sp& looper) : mLooper(looper), mInteractive(true) { JNIEnv* env = jniEnv(); mContextObj = env->NewGlobalRef(contextObj); mServiceObj = env->NewGlobalRef(serviceObj); { AutoMutex _l(mLock); mLocked.systemUiVisibility = ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE; mLocked.pointerSpeed = 0; mLocked.pointerGesturesEnabled = true; mLocked.showTouches = false; } mInteractive = true; sp eventHub = new EventHub(); mInputManager = new InputManager(eventHub, this, this); }

EventHub

A lot of people think, what is EventHub? In English, it means pivot of events. As we mentioned at the beginning of this article, the events of the Input system originate from the driver/kernel, so we can guess that EventHub is the hub for handling meta-events from the driver/kernel. Let's test our idea in the source code.

EventHub was created by doing the following:

Create mEpollFd to listen for readable data (with or without events)

Create mINotifyFd, register it in the DEVICE_PATH(where the path is/dev/input) node, and give it to the kernel to listen for add/delete data events on this device node. epoll_wait() returns whenever a data deletion event arrives, enabling EventHub to receive notifications from the system and obtain event details.

Call epoll_ctl to register mEpollFd and mINotifyFd in epoll

int wakeFd[2] is defined as both read and write ends of the event transmission pipeline, and the read end is registered in epoll so that mEpollFd listens.

EventHub.cpp EventHub::EventHub(void) : mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD), mNextDeviceId(1), mControllerNumbers(), mOpeningDevices(0), mClosingDevices(0), mNeedToSendFinishedDeviceScan(false), mNeedToReopenDevices(false), mNeedToScanDevices(true), mPendingEventCount(0), mPendingEventIndex(0), mPendingINotify(false) { acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID); mEpollFd = epoll_create(EPOLL_SIZE_HINT); LOG_ALWAYS_FATAL_IF(mEpollFd

< 0, "Could not create epoll instance. errno=%d", errno); mINotifyFd = inotify_init(); int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE); …… result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem); …… int wakeFds[2]; result = pipe(wakeFds); …… mWakeReadPipeFd = wakeFds[0]; mWakeWritePipeFd = wakeFds[1]; result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK); …… result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK); …… result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem); …… } 那么这里抛出一个问题:为什么要把管道的读端注册到epoll中?假如EventHub因为getEvents读不到事件而阻塞在epoll_wait()里,而我们没有绑定读端的话,我们要怎么唤醒EventHub?如果绑定了管道的读端,我们就可以通过向管道的写端写数据从而让EventHub因为得到管道写端的数据而被唤醒。 InputManager的创建 接下来继续说InputManager的创建,它的创建就简单多了,创建一个InputDispatcher对象用于分发事件,一个InputReader对象用于读事件并把事件交给InputDispatcher分发,,然后调用initialize()初始化,其实也就是创建了InputReaderThread和InputDispatcherThread。 InputManager.cpp InputManager::InputManager( const sp& eventHub, const sp& readerPolicy, const sp& dispatcherPolicy) { mDispatcher = new InputDispatcher(dispatcherPolicy); mReader = new InputReader(eventHub, readerPolicy, mDispatcher); initialize(); }void InputManager::initialize() { mReaderThread = new InputReaderThread(mReader); mDispatcherThread = new InputDispatcherThread(mDispatcher); } InputDispatcher和InputReader的创建都相对简单。InputDispatcher会创建自己线程的Looper,以及设置根据传入的dispatchPolicy设置分发规则。InputReader则会将传入的InputDispatcher封装为监听对象存起来,做一些数据初始化就结束了。 至此,InputManagerService对象的初始化就完成了,根据开头说的,接下来就会调用InputManagerService的start()方法。 监听线程InputReader和InputDispatcher的启动 在start()方法中,做了以下事情: 调用nativeStart方法,其实就是调用InputManager的start()方法 将InputManagerService交给WatchDog监控 注册触控点速度、显示触控的观察者,并注册广播监控它们 主动调用updateXXX方法更新(初始化) InputManagerService.javapublic void start() { Slog.i(TAG, "Starting input manager"); nativeStart(mPtr); // Add ourself to the Watchdog monitors. Watchdog.getInstance().addMonitor(this); registerPointerSpeedSettingObserver(); registerShowTouchesSettingObserver(); registerAccessibilityLargePointerSettingObserver(); mContext.registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { updatePointerSpeedFromSettings(); updateShowTouchesFromSettings(); updateAccessibilityLargePointerFromSettings(); } }, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler); updatePointerSpeedFromSettings(); updateShowTouchesFromSettings(); updateAccessibilityLargePointerFromSettings(); } 显而易见这里最值得关注的就是InputManager的start()方法了,可惜这个方法并不值得我们如此关心,因为它做的事情很简单,就是启动InputDispatcherThread和InputReaderThread开始监听。 status_t InputManager::start() { status_t result = mDispatcherThread->

run("InputDispatcher", PRIORITY_URGENT_DISPLAY); if (result) { ALOGE("Could not start InputDispatcher thread due to error %d. ", result); return result; } result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY); if (result) { ALOGE("Could not start InputReader thread due to error %d. ", result); mDispatcherThread->requestExit(); return result; } return OK; }

So how is the InputReaderThread thread associated with EventHub?

For InputReadThread:

After startup loop executes mReader->loopOnce()

LoopOnce() calls mEventHub->getEvents to read events

When you read an event, you call processEventsLocked to process the event.

After processing is complete, call getInputDevicesLocked to get input device information

Call the mPolicy->notifyInputDevicesChanged function to use the proxy of InputManagerService to send MSG_DELIVER_INPUT_DEVICES_CHANGED message through Handler to notify that the input device has changed

*** Call mQueuedListener->flush() to pass all events in the event queue to the InputDispatcher registered in InputReader

bool InputReaderThread::threadLoop() { mReader->loopOnce(); return true; }void InputReader::loopOnce() { …… size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE); { // acquire lock AutoMutex _l(mLock); mReaderIsAliveCondition.broadcast(); if (count) { processEventsLocked(mEventBuffer, count); } …… if (oldGeneration != mGeneration) { inputDevicesChanged = true; getInputDevicesLocked(inputDevices); } } // release lock // Send out a message that the describes the changed input devices. if (inputDevicesChanged) { mPolicy->notifyInputDevicesChanged(inputDevices); } …… mQueuedListener->flush(); } Thank you for reading, the above is "how to use the Input subsystem in Android to listen to the start of the thread" content, after learning this article, I believe everyone on Android how to use the Input subsystem to listen to the start of the thread This problem has a deeper understanding, the specific use of the situation also needs to be verified by practice. Here is, Xiaobian will push more articles related to knowledge points for everyone, welcome to pay attention!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report