Android 输入系统

news2024/11/18 17:45:43

概述

Android 输入系统的工作原理概括来说,内核将原始事件写入到设备节点中,InputReader 不断地通过 EventHub 将原始事件取出来并翻译加工成 Android 输入事件,然后交给 InputDispatcher。

InputDispatcher 根据 WMS 提供的窗口信息将事件交给合适的窗口。窗口的 ViewRootImpl 对象再沿着控件树将事件派发给感兴趣的控件。控件对其收到的事件作出响应,更新自己的画面、执行特定的动作
在这里插入图片描述

概念解释

InputManagerService(IMS):Android 系统服务,分为 Java 层和 Native 层两部分,Java 层负责与 WMS 通信,而 Native 层则是 InputReader 和 InputDispatcher 两个输入系统关键组件的运行容器;
WindowManagerService(WMS):它并不是输入系统的一员,新建窗口时,WMS 为新窗口和 IMS 创建了事件传递所用的通道,会将窗口的可点击区域,焦点窗口等信息实时更新到 IMS 的 InputDispatcher 中,使得 InputDispatcher 可以正确将事件派发到指定窗口;
EventHub:直接访问所有的设备节点,通过 getEvents() 函数将所有输入系统相关的待处理的底层事件返回给使用者,包括原始输入事件,设备节点的增删等;
InputReader:运行在一个独立的线程中,负责管理输入设备的列表和配置,以及进行输入事件的加工处理,它通过其线程循环不断地通过 getEvents() 函数从 EventHub 中将事件取出并进行处理,对于设备节点的增删事件,它会更新输入设备列表与配置;对于原始输入事件,InputReader对其进行翻译,组装,封装为包含更多信息,更多可读性的输入事件,然后交给InputDispatcher进行派发;
InputDispatcher:运行于一个独立的线程中,InputDispatcher 中保管来自 WMS 的所有窗口的信息,收到 InputReader 的输入事件后,会在其保管的窗口中寻找合适的窗口,并将事件派发给此窗口;
ViewRootImpl:对某些窗口,如 SurfaceView 的窗口来说,窗口就是输入事件派发的终点,而对其他的如Activity,对话框等使用了 Android 控件系统的窗口来说,输入事件的终点是控件;
在这里插入图片描述

Input 系统流程

服务端

Input 系统初始化 IMS

SystemServer中 的 ServerThread 线程中启动 InputManagerService

  1. 创建 IMS 对象
    1. 创建 NativeInputManager 对象,此对象将是 Native 层组件与 Java 层 IMS 进行通信的桥梁
    2. 创建 Native 层的 InputManager,创建 InputDispatcher、InputReader、EventHub
  2. 将 IMS 添加到服务中心
  3. 调用 IMS 对象的 start() 函数完成 InputDispatcherThread 、InputReaderThread 的创建和启动
    1. InputReader 在其线程循环中不断地从 EventHub 中抽取原始输入事件,进行加工处理后将加工所得的事件放入 InputDispatcher 的派发发队列中
    2. InputDispatcher 则在其线程循环中将派发队列中的事件取出,查找合适的窗口,将事件写入到窗口的事件接收管道中
    3. 窗口事件接收线程的 Looper 从管道中将事件取出,交由事件处理函数进行事件响应
      暂时无法在飞书文档外展示此内容

源码分析

SystemServer.java {
    public static void main(String[] args) {
        new SystemServer().run();
    }
    @Override
    public void run() {
        startOtherServices(t);
    }
    private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
        InputManagerService inputManager = null;
        inputManager = new InputManagerService(context);
        // 将IMS发布给ServiceManager,以便其他人可以访问IMS提供的接口
        ServiceManager.addService(Context.INPUT_SERVICE,inputManager);
        // 设置向WMS发起回调的callback对象
        inputManager.setWindowManagerCallbacks(wm.getInputManagerCallback());
        // 正式启动IMS
        inputManager.start();
    }
}
InputManagerService.java {
    static class Injector {
        private final Context mContext;
        private final Looper mLooper;
        NativeInputManagerService getNativeService(InputManagerService service) {
            return new NativeInputManagerService.NativeImpl(service, mContext, mLooper.getQueue());
        }
    }
    InputManagerService(Injector injector) {
        mHandler = new InputManagerHandler(injector.getLooper());
        mNative = injector.getNativeService(this);
    }
    public void start() {
        Slog.i(TAG, "Starting input manager");
        mNative.start();
    }
}
NativeInputManagerService.java {
    class NativeImpl implements NativeInputManagerService {
        /** Pointer to native input manager service object, used by native code. */
        private final long mPtr;
        NativeImpl(InputManagerService service, Context context, MessageQueue messageQueue) {
            mPtr = init(service, context, messageQueue);
        }
        private native long init(InputManagerService service, Context context,MessageQueue messageQueue);
        public native void start();
    }
}

