Android14 InputManager-InputReader的处理

IMS启动时会调用InputReader.start()方法

InputReader.cpp
status_t InputReader::start() {
    if (mThread) {
        return ALREADY_EXISTS;
    }
    mThread = std::make_unique<InputThread>(
            "InputReader", [this]() { loopOnce(); }, [this]() { mEventHub->wake(); });
    return OK;
}

当线程开始运行后,将会在内建的线程循环中不断地调用threadLoop(),直到此函数返回false,则退出线程循环,从而结束线程。

InputReader的一次线程循环的工作思路非常清晰,一共三步:

□首先从EventHub中抽取未处理的事件列表。这些事件分为两类,一类是从设备节点中读取的原始输入事件,另一类则是输入设备可用性变化事件,简称为设备事件。

□通过processEventsLocked()对事件进行处理。对于设备事件,此函数对根据设备的可用性加载或移除设备对应的配置信息。对于原始输入事件,则在进行转译、封装与加工后将结果暂存到mQueuedListener中。

□所有事件处理完毕后,调用mQueuedListener.flush()将所有暂存的输入事件一次性地交付给InputDispatcher。

void InputReader::loopOnce() {
    int32_t oldGeneration;
    int32_t timeoutMillis;
    // Copy some state so that we can access it outside the lock later.
    bool inputDevicesChanged = false;
    std::vector<InputDeviceInfo> inputDevices;
    std::list<NotifyArgs> notifyArgs;
    { // acquire lock
        std::scoped_lock _l(mLock);

        oldGeneration = mGeneration;
        timeoutMillis = -1;

        auto changes = mConfigurationChangesToRefresh;
        if (changes.any()) {
            mConfigurationChangesToRefresh.clear();
            timeoutMillis = 0;
            refreshConfigurationLocked(changes);
        } else if (mNextTimeout != LLONG_MAX) {
            nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
            timeoutMillis = toMillisecondTimeoutDelay(now, mNextTimeout);
        }
    } // release lock

    std::vector<RawEvent> events = mEventHub->getEvents(timeoutMillis);

    { // acquire lock
        std::scoped_lock _l(mLock);
        mReaderIsAliveCondition.notify_all();
// 如果有事件信息,调用processEventsLocked()函数对事件进行加工处理
        if (!events.empty()) {
            notifyArgs += processEventsLocked(events.data(), events.size());
        }

        if (mNextTimeout != LLONG_MAX) {
            nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
            if (now >= mNextTimeout) {
                if (debugRawEvents()) {
                    ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f);
                }
                mNextTimeout = LLONG_MAX;
                notifyArgs += timeoutExpiredLocked(now);
            }
        }

        if (oldGeneration != mGeneration) {
            inputDevicesChanged = true;
            inputDevices = getInputDevicesLocked();
            notifyArgs.emplace_back(
                    NotifyInputDevicesChangedArgs{mContext.getNextId(), inputDevices});
        }
    } // release lock

    // Send out a message that the describes the changed input devices.
    if (inputDevicesChanged) {
        mPolicy->notifyInputDevicesChanged(inputDevices);
    }

    // Notify the policy of the start of every new stylus gesture outside the lock.
    for (const auto& args : notifyArgs) {
        const auto* motionArgs = std::get_if<NotifyMotionArgs>(&args);
        if (motionArgs != nullptr && isStylusPointerGestureStart(*motionArgs)) {
            mPolicy->notifyStylusGestureStarted(motionArgs->deviceId, motionArgs->eventTime);
        }
    }

    notifyAll(std::move(notifyArgs));

    // Flush queued events out to the listener.
    // This must happen outside of the lock because the listener could potentially call
    // back into the InputReader's methods, such as getScanCodeState, or become blocked
    // on another thread similarly waiting to acquire the InputReader lock thereby
    // resulting in a deadlock.  This situation is actually quite plausible because the
    // listener is actually the input dispatcher, which calls into the window manager,
    // which occasionally calls into the input reader.
    mQueuedListener.flush();
}

processEventsLocked()会分别处理原始输入事件与设备增删事件。

对于原始输入事件,由于EventHub会将属于同一输入设备的原始输入事件放在一起,因此processEventsLocked()可以使processEventsForDeviceLocked()同时处理来自同一输入设备的一批事件。

