aosp15版本InputReader事件如何通知到InputDispatcher的?

Android Framework学习路线:从Binder到SystemServer的系统进阶指南 Android系统架构中,应用层与底层之间的桥梁是Framework,它承载着跨进程通信、消息驱动与系统服务调度等关键机制。其中,Binder作为Android核心IPC通信骨架,依靠mmap实现一次拷贝,为应用与系统服务的高效交互提供基础;Handler消息循环则保障了单线程模型下任务的有序执行,是主线程稳定运行的根基。理解这些原理,不仅有助于优化应用性能,更是掌握系统定制与问题定位的前提。从应用启动到视图绘制,从触摸事件分发到画面合成,Framework贯穿始终。本文将结合实际工程视角,系统梳理从Bin 阅读详情

背景:

针对学员提出的新版本的事件通知notify部分,InputReader代码到底是如何调用到InputDispatcher的notifyMotion,notifyKey等方法的。
在这里插入图片描述

马哥这边看了aosp15的代码后,确实发现这块代码明显比以前绕了比较多,主要加入了多个层次的事件流转,有点雷士以前事件到了ViewRootImpl中要经过多个InputStage进行process,针对这块马哥今天搞一个文章专门来进行解释一下。

InputReader部分源码分析

aosp15版本源码如下
InputReader代码中在整个代码核心流程如下
frameworks/native/services/inputflinger/reader/InputReader.cpp

void InputReader::loopOnce() {
//省略部分不重要
//getEvents获取相关的RawEvent
    std::vector<RawEvent> events = mEventHub->getEvents(timeoutMillis);
  
  if (!events.empty()) {
  //处理原始RawEvent进行转变NotifyArgs
            mPendingArgs += processEventsLocked(events.data(), events.size());
        }
        
        //针对NotifyArgs进行notify
    for (const NotifyArgs& args : notifyArgs) {
        ATRACE_NAME("mNextListener.notify");
        mNextListener.notify(args);
    }

}

在新版本这里mNextListener.notify(args)具体调用到哪里呢?
mNextListener实际上是InputReader构造时候传递进来的

InputReader::InputReader(std::shared_ptr<EventHubInterface> eventHub,
                         const sp<InputReaderPolicyInterface>& policy,
                         InputListenerInterface& listener)
      : mContext(this),
        mEventHub(eventHub),
        mPolicy(policy),
        mNextListener(listener),

构造InputReader是在createInputReader进行构造的:
frameworks/native/services/inputflinger/reader/InputReaderFactory.cpp

//第二个参数就是给上面的mNextListener赋值的
std::unique_ptr<InputReaderInterface> createInputReader(
        const sp<InputReaderPolicyInterface>& policy, InputListenerInterface& listener) {
    return std::make_unique<InputReader>(std::make_unique<EventHub>(), policy, listener);
}

那么什么时候调用的createInputReader呢?
frameworks/native/services/inputflinger/InputManager.cpp

/**
 * The event flow is via the "InputListener" interface, as follows:
 *   InputReader
 *     -> UnwantedInteractionBlocker
 *     -> InputFilter
 *     -> PointerChoreographer
 *     -> InputProcessor
 *     -> InputDeviceMetricsCollector
 *     -> InputDispatcher
 */