InputManagerService.cpp {
    static const JNINativeMethod gInputManagerMethods[] = {
        {"init","(Lcom/android/server/input/InputManagerService;Landroid/content/Context;Landroid/os/MessageQueue;)J",
         (void*)nativeInit},
        {"start", "()V", (void*)nativeStart},
    }
    static jlong nativeInit(JNIEnv* env, jclass /* clazz */,jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
        sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
        // 新建了一个 NativeInputManager 对象
        // 此对象将是 Native 层组件与 Java 层 IMS 进行通信的桥梁
        // 实现了 InputReaderPolicyInterface 与 InputDispatcherPolicyInterface 两个接口
        NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
                messageQueue->getLooper());
        return reinterpret_cast<jlong>(im);
    }
    static void nativeStart(JNIEnv* env, jobject nativeImplObj) {
        NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
        status_t result = im->getInputManager()->start();
    }
    static NativeInputManager* getNativeInputManager(JNIEnv* env, jobject clazz) {
        return reinterpret_cast<NativeInputManager*>(env->GetLongField(clazz,
                 gNativeInputManagerServiceImpl.mPtr));
    }
}
NativeInputManager.cpp {
    NativeInputManager::NativeInputManager(jobject contextObj,jobject serviceObj, const sp<Looper>& looper) :
            mLooper(looper), mInteractive(true) {
        // 创建 Native 层的 InputManager
        InputManager* im = new InputManager(this, this);
        mInputManager = im;
        defaultServiceManager()->addService(String16("inputflinger"), im);
    }
}
InputManager.cpp {
    InputManager::InputManager(const sp<InputReaderPolicyInterface>& readerPolicy,const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
        // 创建 InputDispatcher
        mDispatcher = createInputDispatcher(dispatcherPolicy);
        //这里的 mBlocker 即封装的 mDispatcher ,mDispatcher 实现了 InputListenerInterface 接口
        //通过 getListener().notifyMotion(&args) 通知 mDispatcher 处理事件
        mClassifier = std::make_unique<InputClassifier>(*mDispatcher);
        mBlocker = std::make_unique<UnwantedInteractionBlocker>(*mClassifier);
        // 创建 InputReader
        mReader = createInputReader(readerPolicy, *mBlocker);
    }
    status_t InputManager::start() {
        status_t result = mDispatcher->start();
        result = mReader->start();
        return OK;
    }
}
InputReaderFactory.cpp {
    std::unique_ptr<InputReaderInterface> createInputReader(const sp<InputReaderPolicyInterface>& policy, InputListenerInterface& listener) {
        // 这里会创建 EventHub ,并设置事件回调
        return std::make_unique<InputReader>(std::make_unique<EventHub>(), policy, listener);
    }
}
InputDispatcherFactory.cpp {
    std::unique_ptr<InputDispatcherInterface> createInputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) {
        return std::make_unique<android::inputdispatcher::InputDispatcher>(policy);
    }
}
InputDispatcher.cpp {
    status_t InputDispatcher::start() {
        // 创建供 InputDispatcher 运行的线程 InputDispatcherThread
        mThread = std::make_unique<InputThread>("InputDispatcher", 
            [this]() { dispatchOnce(); }, 
            [this]() { mLooper->wake(); }
        );
        return OK;
    }
}
InputReader.cpp {
    status_t InputReader::start() {
        // 创建供 InputReader 运行的线程 InputReaderThread
        mThread = std::make_unique<InputThread>("InputReader", 
            [this]() { loopOnce(); }, 
            [this]() { mEventHub->wake(); });
        return OK;
    }
}


事件读取(EventHub)与加工(InputReader)

EventHub

  • 避免不断地轮询这些描述符
  • 不需要为每个描述符创建独立的线程进行阻塞读取,避免了资源浪费的同时又可以获得较快的响应速度。

INotify:Linux 内核所提供的一种文件系统变化通知机制。可以监控文件系统的变化,如文件新建、删除、读写等

  • inotify_init() 创建一个 inotify 对象,返回一个 notifyFd。
  • inotify_add_watch 将一个或多个监听添加到 inotify 对象中。
  • read() 函数从 inotify 对象中读取监听事件

Epoll:监听多个描述符的可读/可写状态。等待返回时携带了可读的描述符

  • epoll_create(int max_fds) 创建一个 epoll 对象的描述符,之后对 epoll 的操作均使用这个描述符完成。max_fds 参数表示了此 epoll 对象可以监听的描述符的最大数量。
  • epoll_ctl (int epfd, int op,int fd, struct epoll_event *event) 用于管理注册事件的函数。这个函数可以增加/删除/修改事件的注册。
  • int epoll_wait(int epfd, structepoll_event * events, int maxevents, int timeout) 用于等待事件的到来。当此函数返回时,events 数组参数中将会包含产生事件的文件描述符。
EventHub.cpp {
    EventHub::EventHub(void) : mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD),mNextDeviceId(1),mControllerNumbers(),mNeedToSendFinishedDeviceScan(false),mNeedToReopenDevices(false),mNeedToScanDevices(true),mPendingEventCount(0),mPendingEventIndex(0),mPendingINotify(false) {
        //使用epoll_create()函数创建一个epoll对象,用来监听设备节点是否有数据可读(有无事件)
        mEpollFd = epoll_create1(EPOLL_CLOEXEC);
        //创建一个 inotify 对象 。这个对象将被用来 监听设备节点的增删事件
        mINotifyFd = inotify_init1(IN_CLOEXEC);
    
        std::error_code errorCode;
        bool isDeviceInotifyAdded = false;
        if (std::filesystem::exists("/dev/input", errorCode)) {
            // 将存储设备节点的路径 /dev/input 作为监听对象添加到inotify对象中
            // 当此文件夹下的设备节点发生创建与删除事件时,都可以通过mINotifyFd读取事件的详细信息
            mDeviceInputWd = inotify_add_watch(mINotifyFd, "/dev/input", IN_DELETE | IN_CREATE);
        }
        struct epoll_event eventItem = {};
        eventItem.events = EPOLLIN | EPOLLWAKEUP;
        eventItem.data.fd = mINotifyFd;
        // 将 mINotifyFd 作为 epoll 的一个监控对象
        // 当 inotify 事件到来时,epoll_wait() 将立刻返回
        // EventHub 便可从 mINotifyFd 中读取设备节点的增删信息,并作相应处理
        int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);
    }
}

InputReader

在这里插入图片描述

源码分析