std::list<NotifyArgs> InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
    std::list<NotifyArgs> out;
    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;
            while (batchSize < count) {
                if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT ||
                    rawEvent[batchSize].deviceId != deviceId) {
                    break;
                }
                batchSize += 1;
            }
            if (debugRawEvents()) {
                ALOGD("BatchSize: %zu Count: %zu", batchSize, count);
            }
            out += 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;
            }
        }
        count -= batchSize;
        rawEvent += batchSize;
    }
    return out;
}
std::list<NotifyArgs> InputReader::processEventsForDeviceLocked(int32_t eventHubId,
                                                                const RawEvent* rawEvents,
                                                                size_t count) {
    auto deviceIt = mDevices.find(eventHubId);
    if (deviceIt == mDevices.end()) {
        ALOGW("Discarding event for unknown eventHubId %d.", eventHubId);
        return {};
    }

    std::shared_ptr<InputDevice>& device = deviceIt->second;
    if (device->isIgnored()) {
        // ALOGD("Discarding event for ignored deviceId %d.", deviceId);
        return {};
    }

    return device->process(rawEvents, count);
}
InputDevice.cpp
std::list<NotifyArgs> InputDevice::process(const RawEvent* rawEvents, size_t count) {
    // Process all of the events in order for each mapper.
    // We cannot simply ask each mapper to process them in bulk because mappers may
    // have side-effects that must be interleaved.  For example, joystick movement events and
    // gamepad button presses are handled by different mappers but they should be dispatched
    // in the order received.
    std::list<NotifyArgs> out;
    for (const RawEvent* rawEvent = rawEvents; count != 0; rawEvent++) {
        if (debugRawEvents()) {
            const auto [type, code, value] =
                    InputEventLookup::getLinuxEvdevLabel(rawEvent->type, rawEvent->code,
                                                         rawEvent->value);
            ALOGD("Input event: eventHubDevice=%d type=%s code=%s value=%s when=%" PRId64,
                  rawEvent->deviceId, type.c_str(), code.c_str(), value.c_str(), rawEvent->when);
        }

        if (mDropUntilNextSync) {
            if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
                mDropUntilNextSync = false;
                ALOGD_IF(debugRawEvents(), "Recovered from input event buffer overrun.");
            } else {
                ALOGD_IF(debugRawEvents(),
                         "Dropped input event while waiting for next input sync.");
            }
        } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {
            ALOGI("Detected input event buffer overrun for device %s.", getName().c_str());
            mDropUntilNextSync = true;
            out += reset(rawEvent->when);
        } else {
            for_each_mapper_in_subdevice(rawEvent->deviceId, [&](InputMapper& mapper) {
                out += mapper.process(rawEvent);
            });
        }
        --count;
    }
    return out;
}

InputMapper是InputReader中实际进行原始输入事件加工的场所,它有一系列的子类,分别用于加工不同类型的原始输入事件。而InputDevice的process()函数使用InputMapper的方式是一个简化了的职责链(chain of responsibility)设计模式。InputDevice不需要知道哪一个InputMapper可以处理一个原始输入事件,只须将一个事件逐个交给每一个InputMapper尝试处理,如果InputMapper可以接受这个事件则处理之,否则什么都不做。

根据设备的类型,将设备做如下分配

InputMapper的种类实在很多,对所有类型都进行详细分析并不现实。选择KeyboardInputMapper和MultiTouchInputMapper两个常用并且具有代表性的InputMapper进行探讨

键盘事件的处理

KeyboardInputMapper的process()函数比较简单

□通过processKey()按键事件做进一步处理。

mQueuedListener.flush();

根据扫描码获取虚拟键值以及功能值后,KeyboardInputMapper::process()调用了processKey()函数对按键事件做进一步处理。

将键盘信息转化为NotifyKeyArgs,返回到loopOnce的loopOnce中,通知事件分发

触摸事件的处理

触摸事件的处理

std::list<NotifyArgs> MultiTouchInputMapper::process(const RawEvent* rawEvent) {
    std::list<NotifyArgs> out = TouchInputMapper::process(rawEvent);

    mMultiTouchMotionAccumulator.process(rawEvent);
    return out;
}

事件处理完成后回到最初的

mQueuedListener.flush(); // 将事件交给inputDispatcher的过程

 