InputManager::InputManager(const sp<InputReaderPolicyInterface>& readerPolicy,
                           InputDispatcherPolicyInterface& dispatcherPolicy,
                           PointerChoreographerPolicyInterface& choreographerPolicy,
                           InputFilterPolicyInterface& inputFilterPolicy) {
    mInputFlingerRust = createInputFlingerRust();

    mDispatcher = createInputDispatcher(dispatcherPolicy);
    mTracingStages.emplace_back(
            std::make_unique<TracedInputListener>("InputDispatcher", *mDispatcher));

    if (ENABLE_INPUT_FILTER_RUST) {
        mInputFilter = std::make_unique<InputFilter>(*mTracingStages.back(), *mInputFlingerRust,
                                                     inputFilterPolicy);
        mTracingStages.emplace_back(
                std::make_unique<TracedInputListener>("InputFilter", *mInputFilter));
    }

    if (ENABLE_INPUT_DEVICE_USAGE_METRICS) {
        mCollector = std::make_unique<InputDeviceMetricsCollector>(*mTracingStages.back());
        mTracingStages.emplace_back(
                std::make_unique<TracedInputListener>("MetricsCollector", *mCollector));
    }

    mProcessor = std::make_unique<InputProcessor>(*mTracingStages.back());
    mTracingStages.emplace_back(
            std::make_unique<TracedInputListener>("InputProcessor", *mProcessor));

    mChoreographer =
            std::make_unique<PointerChoreographer>(*mTracingStages.back(), choreographerPolicy);
    mTracingStages.emplace_back(
            std::make_unique<TracedInputListener>("PointerChoreographer", *mChoreographer));

    mBlocker = std::make_unique<UnwantedInteractionBlocker>(*mTracingStages.back());
    mTracingStages.emplace_back(
            std::make_unique<TracedInputListener>("UnwantedInteractionBlocker", *mBlocker));
//传递的mTracingStages.back作为第二个参数,
    mReader = createInputReader(readerPolicy, *mTracingStages.back());
}

这里看到最后 *mTracingStages.back()指的就是mTracingStages集合中最后一个,也就是

std::make_unique<TracedInputListener>("UnwantedInteractionBlocker", *mBlocker)

然后可以看到是一个TracedInputListener类型

/*
 * An implementation of the listener interface that traces the calls to its inner listener.
 */
class TracedInputListener : public InputListenerInterface {
public:
    explicit TracedInputListener(const char* name, InputListenerInterface& innerListener);

    virtual void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override;
    virtual void notifyKey(const NotifyKeyArgs& args) override;
    virtual void notifyMotion(const NotifyMotionArgs& args) override;
    virtual void notifySwitch(const NotifySwitchArgs& args) override;
    virtual void notifySensor(const NotifySensorArgs& args) override;
    virtual void notifyDeviceReset(const NotifyDeviceResetArgs& args) override;
    void notifyVibratorState(const NotifyVibratorStateArgs& args) override;
    void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) override;

private:
    InputListenerInterface& mInnerListener;
    const char* mName;
};

构造这个TracedInputListener时候会,传递一个UnwantedInteractionBlocker类型参数,会赋值给TracedInputListener的mInnerListener对象。
但是注意这里的UnwantedInteractionBlocker构造也需要一个参数InputListenerInterface,会赋值给UnwantedInteractionBlocker的成员变量mQueuedListener

UnwantedInteractionBlocker::UnwantedInteractionBlocker(InputListenerInterface& listener)
      : UnwantedInteractionBlocker(listener, isPalmRejectionEnabled()){};

UnwantedInteractionBlocker::UnwantedInteractionBlocker(InputListenerInterface& listener,
                                                       bool enablePalmRejection)
      : mQueuedListener(listener), mEnablePalmRejection(enablePalmRejection) {}

而且会依次类推到InputDispatcher对应的TracedInputListener

  std::make_unique<TracedInputListener>("InputDispatcher", *mDispatcher)

mDispatcher构造如下,也发现基础
frameworks/native/services/inputflinger/dispatcher/InputDispatcher.h

class InputDispatcher : public android::InputDispatcherInterface {
public:
    static constexpr bool kDefaultInTouchMode = true;

    explicit InputDispatcher(InputDispatcherPolicyInterface& policy);
    // Constructor used for testing.
    explicit InputDispatcher(InputDispatcherPolicyInterface&,
                             std::unique_ptr<trace::InputTracingBackendInterface>);

    void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override;
    void notifyKey(const NotifyKeyArgs& args) override;
    void notifyMotion(const NotifyMotionArgs& args) override;
    void notifySwitch(const NotifySwitchArgs& args) override;
    void notifySensor(const NotifySensorArgs& args) override;
    void notifyVibratorState(const NotifyVibratorStateArgs& args) override;
    void notifyDeviceReset(const NotifyDeviceResetArgs& args) override;
    void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) override;

frameworks/native/services/inputflinger/dispatcher/include/InputDispatcherInterface.h