InputReader.cpp {
    status_t InputReader::start() {
        mThread = std::make_unique<InputThread>("InputReader", 
            [this]() { loopOnce(); }, 
            [this]() { mEventHub->wake(); });
        return OK;
    }
    
    void InputReader::loopOnce() {
        int32_t oldGeneration;
        int32_t timeoutMillis;
        bool inputDevicesChanged = false;
        std::vector<InputDeviceInfo> inputDevices;
    
        // 读取事件,没事件时会 block
        size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
    
        { // acquire lock
            if (count) {
                processEventsLocked(mEventBuffer, count);
            }
        } // release lock
        mQueuedListener.flush();
    }
    void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
        for (const RawEvent* rawEvent = rawEvents; count;) {
            int32_t type = rawEvent->type;
            size_t batchSize = 1;
            if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {
                int32_t deviceId = rawEvent->deviceId;
                // 处理 输入 事件
                processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
            } else {
                switch (rawEvent->type) {
                    case EventHubInterface::DEVICE_ADDED:
                        // 处理 新增设备 事件
                        addDeviceLocked(rawEvent->when, rawEvent->deviceId);
                        break;
                    case EventHubInterface::DEVICE_REMOVED:
                        // 处理 删除设备 事件
                        removeDeviceLocked(rawEvent->when, rawEvent->deviceId);
                        break;
                    case EventHubInterface::FINISHED_DEVICE_SCAN:
                        // 处理 设备扫描完成 事件
                        handleConfigurationChangedLocked(rawEvent->when);
                        break;
                    default:
                        ALOG_ASSERT(false); // can't happen
                        break;
                }
            }
        }
    }
        
    void InputReader::processEventsForDeviceLocked(int32_t eventHubId, const RawEvent* rawEvents,size_t count) {
        auto deviceIt = mDevices.find(eventHubId);
        std::shared_ptr<InputDevice>& device = deviceIt->second;
        device->process(rawEvents, count);
    }
}
EventHub.cpp {
    size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
        struct input_event readBuffer[bufferSize];
        RawEvent* event = buffer;
        Device* device = getDeviceByFdLocked(eventItem.data.fd);
        while (mPendingEventIndex < mPendingEventCount) {
            // mPendingEventIndex 指定尚未处理的 epoll_event 的索引
            const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
            Device* device = getDeviceByFdLocked(eventItem.data.fd);
            for (;;) {
                nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
                // This must be an input event
                if (eventItem.events & EPOLLIN) {
                    // 根据设备id 读取事件到 readBuffer 中
                    int32_t readSize = read(device->fd, readBuffer, sizeof(struct input_event) * capacity);
                    int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
                    size_t count = size_t(readSize) / sizeof(struct input_event);
                    for (size_t i = 0; i < count; i++) {
                        struct input_event& iev = readBuffer[i];
                        event->when = processEventTimestamp(iev);
                        event->readTime = systemTime(SYSTEM_TIME_MONOTONIC);
                        event->deviceId = deviceId;
                        event->type = iev.type;
                        event->code = iev.code;
                        event->value = iev.value;
                        event += 1;
                        capacity -= 1;
                    }
                    break;
                } 
            }
            // 执行 epoll_wait() 函数等待新的事件到来
            int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
        }
        // All done, return the number of events we read.
        return event - buffer;
    }
}
InputDevice.cpp {
    void InputDevice::process(const RawEvent* rawEvents, size_t count) {
        for (const RawEvent* rawEvent = rawEvents; count != 0; rawEvent++) {
            for_each_mapper_in_subdevice(rawEvent->deviceId, [rawEvent](InputMapper& mapper) {
                mapper.process(rawEvent);
            });
            --count;
        }
    }
}
SingleTouchInputMapper.cpp {
    void SingleTouchInputMapper::process(const RawEvent* rawEvent) {
        TouchInputMapper::process(rawEvent);
        mSingleTouchMotionAccumulator.process(rawEvent);
    }
}
TouchInputMapper.cpp {
    void TouchInputMapper::process(const RawEvent* rawEvent) {
        mCursorButtonAccumulator.process(rawEvent);
        mCursorScrollAccumulator.process(rawEvent);
        mTouchButtonAccumulator.process(rawEvent);
        if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
            sync(rawEvent->when, rawEvent->readTime);
        }
    }
    void TouchInputMapper::sync(nsecs_t when, nsecs_t readTime) {
        // Push a new state.
        mRawStatesPending.emplace_back();
        RawState& next = mRawStatesPending.back();
        next.clear();
        next.when = when;
        next.readTime = readTime;
        // Sync button state.
        next.buttonState = mTouchButtonAccumulator.getButtonState() | mCursorButtonAccumulator.getButtonState();
        // Sync scroll
        next.rawVScroll = mCursorScrollAccumulator.getRelativeVWheel();
        mCursorScrollAccumulator.finishSync();
        processRawTouches(false /*timeout*/);
    }
    void TouchInputMapper::processRawTouches(bool timeout) {
        cookAndDispatch(mCurrentRawState.when, mCurrentRawState.readTime);
    }
    void TouchInputMapper::cookAndDispatch(nsecs_t when, nsecs_t readTime) {
        updateTouchSpots();
        // 内部处理各个事件,不需要的会进行拦截
        dispatchButtonRelease(when, readTime, policyFlags);
        dispatchHoverExit(when, readTime, policyFlags);
        dispatchTouches(when, readTime, policyFlags);
        dispatchHoverEnterAndMove(when, readTime, policyFlags);
        dispatchButtonPress(when, readTime, policyFlags);
    }
    void TouchInputMapper::dispatch***(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
         dispatchMotion(...);
    }
    void TouchInputMapper::dispatchMotion(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,uint32_t source, int32_t action, int32_t actionButton,int32_t flags, int32_t metaState, int32_t buttonState,int32_t edgeFlags, const PointerProperties* properties,const PointerCoords* coords, const uint32_t* idToIndex,BitSet32 idBits, int32_t changedId, float xPrecision,float yPrecision, nsecs_t downTime) {
        NotifyMotionArgs args(getContext()->getNextId(), when, readTime, deviceId, source, displayId,
                              policyFlags, action, actionButton, flags, metaState, buttonState,
                              MotionClassification::NONE, edgeFlags, pointerCount, pointerProperties,
                              pointerCoords, xPrecision, yPrecision, xCursorPosition, yCursorPosition,
                              downTime, std::move(frames));
        // Listener 为 InputDispatcher 实现
        getListener().notifyMotion(&args);
    }
}
InputDispatcher.cpp {
        
    void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {
        bool needWake = false;
        {
            // Just enqueue a new motion event.
            std::unique_ptr<MotionEntry> newEntry = std::make_unique<MotionEntry>(args->id, args->eventTime, args->deviceId,args->source, args->displayId, policyFlags,args->action, args->actionButton, args->flags,args->metaState, args->buttonState,args->classification, args->edgeFlags,args->xPrecision, args->yPrecision,args->xCursorPosition, args->yCursorPosition,args->downTime, args->pointerCount,args->pointerProperties, args->pointerCoords);
            needWake = enqueueInboundEventLocked(std::move(newEntry));
        } // release lock
        if (needWake) {
            // 唤醒 InputDispatcher 处理接收到的事件
            mLooper->wake();
        }
    }
        
    bool InputDispatcher::enqueueInboundEventLocked(std::unique_ptr<EventEntry> newEntry) {
        bool needWake = mInboundQueue.empty();
        mInboundQueue.push_back(std::move(newEntry));
        EventEntry& entry = *(mInboundQueue.back());
        switch (entry.type) {
            case EventEntry::Type::KEY: {
                if (...) {
                    needWake = true;
                }
                break;
            }
            case EventEntry::Type::MOTION: {
                if (shouldPruneInboundQueueLocked(static_cast<MotionEntry&>(entry))) {
                    ......
                    needWake = true;
                }
                break;
            }
        }
        return needWake;
    }
}