InputDispatcher继承了InputDispatcherInterface接口
class InputDispatcher : public android::InputDispatcherInterface {
void InputListenerInterface::notify(const NotifyArgs& generalArgs) {
    Visitor v{
            [&](const NotifyInputDevicesChangedArgs& args) { notifyInputDevicesChanged(args); },
            [&](const NotifyConfigurationChangedArgs& args) { notifyConfigurationChanged(args); },
            [&](const NotifyKeyArgs& args) { notifyKey(args); },
            [&](const NotifyMotionArgs& args) { notifyMotion(args); },
            [&](const NotifySwitchArgs& args) { notifySwitch(args); },
            [&](const NotifySensorArgs& args) { notifySensor(args); },
            [&](const NotifyVibratorStateArgs& args) { notifyVibratorState(args); },
            [&](const NotifyDeviceResetArgs& args) { notifyDeviceReset(args); },
            [&](const NotifyPointerCaptureChangedArgs& args) { notifyPointerCaptureChanged(args); },
    };
    std::visit(v, generalArgs);
}

以motion事件为例

void InputDispatcher::notifyMotion(const NotifyMotionArgs& args) {
    if (debugInboundEventDetails()) {
        ALOGD("notifyMotion - id=%" PRIx32 " eventTime=%" PRId64 ", deviceId=%d, source=%s, "
              "displayId=%" PRId32 ", policyFlags=0x%x, "
              "action=%s, actionButton=0x%x, flags=0x%x, metaState=0x%x, buttonState=0x%x, "
              "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, xCursorPosition=%f, "
              "yCursorPosition=%f, downTime=%" PRId64,
              args.id, args.eventTime, args.deviceId, inputEventSourceToString(args.source).c_str(),
              args.displayId, args.policyFlags, MotionEvent::actionToString(args.action).c_str(),
              args.actionButton, args.flags, args.metaState, args.buttonState, args.edgeFlags,
              args.xPrecision, args.yPrecision, args.xCursorPosition, args.yCursorPosition,
              args.downTime);
        for (uint32_t i = 0; i < args.pointerCount; i++) {
            ALOGD("  Pointer %d: id=%d, toolType=%s, x=%f, y=%f, pressure=%f, size=%f, "
                  "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, orientation=%f",
                  i, args.pointerProperties[i].id,
                  ftl::enum_string(args.pointerProperties[i].toolType).c_str(),
                  args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X),
                  args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),
                  args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
                  args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
                  args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
                  args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
                  args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
                  args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
                  args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
        }
    }

    Result<void> motionCheck = validateMotionEvent(args.action, args.actionButton,
                                                   args.pointerCount, args.pointerProperties);
    if (!motionCheck.ok()) {
        LOG(ERROR) << "Invalid event: " << args.dump() << "; reason: " << motionCheck.error();
        return;
    }

    uint32_t policyFlags = args.policyFlags;
    policyFlags |= POLICY_FLAG_TRUSTED;

    android::base::Timer t;
    mPolicy.interceptMotionBeforeQueueing(args.displayId, args.eventTime, policyFlags);
    if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
        ALOGW("Excessive delay in interceptMotionBeforeQueueing; took %s ms",
              std::to_string(t.duration().count()).c_str());
    }

    bool needWake = false;
    { // acquire lock
        mLock.lock();
        if (!(policyFlags & POLICY_FLAG_PASS_TO_USER)) {
            // Set the flag anyway if we already have an ongoing gesture. That would allow us to
            // complete the processing of the current stroke.
            const auto touchStateIt = mTouchStatesByDisplay.find(args.displayId);
            if (touchStateIt != mTouchStatesByDisplay.end()) {
                const TouchState& touchState = touchStateIt->second;
                if (touchState.deviceId == args.deviceId && touchState.isDown()) {
                    policyFlags |= POLICY_FLAG_PASS_TO_USER;
                }
            }
        }

        if (shouldSendMotionToInputFilterLocked(args)) {
            ui::Transform displayTransform;
            if (const auto it = mDisplayInfos.find(args.displayId); it != mDisplayInfos.end()) {
                displayTransform = it->second.transform;
            }

            mLock.unlock();

            MotionEvent event;
            event.initialize(args.id, args.deviceId, args.source, args.displayId, INVALID_HMAC,
                             args.action, args.actionButton, args.flags, args.edgeFlags,
                             args.metaState, args.buttonState, args.classification,
                             displayTransform, args.xPrecision, args.yPrecision,
                             args.xCursorPosition, args.yCursorPosition, displayTransform,
                             args.downTime, args.eventTime, args.pointerCount,
                             args.pointerProperties, args.pointerCoords);

            policyFlags |= POLICY_FLAG_FILTERED;
            if (!mPolicy.filterInputEvent(event, policyFlags)) {
                return; // event was consumed by the filter
            }

            mLock.lock();
        }

        // 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);

        if (args.id != android::os::IInputConstants::INVALID_INPUT_EVENT_ID &&
            IdGenerator::getSource(args.id) == IdGenerator::Source::INPUT_READER &&
            !mInputFilterEnabled) {
            const bool isDown = args.action == AMOTION_EVENT_ACTION_DOWN;
            mLatencyTracker.trackListener(args.id, isDown, args.eventTime, args.readTime);
        }

        needWake = enqueueInboundEventLocked(std::move(newEntry));
        mLock.unlock();
    } // release lock

    if (needWake) {
        mLooper->wake();
    }
}