/* Notifies the system about input events generated by the input reader.
 * The dispatcher is expected to be mostly asynchronous. */
class InputDispatcherInterface : public InputListenerInterface {

那么回到最开始mNextListener其实就是个TracedInputListener对象,里面的mInnerListener其实就是UnwantedInteractionBlocker,mNextListener的notify方法最后调用到如下代码:

void InputListenerInterface::notify(const NotifyArgs& generalArgs) {
    Visitor v{
            [&](const NotifyInputDevicesChangedArgs& args) { notifyInputDevicesChanged(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);
}

这里其实大家可以理解为会根据notify传递的参数类型决定调用具体notifyMotion,notifyKey等方法,这里涉及到了一个新语法Visitor相关,具体看下面ai解释。

新语法Visitor解释

这里直接使用AI搜索解释更为清楚

这段代码展示了C++17中std::variant和std::visit的典型用法,实现了一种类型安全的访问者模式。

代码结构分析
1. Visitor对象的定义

Visitor v{
    [&](const NotifyInputDevicesChangedArgs& args) { notifyInputDevicesChanged(args); },
    [&](const NotifyKeyArgs& args) { notifyKey(args); },
    // ... 其他lambda表达式
};

这里定义了一个Visitor对象v,它包含多个lambda函数,每个lambda对应一种可能的参数类型。

2. std::visit调用

std::visit(v, generalArgs);

std::visit会根据generalArgs实际存储的类型,自动调用对应的lambda函数。

底层机制
std::variant类型
generalArgs很可能是一个std::variant类型,类似:

using GeneralArgs = std::variant<
    NotifyInputDevicesChangedArgs,
    NotifyKeyArgs, 
    NotifyMotionArgs,
    // ... 其他参数类型
>;

Visitor的工作方式
编译时多态:
编译器会为每种可能的类型生成对应的调用代码

类型安全:
不需要手动类型检查或转换

高效:
通常通过函数表实现,比动态多态更轻量

等价的手动实现
如果没有std::visit,可能需要这样写:

// 传统方式 - 需要手动类型检查
if (std::holds_alternative<NotifyKeyArgs>(generalArgs)) {
    notifyKey(std::get<NotifyKeyArgs>(generalArgs));
} else if (std::holds_alternative<NotifyMotionArgs>(generalArgs)) {
    notifyMotion(std::get<NotifyMotionArgs>(generalArgs));
}
// ... 更多else if

应用场景
这种模式常见于:

事件处理系统(如本例)

消息传递

状态机

抽象语法树遍历

优势
类型安全:编译时检查所有类型都已处理

可扩展性:添加新类型时,编译器会提示需要更新Visitor

性能:避免了虚函数调用的开销

清晰性:所有处理逻辑集中在一处

如何到达的InputDispatcher的notifyMotion

其实在InputManager进行构造时候就有相关的注释进行说明:

 * The event flow is via the "InputListener" interface, as follows:
 *   InputReader
 *     -> UnwantedInteractionBlocker
 *     -> InputFilter
 *     -> PointerChoreographer
 *     -> InputProcessor
 *     -> InputDeviceMetricsCollector
 *     -> InputDispatcher

这里就没必要每个地方都进行分析了,最后用Perfetto展示他的调用流程如下:
在这里插入图片描述

可以看到从最开始的是到的UnwantedInteractionBlocker对应的TracedInputListener的中notify方法,一步步到InputDispatcher的notifyMotion方法中。

注意这里面的每个TracedInputListener中的notifyMotion等都有自带打印trace
在这里插入图片描述在这里插入图片描述
等。。。

它们trace的打印代码就是在如下代码中打印的:

frameworks/native/services/inputflinger/InputListener.cpp
在这里插入图片描述到此就分析完成了新版本aosp15上InputReader中的notify是如何一步步调用到InputDispatch的notifyMotion。

原文参考:
https://mp.weixin.qq.com/s/MbNAL1UqCsiGdUuasyuI_g

更多framework实战开发干货,请关注下面“千里马学框架”

[Android 13]Input系列--InputReader事件处理 Android 13。 阅读详情

相关推荐

Android开发学习链路和教程规划

这是一套系统化的Android开发教程,采用全新链路式教学法,从Linux内核到应用上线全流程解析。教程特色在于每个知识点都涵盖底层原理、源码链路、使用方法和工程治理四大维度,确保开发者获得完整认知体系。课程分为三个阶段:底层运行环境(Linux内核、Binder、Zygote等)、项目构建与运行时(Gradle、APK安装、资源系统等)、四大组件与UI开发(Activity、View、Jetpack等)。

9年Android老兵,专注车载系统与移动架构。深耕Java/Kotlin,玩转Lua/Python。擅长百度/高德地图深度定制,分享拒绝浅尝辄止的硬核干货。 66

事件上报处理流程小记(三)InputListener 事件处理链

【代码】InputListener 事件处理链。

qq_34841985的博客 643

codevs 1620 轮船问题 DP 解题报告

某国家被一条河划分为南北两部分,在南岸和北岸总共有N对城市,每一城市在对岸都有唯一的友好城市,任何两个城市都没有相同的友好城市。

Hawo11的博客 817

InputDispatcher创建说明

InputDispatcher创建说明

我的博客 268

串口调试神器诞生记:我是如何用Cursor+AI自动生成C#上位机的(含VSCode插件清单)

本文详细介绍了如何利用Cursor和AI技术快速开发C#上位机程序,实现高效的串口通讯调试。从开发环境搭建到代码自动生成,再到性能优化和功能增强,全面展示了AI辅助开发在串口调试中的实际应用和显著效率提升。

weixin_28340315的博客 301

Java开发者必看:5分钟搞定PaddleOCR表格识别,导出Excel和HTML超详细教程

本文为Java开发者提供了集成PaddleOCR实现智能表格识别的详细教程,包括环境配置、核心代码实现、结果导出到Excel和HTML的步骤,以及性能优化技巧。通过PaddleOCR技术,开发者可以快速将图片表格转换为结构化数据,大幅提升工作效率。

weixin_30872499的博客 99

Android 输入事件分发全流程梳理(一)

本文带着您根据Android U 14版本的AOSP源码探索输入事件的分发全流程。

moke926的博客 1567

牛耕式全覆盖算法-matlab代码

牛耕式路径全覆盖算法,也称为牛耕算法或者蚂蚁算法,是一种用于解决路径规划问题的启发式算法。这个算法的灵感来自于牛在耕田时的行为。在这个算法中,假设有一块田地需要耕作,牛从田地的某个角落开始行走,走过的路径会被标记。牛会优先选择尚未经过的路径,当所有的路径都走过后,算法停止。在路径规划问题中,田地可以看作是一个由节点和边构成的图。每个节点代表一个位置,每条边代表两个节点之间的连接。路径全覆盖问题是找出一组路径,使得每个节点都被至少一条路径经过。牛耕式路径全覆盖算法的基本思想是以启发式的方式逐步构建路径。

m0_69611489的博客 1690

input中Stylus事件的优先级高于Touch事件

触控笔优先级高于普通touch事件是在这里定义的 mPreferStylusOverTouchBlocker,当设备检测到触控笔事件时会drop touch事件内容,从而使touch事件无发分派。reader和dispatcher在InputManager中实例化,二者通过process和blocker联系。reader可以读取多种事件,以touch为例,将touch事件先通知到blocker再到process最后到dispatch。touch事件处理部分流程:reader->dispatcher。

qq_41278609的博客 582

BlenderGis安装碰到问题,ImageIO、无法点击和地图不加载

BlenderGis安装碰到问题,ImageIO、无法点击和地图不加载问题

qq_45176349的博客 4965

《C/C++等级考试(1~8级)历届真题解析》专栏总目录

中国电子学会《全国青少年软件编程等级考试》C/C++编程(1~8级)历届真题解析。订阅后可阅读专栏内所有真题解析,真题持续更新中!

人生就是一个不断学习的过程 2万+

Android Input 系列专题【inputflinger事件的读取与分发】

文章摘要: ServiceManager是Android Binder机制的核心管理组件,负责服务注册与查询。作为独立进程,它通过Binder驱动与其他进程通信。启动时完成三个关键步骤:1)打开并映射Binder驱动设备;2)注册为上下文管理者;3)进入循环处理服务请求。服务进程注册时,ServiceManager将句柄保存到链表并通知Binder驱动创建内核对象;客户端查询时,通过句柄查找服务。Binder驱动在内核空间维护红黑树管理服务引用,实现跨进程通信。整个过程体现了观察者模式的设计思想,通过用户空