事件的派发

在这里插入图片描述

源码分析

InputDispatcher.cpp {
    status_t InputDispatcher::start() {
        mThread = std::make_unique<InputThread>("InputDispatcher", 
            [this]() { dispatchOnce(); }, 
            [this]() { mLooper->wake(); }
        );
        return OK;
    }
    void InputDispatcher::dispatchOnce() {
        { // acquire lock
            // 处理接收的消息
            dispatchOnceInnerLocked(&nextWakeupTime);
            // input anr 检测
            const nsecs_t nextAnrCheck = processAnrsLocked();
        } // release lock
        // 开启下次轮训等待 weak 唤醒
        mLooper->pollOnce(timeoutMillis);
    }
    void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
        // 取出从 reader 线程入队的事件
        mPendingEvent = mInboundQueue.front();
        mInboundQueue.pop_front();
        switch (mPendingEvent->type) {
            case EventEntry::Type::KEY: {
                std::shared_ptr<KeyEntry> keyEntry = std::static_pointer_cast<KeyEntry>(mPendingEvent);
                done = dispatchKeyLocked(currentTime, keyEntry, &dropReason, nextWakeupTime);
                break;
            }
            case EventEntry::Type::MOTION: {
                std::shared_ptr<MotionEntry> motionEntry = std::static_pointer_cast<MotionEntry>(mPendingEvent);
                done = dispatchMotionLocked(currentTime, motionEntry, &dropReason, nextWakeupTime);
                break;
            }
        }
    }
    bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr<MotionEntry> entry,DropReason* dropReason, nsecs_t* nextWakeupTime) {
        // Identify targets.
        std::vector<InputTarget> inputTargets;
        addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(*entry));
        dispatchEventLocked(currentTime, entry, inputTargets);
        return true;
    }
    void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,std::shared_ptr<EventEntry> eventEntry,const std::vector<InputTarget>& inputTargets) {
       for (const InputTarget& inputTarget : inputTargets) {
            // Connection 对象中,有两个集合,分别是 outboundQueue 和 waitQueue。
            // outboundQueue 集合中存放发送给 APP 侧的输入事件
            // waitQueue 中存放发送给 APP 侧但还没收到回应的输入事件。
            sp<Connection> connection = getConnectionLocked(inputTarget.inputChannel->getConnectionToken());
            if (connection != nullptr) {
                prepareDispatchCycleLocked(currentTime, connection, eventEntry, inputTarget);
            }
        }
    }
    void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,const sp<Connection>& connection,std::shared_ptr<EventEntry> eventEntry,const InputTarget& inputTarget) {
        enqueueDispatchEntriesLocked(currentTime, connection, eventEntry, inputTarget);
    }
    void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,const sp<Connection>& connection,std::shared_ptr<EventEntry> eventEntry,const InputTarget& inputTarget) {
        bool wasEmpty = connection->outboundQueue.empty();
        // 事件加入到Connection对象中outboundQueue集合
        enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,InputTarget::FLAG_DISPATCH_AS_***);
        if (wasEmpty && !connection->outboundQueue.empty()) {
            startDispatchCycleLocked(currentTime, connection);
        }
    }
    void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,const sp<Connection>& connection) {
       while (connection->status == Connection::Status::NORMAL && !connection->outboundQueue.empty()) {
            // 取出队首事件,进行分发
            DispatchEntry* dispatchEntry = connection->outboundQueue.front();
            // Publish the event.
            status_t status;
            const EventEntry& eventEntry = *(dispatchEntry->eventEntry);
            switch (eventEntry.type) {
                case EventEntry::Type::KEY: {
                    const KeyEntry& keyEntry = static_cast<const KeyEntry&>(eventEntry);
                    // Publish the key event.
                    status = connection->inputPublisher.publishKeyEvent(dispatchEntry->seq,dispatchEntry->resolvedEventId, keyEntry.deviceId,keyEntry.source, keyEntry.displayId,std::move(hmac), dispatchEntry->resolvedAction,dispatchEntry->resolvedFlags, keyEntry.keyCode,keyEntry.scanCode, keyEntry.metaState,keyEntry.repeatCount, keyEntry.downTime,keyEntry.eventTime);
                    break;
                }
    
                case EventEntry::Type::MOTION: {
                    const MotionEntry& motionEntry = static_cast<const MotionEntry&>(eventEntry);
                    // Publish the motion event.
                    status = connection->inputPublisher.publishMotionEvent(dispatchEntry->seq,dispatchEntry->resolvedEventId,motionEntry.deviceId, motionEntry.source,motionEntry.displayId, std::move(hmac),dispatchEntry->resolvedAction,motionEntry.actionButton,dispatchEntry->resolvedFlags,motionEntry.edgeFlags, motionEntry.metaState,motionEntry.buttonState,motionEntry.classification,dispatchEntry->transform,motionEntry.xPrecision, motionEntry.yPrecision,motionEntry.xCursorPosition,motionEntry.yCursorPosition,dispatchEntry->rawTransform,motionEntry.downTime, motionEntry.eventTime,motionEntry.pointerCount,motionEntry.pointerProperties, usingCoords);
                    break;
                }
        }
    }
}
InputTransport.cpp {
    
    status_t InputPublisher::publishMotionEvent(...) {
        InputMessage msg;
        msg.header.type = InputMessage::Type::MOTION;
        msg.header.seq = seq;
        msg.body.motion.eventId = eventId;
        msg.body.motion.deviceId = deviceId;
        msg.body.motion.source = source;
        msg.body.motion.displayId = displayId;
        msg.body.motion.hmac = std::move(hmac);
        msg.body.motion.action = action;
        msg.body.motion.actionButton = actionButton;
        msg.body.motion.flags = flags;
        msg.body.motion.edgeFlags = edgeFlags;
        msg.body.motion.metaState = metaState;
        msg.body.motion.buttonState = buttonState;
        ......
        return mChannel->sendMessage(&msg);
    }
    
    status_t InputChannel::sendMessage(const InputMessage* msg) {
        const size_t msgLength = msg->size();
        InputMessage cleanMsg;
        msg->getSanitizedCopy(&cleanMsg);
        ssize_t nWrite;
        do {
            // socket 的方式发送到客户端,通过的是 FD 管道
            nWrite = ::send(getFd(), &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
        } while (nWrite == -1 && errno == EINTR);
    
        return OK;
    }
}

客户端

事件接收初始化

在这里插入图片描述

源码分析

ViewRootImpl.java {
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
        int userId) {
        InputChannel inputChannel = null;
        if ((mWindowAttributes.inputFeatures & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
            inputChannel = new InputChannel();
        }
        // 创建 channel
        res = mWindowSession.addToDisplayAsUser(mWindow, mSeq, mWindowAttributes,
                                                getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrame,
                                                mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                                                mAttachInfo.mDisplayCutout, inputChannel,
                                                mTempInsets, mTempControls);
        // 通过 loop 监听 channel 的 fd
        mInputEventReceiver = new WindowInputEventReceiver(inputChannel,Looper.myLooper());
    }
}
Session.java {
    public int addToDisplayAsUser(...,InputChannel outInputChannel,...) {
        return mService.addWindow(this, window, attrs, viewVisibility, displayId, userId,
                requestedVisibilities, outInputChannel, outInsetsState, outActiveControls,
                outAttachedFrame, outSizeCompatScale);
    }
}
WindowManagerService.java {
    public int addWindow(...,InputChannel outInputChannel,...){
        final WindowState win = new WindowState(this, session, client, token, parentWindow,appOp[0], seq, attrs, viewVisibility, session.mUid, userId,session.mCanAddInternalSystemWindow);
        final boolean openInputChannels = (outInputChannel != null && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
        if  (openInputChannels) {
            win.openInputChannel(outInputChannel);
        }
    }
}
WindowState.java {
    void openInputChannel(InputChannel outInputChannel) {
        String name = getName();
        // 创建两个 channel
        mInputChannel = mWmService.mInputManager.createInputChannel(name);
        mInputChannelToken = mInputChannel.getToken();
        mInputWindowHandle.setToken(mInputChannelToken);
        mWmService.mInputToWindowMap.put(mInputChannelToken, this);
        if (outInputChannel != null) {
            // 将服务端创建的 InputChannel 赋值给 outInputChannel
            mInputChannel.copyTo(outInputChannel);
        }
    }
}
InputManagerService.java {
    public InputChannel createInputChannel(String name) {
        return mNative.createInputChannel(name);
    }
}
com_android_server_input_InputManagerService.cpp {
    static jobject nativeCreateInputChannel(JNIEnv* env, jobject nativeImplObj, jstring nameObj) {
        NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
        // Native 层 InputChannel 的创建
        base::Result<std::unique_ptr<InputChannel>> inputChannel = im->createInputChannel(name);
        //Java 层 InputChannel 的创建
        jobject inputChannelObj = android_view_InputChannel_createJavaObject(env, std::move(*inputChannel));
        return inputChannelObj;
    }
}
NativeInputManager.cpp {
    base::Result<std::unique_ptr<InputChannel>> NativeInputManager::createInputChannel(const std::string& name) {
        return mInputManager->getDispatcher().createInputChannel(name);
    }
}

InputDispatcher.cpp {
    
    Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputChannel(const std::string& name) {
        
        std::unique_ptr<InputChannel> serverChannel;
        std::unique_ptr<InputChannel> clientChannel;
        // 创建 客户端与服务端的 InputChannel,使用 socket 进行通信
        status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
    
        { // acquire lock
            const sp<IBinder>& token = serverChannel->getConnectionToken();
            // 封装 connection 用于 服务端事件分发
            sp<Connection> connection = new Connection(std::move(serverChannel), false /*monitor*/, mIdGenerator);
            mConnectionsByToken.emplace(token, connection);
    
            std::function<int(int events)> callback = std::bind(&InputDispatcher::handleReceiveCallback,
                                                                this, std::placeholders::_1, token);
    
            int fd = serverChannel->getFd();
            // 服务端 接收 客户端发送的事件,代表 客户端 消息处理完成
            mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, new LooperEventCallback(callback), nullptr);
        } // release lock
    
        return clientChannel;
    }
}
InputTransport.cpp {
    status_t InputChannel::openInputChannelPair(const std::string& name,
                                                std::unique_ptr<InputChannel>& outServerChannel,
                                                std::unique_ptr<InputChannel>& outClientChannel) {
        int sockets[2];
        int bufferSize = SOCKET_BUFFER_SIZE;
        setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
        setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
        setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
        setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
    
        sp<IBinder> token = new BBinder();
        std::string serverChannelName = name + " (server)";
        android::base::unique_fd serverFd(sockets[0]);
        outServerChannel = InputChannel::create(serverChannelName, std::move(serverFd), token);
        std::string clientChannelName = name + " (client)";
        android::base::unique_fd clientFd(sockets[1]);
        outClientChannel = InputChannel::create(clientChannelName, std::move(clientFd), token);
        return OK;
    }
}

WindowInputEventReceiver.java {
    public InputEventReceiver(InputChannel inputChannel, Looper looper) {
        mInputChannel = inputChannel;
        mMessageQueue = looper.getQueue();
        mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
                mInputChannel, mMessageQueue);
    }
}
android_view_InputEventReceiver.cpp {
        
    static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,jobject inputChannelObj, jobject messageQueueObj) {
        //根据 java 层 InputChannel 找到对应 Native 层 InputChannel
        std::shared_ptr<InputChannel> inputChannel =
                android_view_InputChannel_getInputChannel(env, inputChannelObj);
       //根据 java 层 MessageQueue 找到对应 Native 层消息队列
        sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
        //创建 Native 层接收器,inputChannel 交给 mInputConsumer(inputChannel)
        sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,receiverWeak, inputChannel, messageQueue);
        status_t status = receiver->initialize();
        return reinterpret_cast<jlong>(receiver.get());
    }
    status_t NativeInputEventReceiver::initialize() {
        setFdEvents(ALOOPER_EVENT_INPUT);
        return OK;
    }
    void NativeInputEventReceiver::setFdEvents(int events) {
        if (mFdEvents != events) {
            mFdEvents = events;
            int fd = mInputConsumer.getChannel()->getFd();
            if (events) {
                //当监听的句柄 fd 发生事件,触发 NativeInputEventReceiver#handleEvent 方法
                mMessageQueue->getLooper()->addFd(fd, 0, events, this, nullptr);
            } else {
                mMessageQueue->getLooper()->removeFd(fd);
            }
        }
    }
}