到达InputDispatcher的Motion事件被保存在MotionEntry类中

bool InputDispatcher::enqueueInboundEventLocked(std::unique_ptr<EventEntry> newEntry) {
    bool needWake = mInboundQueue.empty();
    mInboundQueue.push_back(std::move(newEntry));
    EventEntry& entry = *(mInboundQueue.back());
    traceInboundQueueLengthLocked();

    switch (entry.type) {
        case EventEntry::Type::KEY: {
            LOG_ALWAYS_FATAL_IF((entry.policyFlags & POLICY_FLAG_TRUSTED) == 0,
                                "Unexpected untrusted event.");
            // Optimize app switch latency.
            // If the application takes too long to catch up then we drop all events preceding
            // the app switch key.
            const KeyEntry& keyEntry = static_cast<const KeyEntry&>(entry);
            if (isAppSwitchKeyEvent(keyEntry)) {
                if (keyEntry.action == AKEY_EVENT_ACTION_DOWN) {
                    mAppSwitchSawKeyDown = true;
                } else if (keyEntry.action == AKEY_EVENT_ACTION_UP) {
                    if (mAppSwitchSawKeyDown) {
                        if (DEBUG_APP_SWITCH) {
                            ALOGD("App switch is pending!");
                        }
                        mAppSwitchDueTime = keyEntry.eventTime + APP_SWITCH_TIMEOUT;
                        mAppSwitchSawKeyDown = false;
                        needWake = true;
                    }
                }
            }

            // If a new up event comes in, and the pending event with same key code has been asked
            // to try again later because of the policy. We have to reset the intercept key wake up
            // time for it may have been handled in the policy and could be dropped.
            if (keyEntry.action == AKEY_EVENT_ACTION_UP && mPendingEvent &&
                mPendingEvent->type == EventEntry::Type::KEY) {
                KeyEntry& pendingKey = static_cast<KeyEntry&>(*mPendingEvent);
                if (pendingKey.keyCode == keyEntry.keyCode &&
                    pendingKey.interceptKeyResult ==
                            KeyEntry::InterceptKeyResult::TRY_AGAIN_LATER) {
                    pendingKey.interceptKeyResult = KeyEntry::InterceptKeyResult::UNKNOWN;
                    pendingKey.interceptKeyWakeupTime = 0;
                    needWake = true;
                }
            }
            break;
        }

        case EventEntry::Type::MOTION: {
            LOG_ALWAYS_FATAL_IF((entry.policyFlags & POLICY_FLAG_TRUSTED) == 0,
                                "Unexpected untrusted event.");
            if (shouldPruneInboundQueueLocked(static_cast<MotionEntry&>(entry))) {
                mNextUnblockedEvent = mInboundQueue.back();
                needWake = true;
            }
            break;
        }
        case EventEntry::Type::FOCUS: {
            LOG_ALWAYS_FATAL("Focus events should be inserted using enqueueFocusEventLocked");
            break;
        }
        case EventEntry::Type::TOUCH_MODE_CHANGED:
        case EventEntry::Type::CONFIGURATION_CHANGED:
        case EventEntry::Type::DEVICE_RESET:
        case EventEntry::Type::SENSOR:
        case EventEntry::Type::POINTER_CAPTURE_CHANGED:
        case EventEntry::Type::DRAG: {
            // nothing to do
            break;
        }
    }

    return needWake;
}