qq_27672101的博客 2134

【技能】OpenClaw Skills学习及手搓一个极简skill

本文介绍了OpenClaw Skills的基础概念与使用方法。Skills是OpenClaw代理使用工具的核心载体,通过SKILL.md文件定义技能属性和执行方式。技能加载遵循"工作区>本地>内置>自定义目录"的优先级规则。用户可通过ClawHub一键安装现成技能,也可自定义开发技能:创建技能目录,编写包含YAML元数据和执行指令的SKILL.md文件,放置于工作区skills目录进行测试。文章还介绍了多代理场景下的技能共享、插件集成以及安全注意事项,包括沙箱使用和敏感

海兰 3976

Android InputReader层冻结屏幕触摸:AOSP定制实现与状态机恢复

从Android输入系统的事件分发链路说起,触摸事件从内核evdev经EventHub、InputReader、InputDispatcher到应用WindowManager。若需在系统级实现“屏幕触摸冻结但物理按键可用”,需在InputReader的TouchInputMapper层精准拦截,而非简单Window Flag或Dispatcher过滤。这是因为Reader层位于事件状态机合成之前,可彻底丢弃触摸而不污染触点状态。本文结合AOSP版本差异(Android 12重构前后),解析动态冻结开关设计、

weixin_31858171的博客 124