事件接收

在这里插入图片描述

源码分析

android_view_InputEventReceiver.cpp {
    //当监听的句柄 fd 发生事件,触发 NativeInputEventReceiver#handleEvent 方法
    int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {
        status_t status = consumeEvents(env, false /*consumeBatches*/, -1, nullptr);
    }
    status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
       for (;;) {
            InputEvent* inputEvent;
            status_t status = mInputConsumer.consume(&mInputEventFactory,consumeBatches, frameTime, &seq, &inputEvent);
            jobject inputEventObj;
            switch (inputEvent->getType()) {
                case AINPUT_EVENT_TYPE_KEY:
                    inputEventObj = android_view_KeyEvent_fromNative(env,static_cast<KeyEvent*>(inputEvent));
                    break;
                case AINPUT_EVENT_TYPE_MOTION: {
                    MotionEvent* motionEvent = static_cast<MotionEvent*>(inputEvent);
                    inputEventObj = android_view_MotionEvent_obtainAsCopy(env, motionEvent);
                    break;
                }
            }
            if (inputEventObj) {
                // receiverWeak 是 ViewRootImpl 中创建的 WindowInputEventReceiver 对象
                // call WindowInputEventReceiver 的 dispatchInputEvent 方法
                env->CallVoidMethod(receiverObj.get(),
                        gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);
            }
        }
    }
}