通过这两个函数可以看出,到达InputDispatcher的Motion事件被保存在MotionEntry类中,然后排在mInboundQueue列表的队尾,这个mInboundQueue就是InputDispatcher的派发队列。MotionEntry是EventEntry的一个子类,保存了Motion事件的信息。Key事件也有一个KeyEntry与之对应。EventEntry是输入事件在InputDispatcher中的存在形式。另外,由于InputDispatcher在没有事件可以派发时(mInboundQueue为空),将会进入休眠状态,因此在将事件放入派发队列时,需要将派发线程唤醒。

上述过程运行在inputReader线程中,至此inputReader的处理完成

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Nignx的搭建与核心配置

目录 一、Nginx是什么&#xff1f; 1、Nginx概述 2、Nginx模块与作用 3、Nginx三大作用&#xff1a;反向代理&#xff0c;负载均衡&#xff0c;动静分离 nginx七层负载均衡调度算法&#xff08;六种&#xff09; 1、轮询&#xff08;默认调度算法&#xff09; 2、加权轮…

雨量水位在线监测站

TH-SW2随着科技的不断进步&#xff0c;雨量水位在线监测站在防汛、水资源管理、环境保护等领域发挥着越来越重要的作用。传统的雨量水位监测方法往往存在精度不高、实时性不强等问题&#xff0c;而雷达式测量技术的出现&#xff0c;则为这一领域带来了革命性的变化。 一、雨量水…

【踩坑专栏】主机ping虚拟机失败

我出现的问题finalshell连接超时&#xff0c;ping了一下发现ping都ping不通&#xff0c;于是发现问题所在。 最开始我是把虚拟机的网络设置改为桥接模式&#xff0c;问题解决了&#xff0c;但是这种模式的问题就是每次开机&#xff0c;ip都会改变&#xff0c;因此非常麻烦&…

零到大师:嵌入式Linux学习书单分享

大家好&#xff0c;我是知微&#xff01; 上一篇推荐的书单嵌入式软件必读10本书_单片机篇&#xff0c;收到反响很好。再推荐一篇嵌入式Linux相关的书单。 《鸟哥的Linux私房菜》 鸟哥的Linux系列适合零基础小伙伴&#xff0c;从电脑基础到文件系统、shell脚本等等&#xff…

【PX4学习笔记】04.QGC地面站的使用

目录 文章目录 目录PX4代码烧入PX4固件代码的烧入方式1PX4固件代码的烧入方式2 QGC地面站的基础使用连接地面站的方式查看关键的硬件信息 QGC地面站的Application Settings模块Application Settings模块-常规界面单位其他设置数据持久化飞机中的数传日志飞行视图计划视图自动连…

Unity Shader ASE基础效果思路与代码(一):遮罩、硬边溶解、光边溶解、UV扰动

Unity Shader ASE基础效果思路与代码(一)&#xff1a;遮罩、硬边溶解、光边溶解、UV扰动 文章目录 Unity Shader ASE基础效果思路与代码(一)&#xff1a;遮罩、硬边溶解、光边溶解、UV扰动遮罩效果硬边溶解光边溶解UV扰动 遮罩效果 效果展示&#xff1a; 思路与代码&#xff1…

SQL注入:堆叠注入-强网杯[随便注]

目录 什么是堆叠注入&#xff1f; 强网杯-随便注 rename && alter绕过 prepare绕过 Handle绕过 靶机&#xff1a;BUUCTF在线评测 什么是堆叠注入&#xff1f; 在一些场景中&#xff0c;应用程序支持一次执行多条SQL语句&#xff0c;我们称为堆叠查询&#xff0c;…

HTML5-CSS3

一、HTML5的新特性 HTML5 的新增特性主要是针对于以前的不足&#xff0c;增加了一些新的标签、新的表单和新的表单属性等。 这些新特性都有兼容性问题&#xff0c;基本是 **IE9 以上版本的浏览器**才支持&#xff0c;如果不考虑兼容性问题&#xff0c;可以大量使用这些新特性…

前端是做什么的?

前端很多时候代表从事前端开发的工程师&#xff0c;定义上来说&#xff0c;他们负责规划、设计、构建和运行网站、软件程序和基于 Web 的应用程序的用户界面系统。通俗点来说&#xff0c;我们看到的网站、点击网站所有的按钮以及和网站的交互都是前端所进行的工作。 概述 前端…

pclpy 窗口可视化多个点云