永磁同步电机MTPA控制详解:从理论到实践的全方位指南

永磁同步电机(PMSM)凭借其高功率密度卓越的效率以及精准的控制性能,已在工业驱动、电动汽车和家用电器等领域获得广泛应用。而在PMSM的诸多控制策略中,最大转矩电流比(MTPA)控制因其能显著提升电机效率而占据核心地位。在输出特定转矩时,通过优化直轴电流(Id)和交轴电流(Iq)的分配,使定子电流的幅值最小化。对于电机测试工程师而言,理解MTPA意味着掌握了一把提升系统性能的关键钥匙。为什么这一点如此重要?因为定子电流的减小直接带来了铜损的降低。

优快云-新能源汽车研发测试专业博主 518

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

android_touch_event_flow

android点击事件下发流程

weixin_42143160的博客 665

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

Android 返回手势详解

本文基于 AOSP 源码,梳理 SystemUI 的如何通过 InputDispatcher 的 Spy Window / Global Monitor 机制监听屏幕边缘返回手势,并分析事件分发、窗口注册、指针窃取的完整链路。

congqingbin的专栏 455

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

Android 10输入系统深度解析:epoll双缓冲与零拷贝事件分发机制

Android输入系统是连接硬件中断与应用交互的核心枢纽,其本质是一套多级缓冲、跨进程协同的实时事件处理架构。在Android 10(Q)版本中,系统摒弃了传统的ioctl轮询模式,转而采用epoll异步监听机制提升采集效率;同时引入mInboundQueue与mOutboundQueue双环形缓冲区,结合ashmem+eventfd实现零拷贝跨进程通信。这种设计显著降低了触摸延迟、避免了队列溢出丢事件,并为手势识别等策略逻辑提供了Java层动态注入能力。典型应用场景包括车载中控高并发触控、工业平板手套模式

weixin_29189003的博客 183

Android Input子系统介绍(一)

摘要:Android Input系统负责处理用户输入操作,支持触摸屏、键盘等多种外设。输入事件通过设备驱动转换为内核事件并写入/dev/input节点。系统核心组件包括EventHub(监控设备节点)、InputReader(获取事件)、InputDispatcher(分发事件)和InputManagerService(中央调度)。这些组件协作将硬件输入转换为上层应用可识别的KeyEvent或MotionEvent对象,完成从硬件到应用的完整输入流程。