InputTransport.cpp {
    status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consumeBatches,
                                    nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) {
       while (!*outEvent) {
            // 从 socket 中读取 InputMessage
            status_t result = mChannel->receiveMessage(&mMsg);
            switch (mMsg.header.type) {
                case InputMessage::Type::KEY: {
                    KeyEvent* keyEvent = factory->createKeyEvent();
                    // 将 InputMessage 封装成 keyEvent
                    initializeKeyEvent(keyEvent, &mMsg);
                    *outSeq = mMsg.header.seq;
                    *outEvent = keyEvent;
                    break;
                }
    
                case InputMessage::Type::MOTION: {
                    MotionEvent* motionEvent = factory->createMotionEvent();
                    updateTouchState(mMsg);
                    // 将 InputMessage 封装成 MotionEvent
                    initializeMotionEvent(motionEvent, &mMsg);
                    *outSeq = mMsg.header.seq;
                    *outEvent = motionEvent;
                    break;
                }
        }
        return OK;
    }
    
    void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage* msg) {
        event->initialize(msg->body.motion.eventId, msg->body.motion.deviceId, msg->body.motion.source,msg->body.motion.displayId, msg->body.motion.hmac, msg->body.motion.action,msg->body.motion.actionButton, msg->body.motion.flags,msg->body.motion.edgeFlags, msg->body.motion.metaState,
                          msg->body.motion.buttonState, msg->body.motion.classification, transform,msg->body.motion.xPrecision, msg->body.motion.yPrecision,msg->body.motion.xCursorPosition, msg->body.motion.yCursorPosition,displayTransform, msg->body.motion.downTime, msg->body.motion.eventTime,pointerCount, pointerProperties, pointerCoords);
    }
    
    status_t InputChannel::receiveMessage(InputMessage* msg) {
        ssize_t nRead;
        do {
            // 通过 socket 接收 InputDispatcher 发送的数据
            nRead = ::recv(getFd(), msg, sizeof(InputMessage), MSG_DONTWAIT);
        } while (nRead == -1 && errno == EINTR);
        return OK;
    }
}

事件分发

在这里插入图片描述

源码分析