pclpy 窗口可视化多个点云 一、算法原理二、代码三、结果1.可视化结果 四、相关数据五、问题与解决方案1.问题2.解决 一、算法原理 原理看一下代码写的很仔细的。。目前在同一个窗口最多可视化两个点云。。 二、代码 from pclpy import pcldef CloudShow(cloud1, cloud2):&q…

java数据结构与算法刷题-----LeetCode222. 完全二叉树的节点个数

java数据结构与算法刷题目录&#xff08;剑指Offer、LeetCode、ACM&#xff09;-----主目录-----持续更新(进不去说明我没写完)&#xff1a;https://blog.csdn.net/grd_java/article/details/123063846 1. 法一&#xff1a;利用完全二叉树性质&#xff0c;进行递归二分查找 解…

145. Binary Tree Postorder Traversal(二叉树的后序遍历)

问题描述 给你一棵二叉树的根节点 root &#xff0c;返回其节点值的 后序遍历 。 问题分析 此问题与二叉树的前序遍历分析类似&#xff0c;只是在数组合并的时候顺序发生一下变化就行了。 代码 int* postorderTraversal(struct TreeNode* root, int* returnSize) {if(root…

【电子书】计算机课程

资料 wx&#xff1a;1945423050 个人整理了一些互联网电子书 计算机课程 Netty权威指南&#xff08;第2版&#xff09;.epubSharePoint Server 2016 IT Pro 部署指南.epubTensorFlow自然语言处理.epubWebGIS之OpenLayers全面解析.epub从Paxos到Zookeeper分布式一致性原理与实践…

《图解设计模式》笔记(一)适应设计模式

图灵社区 - 图解设计模式 - 随书下载 评论区 雨帆 2017-01-11 16:14:04 对于设计模式&#xff0c;我个人认为&#xff0c;其实代码和设计原则才是最好的老师。理解了 SOLID&#xff0c;如何 SOLID&#xff0c;自然而然地就用起来设计模式了。Github 上有一个 tdd-training&…

Javascript中var和let之间的区别

文章目录 一.变量提升(声)二.let和var的区别 区别&#xff1a; 1、var有变量提升&#xff0c;而let没有&#xff1b; 2、let不允许在相同的作用域下重复声明&#xff0c;而var允许&#xff1b; 3、let没有暂时性死区问题&#xff1b; 4、let创建的全局变量没有给window设置对应…

【广度优先搜索】【网格】【割点】【 推荐】1263. 推箱子

作者推荐 视频算法专题 涉及知识点 广度优先搜索 网格 割点 并集查找 LeetCode:1263. 推箱子 「推箱子」是一款风靡全球的益智小游戏&#xff0c;玩家需要将箱子推到仓库中的目标位置。 游戏地图用大小为 m x n 的网格 grid 表示&#xff0c;其中每个元素可以是墙、地板或…

ctfshow web入门 web141-145

1.web141 ^\w$表示在开头和末尾匹配字母数字_&#xff0c;传入的v3值不能有字母数字_&#xff0c;即无字母的命令执行 php中1-phpinfo()是可以执行的&#xff0c;加减乘除都可以实现 这里或&#xff0c;异或&#xff0c;取反等运算都可以 这里采用羽师傅的异或脚本生成paylo…

小白水平理解面试经典题目LeetCode 404 Sum of Left Leaves【Tree】

404 左叶之和 小白翻译 给定二叉树的root&#xff0c;返回所有左叶的总和。 叶子是没有子节点的节点。左叶是另一个节点的左子节点的叶。 例子 小白教室做题 在大学某个自习的下午&#xff0c;小白坐在教室看到这道题。想想自己曾经和白月光做题&#xff0c;现在大过年的&a…

[服务器-数据库]MongoDBv7.0.4不支持ipv6访问

文章目录 MongoDBv7.0.4不支持ipv6访问错误描述问题分析错误原因解决方式 MongoDBv7.0.4不支持ipv6访问 错误描述 报错如下描述 Cannot connect to MongoDB.No suitable servers found: serverSelectionTimeoutMS expired: [failed to resolve 2408]问题分析 首先确定其是…

Github 2024-02-22 开源项目日报 Top10

根据Github Trendings的统计&#xff0c;今日(2024-02-22统计)共有10个项目上榜。根据开发语言中项目的数量&#xff0c;汇总情况如下&#xff1a; 开发语言项目数量Python项目4非开发语言项目2Go项目2HTML项目1Dart项目1Vue项目1JavaScript项目1TypeScript项目1 《Hello 算法…