lzz214的博客 539

Android 知识体系

Android归档前言 前言 感谢: Gityuan:Android系统架构开篇 刘望舒的博客:知识体系

许浑的博客 8199

Android系统真实体感:硬件约束、软件博弈与用户幻觉的动态平衡

Android并非抽象的操作系统概念,而是根植于物理世界的技术综合体。其运行本质由芯片寄存器、PCB信号完整性、传感器时钟同步和电池电化学特性等硬件铁律所限定;同时在AOSP开放框架、Vendor闭源Blob与OEM深度定制之间持续博弈;最终通过16ms渲染周期、触控预测插值与后台唤醒契约,构建出符合人眼与神经响应的‘流畅幻觉’。理解Android整体印象,关键在于穿透Framework层,直抵SoC启动序列、HAL时序偏差、Thermal HAL物理判决等实测细节——这正是Android系统工程师每日调试

diaoqi6581的博客 430

揭秘安卓系统架构:从内核到应用全解析

Android操作系统是基于Linux内核的开源移动操作系统,采用分层架构设计。其核心组件包括:Linux内核负责硬件资源管理;硬件抽象层(HAL)统一硬件接口;原生库提供核心功能支持;Android运行时(ART)执行应用代码;应用框架层为开发者提供API;系统应用和用户应用分别提供基础功能和扩展服务。这种层次化设计实现了软硬件解耦,确保了系统的稳定性和可扩展性。

本博客聚焦游戏开发技巧、创业实战心得及大学热门课程干货。这里既有技术深度,也有思维广度,无论你是开发者、创业者还是高校学子,都能在这里找到适合自己的成长之路! 2590

车机双屏测试为何必须用ADB+模拟器精准控制

车机双屏并非简单叠加的两个Android显示单元,而是基于独立GPU、不同刷新率与HAL层深度耦合的毫秒级协同系统。其核心原理在于DisplayManagerService、SurfaceFlinger与Hardware Composer三者在多DisplayPipeline下的时序调度与状态同步。技术价值体现在将不可控硬件异常(如CAN干扰、eDP vblank超时)转化为可编程的软件可观测指标,大幅提升边界场景复现效率。典型应用场景包括跨屏拖拽稳定性验证、异步刷新降级策略测试、HAL服务故障注入等。本文

weixin_34293911的博客 198

车机STR唤醒黑屏根因与治本方案:Display State同步与焦点仲裁

车载语音唤醒(STR)黑屏问题本质是Android Automotive图形栈中Display Power State与SurfaceFlinger帧提交的时序失配。其原理在于Display状态切换延迟导致图层可见性被静默置为false,且Input Dispatcher未及时完成焦点仲裁,引发SurfaceFlinger丢帧与视觉层遮罩。该问题凸显车载系统对时间确定性的严苛要求,技术价值在于保障HMI一致性、满足UNECE R155功能安全输入事件完整性,并规避ADAS/HUD交互失效风险。典型应用于高通

weixin_34241036的博客 245

车载Android USB接入实战:Host模式、串口、CAN与HID全链路解析

USB是嵌入式Linux系统中关键的外设接入总线,其在Android平台上的行为由内核驱动、HAL层、Framework权限模型及SELinux策略共同决定。理解USB Host模式启动机制、CDC ACM与vendor-specific设备(如CH340、USB-CAN)的驱动兼容性、HID Report Descriptor解析逻辑,是实现稳定外设通信的基础。技术价值体现在规避枚举失败、open拒绝、事件丢失等典型问题,支撑OBD-II诊断、方向盘HID控制、胎压CAN采集等车规级应用。本文聚焦Andr

cuixie2370的博客 276
上一篇: 学员投稿:wms优化改进dumpsys层级结构树显示具体层级数字
下一篇: 必学音频干货调试技巧:命令dumpsys audio相关信息剖析
千里马学框架
博客等级 码龄6年 1万+粉丝 650原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

千里马学框架

帮助你了,就请我喝杯咖啡

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值