ViewRootImpl.java {
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,int userId) {
        if (inputChannel != null) {
            mInputEventReceiver = new WindowInputEventReceiver(inputChannel,Looper.myLooper());
        }
    }
    final class WindowInputEventReceiver extends InputEventReceiver {
        // call by native
        private void dispatchInputEvent(int seq, InputEvent event) {
            onInputEvent(event);
        }
        @Override
        public void onInputEvent(InputEvent event) {
            enqueueInputEvent(event, this, 0, true);
        }
    }
    void enqueueInputEvent(InputEvent event,InputEventReceiver receiver, int flags, boolean processImmediately) {
        QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
        mPendingInputEventTail = q;
        doProcessInputEvents();
    }
    void doProcessInputEvents() {
        // Deliver all pending input events in the queue.
        while (mPendingInputEventHead != null) {
            QueuedInputEvent q = mPendingInputEventHead;
            deliverInputEvent(q);
        }
    }
    private void deliverInputEvent(QueuedInputEvent q) {
        try {
            InputStage stage;
            stage.deliver(q);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
    }
    abstract class InputStage {
        public final void deliver(QueuedInputEvent q) {
            result = onProcess(q);
        }
    }
    final class ViewPostImeInputStage extends InputStage {
        protected int onProcess(QueuedInputEvent q) {
            if (q.mEvent instanceof KeyEvent) {
                return processKeyEvent(q);
            } else {
                return processPointerEvent(q);
            }
        }
        private int processPointerEvent(QueuedInputEvent q) {
            final MotionEvent event = (MotionEvent)q.mEvent;
            boolean handled = mView.dispatchPointerEvent(event);
            return handled ? FINISH_HANDLED : FORWARD;
        }
    }
}
View.java {
    public final boolean dispatchPointerEvent(MotionEvent event) {
        if (event.isTouchEvent()) {
            return dispatchTouchEvent(event);
        } else {
            return dispatchGenericMotionEvent(event);
        }
    }
}
DecorView.java {
    public boolean dispatchTouchEvent(MotionEvent event) {
        //Activity 实现了 Window.Callback 这个接口
        final Window.Callback cb = mWindow.getCallback();
        return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
                ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
    }
}
Activity.java {
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;
        }
        return onTouchEvent(ev);
    }
}
PhoneWindow.java {
    public boolean superDispatchTouchEvent(MotionEvent event) {
        return mDecor.superDispatchTouchEvent(event);
    }
}
DecorView.java {
    public boolean superDispatchTouchEvent(MotionEvent event) {
        return super.dispatchTouchEvent(event);
    }
}
ViewGroup.java {
    public boolean dispatchTouchEvent(MotionEvent ev) {
        onInterceptTouchEvent(ev);
        for (int i = childrenCount - 1; i >= 0; i--) {
            dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)
        }
    }
    private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
        View child, int desiredPointerIdBits) {
        return child.dispatchTouchEvent(event);
    }
}
View.java{
    public boolean dispatchTouchEvent(MotionEvent event) {
        onTouchEvent(event)
    }
    public boolean onTouchEvent(MotionEvent event) {
        //do sameing
    }
}

相关工具

getevent

adb shell getevent [-选项] [device_path]
查询 所有设备节点,event0:音量加、电源,event1:音量减
在这里插入图片描述

音量加减、 电源键 事件输入,SYN_REPORT 为单个事件的结束标志。
在这里插入图片描述

常见 EventCode

key描述
EV_KEY键盘、按钮或其他类似按键的状态变化
EV_REL相对坐标轴值的变化,例如鼠标移动5个单位
EV_ABS绝对轴值的变化,例如触摸屏上触摸的坐标
SYN_REPORT将事件同步并打包

<事件发生时间> <事件类型> <事件代码> <事件值>
产生事件时的时间戳([ 2038.027687])
产生事件的设备节点(/dev/input/event0)
事件类型(0001),事件代码(0073)以及事件的值(00000001)
在这里插入图片描述

sendevent

sendevent <节点路径> <类型><代码> <值>
sendevent 的输入参数与 getevent 的输出是对应的,只不过 sendevent 的参数为十进制。
模拟音量+键:音量+ 键的代码 0x73 的十进制为 115
在这里插入图片描述

dumpsys input

$adb shell dumpsys input
Event Hub State:
    ......
    10: virtual_input_device
      Classes: 0x00000065
      Path: /dev/input/event9
      Enabled: true
      Descriptor: ea011df68d5e458fae3d98067e2d851a8bb4cac2
      Location:
      ControllerNumber: 6
      UniqueId:
      Identifier: bus=0x0006, vendor=0x1234, product=0x5678, version=0x0001
      KeyLayoutFile: /system/usr/keylayout/Generic.kl
      KeyCharacterMapFile: /system/usr/keychars/Generic.kcm
      ConfigurationFile:
      HaveKeyboardLayoutOverlay: false
      VideoDevice: <none>
Input Reader State:
    ......
    Device 10: virtual_input_device
Input Dispatcher State:
    ......
    Display: 5
    Windows:
  Display: 0

相关文档

触摸设备 | Android 开源项目 | Android Open Source Project
Linux 输入事件代码
Linux 多点触控协议
Linux 输入驱动程序

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/496225.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

24.Stream流

Stream流 一、什么是Stream流 Stream流操作是Java 8提供一个重要新特性&#xff0c;它允许开发人员以声明性方式处理集合&#xff0c;其核心类库主要改进了对集合类的 API和新增Stream操作。Stream类中每一个方法都对应集合上的一种操作。将真正的函数式编程引入到Java中&…

2023全栈开发人员职业路线图

0. 全栈开发人员职业路线图 全栈开发人员是IT行业中薪资最高的职业之一。 如果您想成为一名全栈开发人员&#xff0c;以下是2023年全栈开发人员路线图上的十一个步骤&#xff1a; 掌握敏捷开发和Scrum学习浏览器技术&#xff0c;如HTML和CSS熟练掌握JavaScript或TypeScript了…

单月涨粉303.72w,反差感才是主流吗?

据新抖「直播带货风向」数据显示&#xff0c;抖音4月的直播商品数量达到1021.32w&#xff0c;较上月的695.91w环比增长50.18%&#xff0c;直播销量环比增加16.81%。从这几个数值就可以看出4月的抖音电商依旧如火如荼...... 那么&#xff0c;4月&#xff0c;抖音又出现哪些新的看…

“世界中医药之都” 亳州市医保局领导一行莅临万民健康交流指导

为进一步推进智慧医疗、智慧服务、智慧管理“三位一体”为主旨的“智慧中医、健康社区”项目建设。2023 年 5 月 3 日&#xff0c;“世界中医药之都” 亳州市医保局 局长 吴旭春 、 医保中心主任秦克靖 、 办公室主任徐伟 等一行 5 人莅临 万民健康交流 指导工作 &#xff0c…

day27_mysql

今日内容 零、 复习昨日 一、单表查询 二、多表联查 零、 复习昨日 1 DDL,DML,DQL是啥 DDL 数据定义语言&#xff08;库&#xff0c;表&#xff0c;列&#xff09;DML 数据操作语言&#xff08;表内数据的操作增删改&#xff09;DQL 数据查询语言&#xff08;表内数据的查询&am…

酷游浅谈网站Javas cript型别

最近整理了一下&#xff0c;【酷游娜娜手机&#x1d54d;找看看nay3989提供】就决定跟大家讨论一下最近对于Javascripet的型别认识。 弱型别&#xff36;&#xff33; 强型别 Javascripet是一种「弱型别」的语言&#xff0c;所以会产生很多你意想不到恶心的事情 至于什么是弱…

软件测试、测试和开发、测试和调试【详细介绍】

目录 一、什么是软件测试 1.软件测试的定义 2.软件测试的目的 3.软件测试的不可穷性 二、开发和测试的区别 三、测试和调试的区别 一、什么是软件测试 在日常生活中&#xff0c;测试是无处不在的。比如新买的手机是否好用、新买的衣服穿着是否合身等等场景&#xff0c;均…

点成案例丨细胞培养芯片用于构建肠模型实例分享

器官芯片是一种利用微芯片制造技术制造的微流体细胞培养设备。该设备包含多个连续灌注腔室&#xff0c;具有多细胞层结构、组织界面、物理化学微环境以及人体血管循环等特征&#xff0c;可以模拟和重构人体器官的生理功能&#xff0c;为相关研究提供了可靠的平台。器官芯片技术…

java中设计模式总结

设计模式是实际工作中写的各种代码进行高层次抽象的总结&#xff0c;其中最出名的当属 Gang of Four (GoF) 的分类了&#xff0c;他们将设计模式分类为 23 种经典的模式&#xff0c;根据用途又可以分为三大类&#xff0c;分别为创建型模式、结构型模式和行为型模式。 有一些重…

【6D位姿估计】Point Pair Feature (PPF)

论文链接:Drost et al. Model Globally, Match Locally: Efficient and Robust 3D Object Recognition. CVPR, 2010. Model Globally, Match Locally 论文名字用 4 个词高度总结了 PPF 算法的精髓:“整体建模,局部匹配”。 下面这张图反应了论文的基本思想(算法概要): …

【鸿蒙应用ArkTS开发系列】- 导航栏Tab组件使用讲解

目录 Tabs介绍Tabs使用例子TabBar 样式设置定义菜单样式对象-NavigationItem定义一个底部菜单栏集合数据-NavigationList修改TabBuilder Tab 组件控制题外话 现在市场上的大部分应用&#xff0c;主页都是才用底部导航栏菜单作为页面主体框架来展示&#xff0c; 在鸿蒙中是使用…

STM32库函数笔记分享

之前刚开始自学的部分STM32笔记放出&#xff0c;希望对新入门STM32和想要复习库函数的小伙伴们起到帮助。 建立工程 1.寄存器操作方式 需要不断地查手册来了解每一位是干什么用的 优点&#xff1a;代码简介&#xff1b; 缺点&#xff1a;不太方便。 2.库函数操作方式 1.调用库函…

【leetcode热题100】接雨水、直方图最大矩形面积、矩阵中最大的矩形

文章目录 一、接雨水方法一&#xff1a;按列求&#xff08;动态规划&#xff09;方法二&#xff1a;双指针方法三&#xff1a;单调栈 二、直方图最大矩形面积单调栈哨兵位优化 三、矩阵中最大的矩形前缀和单调栈 一、接雨水 题目链接 题目描述&#xff1a; 给定 n 个非负整数…

JVM_垃圾回收器

目录 一、GC分类1.串行vs并行2.并发式vs独占式3.压缩式vs非压缩式4.年轻代vs老年代 二、GC评估指标1.吞吐量2.暂停时间3.小结 三、垃圾回收器都有哪些&#xff1f;1.GC发展史2.7种GC组合关系&#xff1f;3.为什么这么多GC4.如何查看默认GC?5.Serial GC&#xff1a;串行回收5.1…

字符设备驱动开发实验

我们以 chadev 这个虚拟设备为 例&#xff0c;完整的编写一个字符设备驱动模块。chadev 不是实际存在的一个设备&#xff0c;是为了方 便讲解字符设备的开发而引入的一个虚拟设备设备有两个缓冲区&#xff0c;一个为读缓冲 区&#xff0c;一个为写缓冲区&#xff0c;这两个缓冲…

Spring事务隔离级别详解

Spring有五大隔离级别&#xff1a; 1、ISOLATION_DEFAULT 2、ISOLATION_READ_UNCOMMITTED 3、ISOLATION_READ_COMMITTED 4、ISOLATION_REPEATABLE_READ 5、ISOLATION_SERIALIZABLE ISOLATION_DEFAULT 用底层数据库的设置隔离级别。 ISOLATION_READ_UNCOMMITTED 一个事…

java 数组创建的方法

数组是一个由一组元素组成的集合&#xff0c;我们可以用一个数组来表示集合。 java中最基本的数据类型是字符串&#xff0c;其长度是固定的&#xff0c;且不可变&#xff0c;一个字符串只能以一个数字开头。 在 Java中我们可以通过 myConst关键字来指定数组的长度。下面就看一下…

直线飙升到10万+star的AutoGpt,有多强?帮我写了个网页!

先来感受一下10万的star&#xff0c;到底有多强&#xff01; 从4月2日开始&#xff0c;直线飙升到10万star Auto-GPT是一个实验性的开源应用程序&#xff0c;展示了GPT-4语言模型的功能。这个程序由GPT-4驱动&#xff0c;将LLM“思想”链接在一起&#xff0c;以自主实现您设定的…

CTO解读:从“RSAC 2023”到“韧性数据安全”—Stronger Together

一年一度RSA Conference已落下帷幕。作为全球最具规模的安全大会&#xff0c;每年一届的RSAC都是安全行业的风向标。 2023年RSAC的主题是&#xff1a;Stronger Together&#xff0c;一起更强大。安全产品往往是场景化的&#xff0c;单点产品解决不同的问题&#xff0c;有机的整…

QT自制软键盘 最完美、最简单、跟自带虚拟键盘一样

QT自制软键盘 最完美、最简单、跟自带虚拟键盘一样 [1] QT自制软键盘 最完美、最简单、跟自带虚拟键盘一样一、本自制虚拟键盘特点二、windows打开系统自带软键盘三、让键盘界面保持在最上方、不改变底层界面焦点四、长按按键重复输入键盘内容五、模拟键盘点击事件完成虚拟键盘…