Android系统开发(八):从麦克风到扬声器,音频HAL框架的奇妙之旅
引言:音浪太强,我稳如老 HAL!
如果有一天你的耳机里传来的不是《咱们屯里人》,而是金属碰撞般的杂音,那你可能已经感受到了 Android 音频硬件抽象层 (HAL) 出问题的后果!在 Android 音频架构中,HAL 扮演着连接音频应用和硬件的桥梁。这篇文章旨在揭开 Android 音频 HAL 的神秘面纱,解析其实现机制,带你了解背后的技术奥秘和开发技巧。音频是每款 Android 设备的灵魂,而理解音频 HAL 则是开发高品质音频应用的关键。音浪已经到来,快点开文章感受一下吧!
一、技术背景:听得见的技术艺术
Android 的音频架构覆盖了从应用层到硬件的整个链路:
- 应用层:
android.media
提供了高级别的音频 API,例如播放和录制功能。 - 中间层:音频框架与音频服务协调音频流的路由和处理。
- 硬件层:音频 HAL 是软件世界和硬件世界的接口,它定义了与音频驱动程序交互的规则。
随着音频技术的发展,设备厂商需要实现个性化的音频功能,例如 Dolby Atmos、Hi-Res Audio 等。而 HAL 则让 Android 系统不需要关心硬件底层的实现细节,使得音频功能的开发更高效、更灵活。
二、概念原理:HAL 是如何工作的?
音频 HAL 是一种硬件抽象层,位于 Android 音频框架与硬件驱动之间,核心机制包括:
- 接口定义:
audio.h
文件定义了音频 HAL 的标准接口。厂商需要实现这些接口,例如音频输入、输出、音量控制等。 - 模块加载:通过
hw_get_module()
函数加载音频 HAL 模块。 - 音频路由:通过 HAL 实现音频流的正确路由,如耳机、扬声器等。
- 驱动交互:HAL 与音频驱动程序交互,控制硬件执行音频操作。
简单来说,HAL 就像音频架构中的“翻译官”,让音频框架和硬件设备说“同一种语言”。
三、实现方法:如何开发音频 HAL?
开发步骤
-
环境准备:
- 下载并编译 AOSP 源码(需要适配目标设备)。
- 安装 Android NDK 和调试工具。
-
实现音频 HAL 接口:
- 创建音频 HAL 模块(
audio_hw.c
)。 - 实现
audio_hw_device
接口,例如初始化、音频流打开/关闭等。
- 创建音频 HAL 模块(
-
配置设备支持:
- 在
Android.mk
或CMakeLists.txt
中声明模块和依赖项。 - 修改设备树配置,关联 HAL 模块与硬件设备。
- 在
-
调试与验证:
- 使用
adb logcat
查看音频日志输出。 - 使用
tinyplay
、tinymix
工具测试音频流。
- 使用
项目实战:Android 音频 HAL 详细实践
以下是关于 Android 音频 HAL 实现的详细项目实战案例。所有代码都可以直接在编译环境中运行。
案例 1:实现基本的音频输出功能
目标:为设备自定义音频芯片实现基本的音频播放功能。
实现步骤:
-
实现音频输出流的 HAL 接口
在audio_hw.c
中定义并实现 HAL 所需的函数。 -
代码实现
创建音频设备和输出流结构,设置输出流的写入功能。
#include <hardware/audio.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>// 定义音频设备结构体
struct audio_device {struct audio_hw_device hw_device;// 其他必要的设备配置
};// 定义音频输出流结构体
struct audio_stream_out {struct audio_stream common;int (*write)(struct audio_stream_out *stream, const void *buffer, size_t bytes);int sample_rate;
};// 打开音频输出流
static int adev_open_output_stream(struct audio_hw_device *dev, audio_io_handle_t handle, audio_devices_t devices, audio_output_flags_t flags,struct audio_config *config, struct audio_stream_out **stream_out) {struct audio_stream_out *out_stream = calloc(1, sizeof(struct audio_stream_out));if (!out_stream) {return -ENOMEM;}out_stream->write = out_write; // 设置写入函数out_stream->sample_rate = config->sample_rate;*stream_out = out_stream;return 0;
}// 实现音频数据写入功能
static ssize_t out_write(struct audio_stream_out *stream, const void *buffer, size_t bytes) {// 模拟将音频数据写入硬件printf("Writing %zu bytes to audio hardware\n", bytes);// 实际场景应调用底层驱动接口return bytes;
}// 关闭音频输出流
static void adev_close_output_stream(struct audio_hw_device *dev, struct audio_stream_out *stream) {free(stream);
}// 打开音频设备
static int adev_open(const hw_module_t *module, const char *name, hw_device_t **device) {struct audio_device *adev = calloc(1, sizeof(struct audio_device));if (!adev) {return -ENOMEM;}adev->hw_device.common.module = (hw_module_t *)module;adev->hw_device.open_output_stream = adev_open_output_stream;adev->hw_device.close_output_stream = adev_close_output_stream;*device = (hw_device_t *)adev;return 0;
}// HAL 模块结构
static struct hw_module_methods_t hal_module_methods = {.open = adev_open,
};struct audio_module HAL_MODULE_INFO_SYM = {.common = {.tag = HARDWARE_MODULE_TAG,.module_api_version = AUDIO_MODULE_API_VERSION_0_1,.hal_api_version = HARDWARE_HAL_API_VERSION,.id = AUDIO_HARDWARE_MODULE_ID,.name = "Custom Audio HAL",.author = "Your Name",.methods = &hal_module_methods,},
};
案例 2:支持音量调节功能
目标:为音频输出流实现音量调节功能。
-
步骤说明
- 修改
audio_stream_out
结构,添加音量设置方法。 - 在
out_set_volume
函数中设置左右声道音量。
- 修改
-
代码实现
// 音量调节功能实现
static int out_set_volume(struct audio_stream_out *stream, float left, float right) {printf("Setting volume: left = %.2f, right = %.2f\n", left, right);// 实际场景中应通过驱动设置硬件音量return 0;
}// 在输出流结构中添加 set_volume 方法
static int adev_open_output_stream(struct audio_hw_device *dev, audio_io_handle_t handle, audio_devices_t devices, audio_output_flags_t flags, struct audio_config *config, struct audio_stream_out **stream_out) {struct audio_stream_out *out_stream = calloc(1, sizeof(struct audio_stream_out));if (!out_stream) {return -ENOMEM;}out_stream->write = out_write;out_stream->set_volume = out_set_volume; // 设置音量调节函数out_stream->sample_rate = config->sample_rate;*stream_out = out_stream;return 0;
}
案例 3:实现麦克风音频输入功能
目标:为设备的麦克风实现音频录制功能。
-
步骤说明
- 创建音频输入流结构,定义输入流的读取方法。
- 通过
adev_open_input_stream
接口打开音频输入流。
-
代码实现
// 定义音频输入流结构
struct audio_stream_in {struct audio_stream common;ssize_t (*read)(struct audio_stream_in *stream, void *buffer, size_t bytes);int sample_rate;
};// 打开音频输入流
static int adev_open_input_stream(struct audio_hw_device *dev, audio_io_handle_t handle, audio_devices_t devices, struct audio_config *config, struct audio_stream_in **stream_in) {struct audio_stream_in *in_stream = calloc(1, sizeof(struct audio_stream_in));if (!in_stream) {return -ENOMEM;}in_stream->read = in_read; // 设置读取函数in_stream->sample_rate = config->sample_rate;*stream_in = in_stream;return 0;
}// 实现音频数据读取功能
static ssize_t in_read(struct audio_stream_in *stream, void *buffer, size_t bytes) {printf("Reading %zu bytes from microphone\n", bytes);// 实际场景应从硬件获取音频数据memset(buffer, 0, bytes); // 模拟空数据return bytes;
}// 关闭音频输入流
static void adev_close_input_stream(struct audio_hw_device *dev, struct audio_stream_in *stream) {free(stream);
}// 注册输入流到设备
static int adev_open(const hw_module_t *module, const char *name, hw_device_t **device) {struct audio_device *adev = calloc(1, sizeof(struct audio_device));if (!adev) {return -ENOMEM;}adev->hw_device.common.module = (hw_module_t *)module;adev->hw_device.open_input_stream = adev_open_input_stream;adev->hw_device.close_input_stream = adev_close_input_stream;*device = (hw_device_t *)adev;return 0;
}
如何运行
-
配置设备支持:
在设备树文件中添加音频 HAL 的配置,确保设备能够加载audio_hw.c
编译后的模块。 -
编译并集成:
使用 Android 编译系统将音频 HAL 编译为共享库(.so
文件)。 -
测试功能:
- 使用
adb logcat
查看音频日志。 - 使用工具
tinyplay
播放音频文件验证输出功能。 - 使用
tinycap
录制音频文件验证输入功能。
- 使用
通过这些案例,您可以逐步实现并调试完整的音频 HAL 模块,从而掌握 Android 音频架构的核心开发技巧。
五、那些坑和技巧
- 音频卡检测失败:
- 检查设备树配置是否正确。
- 延迟高问题:
- 优化 HAL 中的缓冲区大小。
- 音质问题:
- 调整驱动程序的采样率和位深配置。
六、适配
- 优点:标准化接口,提升开发效率,易于硬件适配。
- 缺点:抽象层可能增加一定延迟,不适合对时延要求极高的场景。
七、性能评估
- 响应时间:音频 HAL 的延迟通常在 10ms 左右。
- 资源消耗:合理优化后的 HAL 实现对 CPU 和内存的影响较小。
八、展望
随着高分辨率音频和 AI 降噪技术的普及,音频 HAL 的发展方向包括支持更多音频格式、更智能的路由功能以及更高效的音频处理算法。
九、结语
通过本文,了解了 Android 音频 HAL 的实现方法及实际案例。音频 HAL 是 Android 音频架构的核心部分,对开发高品质音频应用至关重要。尝试自己动手实现一个 HAL 模块,感受音频开发的乐趣吧!
参考文献
以下是本文在撰写过程中使用的主要参考资料和资源,涵盖了 Android 音频架构相关的文档、技术书籍和实践案例,帮助读者深入学习和实践。
官方文档与代码仓库
-
Android 官方音频架构文档
- 描述了 Android 音频架构的整体设计与 HAL 的实现方式。
- 包括音频 HAL 接口、相关 API 和功能说明。
-
Android AOSP GitHub 仓库
- 提供音频 HAL 的参考实现代码。
- 重点关注
audio.h
和audio_policy.h
文件,它们定义了 HAL 的接口规范。
-
Android 内核源码仓库
- 具体查看
sound/soc/
目录,了解内核层驱动与音频硬件的交互。
- 具体查看
-
AudioFlinger
- Android 音频服务的核心部分。
- 分析如何与音频 HAL 和媒体服务交互。
书籍与经典参考资料
-
《Android Audio Internals》
- 作者:Karim Yaghmour
- 深入分析 Android 音频子系统的内部实现和工作机制。
-
《Mastering Embedded Linux Programming》
- 作者:Chris Simmonds
- 包括嵌入式音频开发和调试的技巧,适用于 Android 驱动层开发。
-
《Linux Device Drivers》
- 作者:Jonathan Corbet
- 经典书籍,讲解内核模块开发基础,涵盖音频驱动相关的内容。
-
《Android 系统级开发实战》
- 以实战案例讲解 Android 音频架构中的 HAL 和驱动开发。
技术文章与博客
-
《Android Audio HAL 开发详解》
- 链接:文章地址
- 包含从音频流定义到音量控制的完整实现。
-
《AudioFlinger 与 Audio HAL 的交互机制》
- 链接:文章地址
- 专注于分析 AudioFlinger 的工作流程和 HAL 的接口调用。
-
《音频驱动开发:从 Linux 到 Android》
- 链接:文章地址
- 探讨从 Linux 到 Android 音频驱动的移植与优化。
工具与库
-
Tinyalsa
- 链接:https://github.com/tinyalsa/tinyalsa
- 用于测试音频 HAL 的简单工具,可以快速验证音频流的输入与输出功能。
-
ALSA Utils
- 链接:https://alsa-project.org/
- 音频开发和调试的重要工具包,提供诸如
aplay
、arecord
等功能。
-
PulseAudio
- 链接:https://www.freedesktop.org/wiki/Software/PulseAudio/
- 高级音频管理工具,适用于理解音频系统的高级功能。
社区与论坛
-
Android 开发者社区
- 链接:https://developer.android.com/community
- 包括开发者博客、社区答疑等资源。
-
Stack Overflow 音频 HAL 相关问答
- 链接:https://stackoverflow.com/questions/tagged/android-audio
- 解决开发过程中常见的疑难问题。
-
Kernel Newbies
- 链接:https://kernelnewbies.org/
- 提供关于内核开发的入门教程和讨论。
调试与性能优化资料
-
《Android HAL 调试工具使用指南》
- 描述如何使用
adb shell
和日志工具分析音频问题。 - 涉及
dumpsys media.audio_flinger
和dmesg
命令的使用。
- 描述如何使用
-
《音频性能优化与调试最佳实践》
- 详细说明如何优化音频流的延迟、提高采样率以及调试驱动问题。
-
Google Perfetto 工具
- 链接:https://perfetto.dev/
- Android 官方推荐的性能追踪工具,适用于音频流的性能分析。
开发环境与测试平台
-
Android Open Source Project (AOSP)
- 链接:https://source.android.com/
- 配置和编译 AOSP 的完整指南。
-
Linaro Toolchain
- 链接:https://www.linaro.org/downloads/
- 提供高性能的交叉编译工具链,适合音频模块的开发。
-
qemu 和真实设备
- 通过模拟器和开发板(如 Raspberry Pi)进行测试,以确保兼容性。
欢迎关注 GongZhongHao,码农的乌托邦,程序员的精神家园!
相关文章:
Android系统开发(八):从麦克风到扬声器,音频HAL框架的奇妙之旅
引言:音浪太强,我稳如老 HAL! 如果有一天你的耳机里传来的不是《咱们屯里人》,而是金属碰撞般的杂音,那你可能已经感受到了 Android 音频硬件抽象层 (HAL) 出问题的后果!在 Android 音频架构中,…...
Golang Gin系列-2:搭建Gin 框架环境
开始网络开发之旅通常是从选择合适的工具开始的。在这个全面的指南中,我们将引导你完成安装Go编程语言和Gin框架的过程,Gin框架是Go的轻量级和灵活的web框架。从设置Go工作空间到将Gin整合到项目中,本指南是高效而强大的web开发路线图。 安装…...
FGC_grasp复现
复现FGC_grasp 环境配置数据集准备RuntimeError: CUDA error: invalid device ordinal 问题的解决方案raise BadZipFile("File is not a zip file") zipfile.BadZipFile: File is not a zip file问题的解决方案加载数据集时总是被kill然后服务器也卡住了动不了问题的…...
实力认证 | 海云安入选《信创安全产品及服务购买决策参考》
近日,国内知名安全调研机构GoUpSec发布了2024年中国网络安全行业《信创安全产品及服务购买决策参考》,报告从产品特点、产品优势、成功案例、安全策略等维度对各厂商信创安全产品及服务进行调研了解。 海云安凭借AI大模型技术在信创安全领域中的创新应用…...
Avalonia系列文章之小试牛刀
最近有朋友反馈,能否分享一下Avalonia相关的文章,于是就抽空学习了一下,发现Avalonia真的是一款非常不错的UI框架,值得花时间认真学习一下,于是边学习边记录,整理成文,分享给大家,希…...
中国数字安全产业年度报告(2024)
数字安全是指,在全球数字化背景下,合理控制个人、组织、国家在各种活动中面临的数字风险,保障数字社会可持续发展的政策法规、管理措施、技术方法等安全手段的总和。 数字安全领域可从三个方面对应新质生产力的三大内涵:一是基于大型语言模型…...
LabVIEW桥接传感器配置与数据采集
该LabVIEW程序主要用于配置桥接传感器并进行数据采集,涉及电压激励、桥接电阻、采样设置及错误处理。第一个VI("Auto Cleanup")用于自动清理资源,建议保留以确保系统稳定运行。 以下是对图像中各个组件的详细解释&#…...
简明docker快速入门并实践方法
简明docker快速入门并实践方法 前言:1. 什么是Docker?2. Docker的基本概念3. 安装配置Docker4. Docker基本命令:5. 简单实践:拉取Nginx镜像-自定义配置-推送镜像步骤 1:拉取Nginx镜像步骤 1.5(可选…...
《MambaIR:一种基于状态空间模型的简单图像修复基线方法》学习笔记
paper:2402.15648 目录 摘要 一、引言 1、模型性能的提升依赖于网络感受野的扩大: 2、全局感受野和高效计算之间存在固有矛盾: 3、改进版 Mamba的巨大潜力 4、Mamba 在图像修复任务中仍面临以下挑战: 5、方法 6、主要贡献…...
链式前向星的写法
【图论02】动画说图的三种保存方式 降低理解门槛 邻接表 链式前向星 邻接矩阵_哔哩哔哩_bilibili 杭电ACM刘老师-算法入门培训-第12讲-拓扑排序及链式前向星_哔哩哔哩_bilibili 图论003链式前向星_哔哩哔哩_bilibili(链式前向星的遍历) head数组的下标…...
【逆境中绽放:万字回顾2024我在挑战中突破自我】
🌈个人主页: Aileen_0v0 🔥热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 💫个人格言:“没有罗马,那就自己创造罗马~” 文章目录 一、引言二、个人成长与盘点情感与心理成长学习与技能提升其它荣誉 三、年度创作历程回顾创作内容概…...
尺取法(算法优化技巧)
问题和序列的区间有关,且需要操作两个变量,可以用两个下标(指针)i 和 j 扫描区间。 1,反向扫描,i 从头,j 从尾,在中间相遇。 例1.1(P37) 找指定和的整数对…...
基于 K-Means 聚类分析实现人脸照片的快速分类
注:本文在创作过程中得到了 ChatGPT、DeepSeek、Kimi 的智能辅助支持,由作者本人完成最终审阅。 在 “视频是不能 P 的” 系列文章中,博主曾先后分享过人脸检测、人脸识别等相关主题的内容。今天,博主想和大家讨论的是人脸分类问题。你是否曾在人群中认错人,或是盯着熟人的…...
【漏洞预警】FortiOS 和 FortiProxy 身份认证绕过漏洞(CVE-2024-55591)
文章目录 一、产品简介二、漏洞描述三、影响版本四、漏洞检测方法五、解决方案 一、产品简介 FortiOS是Fortinet公司核心的网络安全操作系统,广泛应用于FortiGate下一代防火墙,为用户提供防火墙、VPN、入侵防御、应用控制等多种安全功能。 FortiProxy则…...
7.5.4 MVCC优化测试
作者: h5n1 原文来源: https://tidb.net/blog/4e02d900 1. 背景 由于MVCC 版本数量过多导致rocksdb扫描key数量过多影响SQL执行时间是tidb经常出现问的问题,tidb也一直在致力于优化该问题。 一些优化方式包括比: (1) 从传统…...
STM32 FreeRTOS 事件标志组
目录 事件标志组简介 基本概念 1、事件位(事件标志) 2、事件组 事件组和事件位数据类型 事件标志组和信号量的区别 事件标志组相关API函数介绍 事件标志组简介 基本概念 当在嵌入式系统中运行多个任务时,这些任务可能需要相互通信&am…...
生成树机制实验
1 实验内容 1、基于已有代码,实现生成树运行机制,对于给定拓扑(four_node_ring.py),计算输出相应状态下的生成树拓扑 2、构造一个不少于7个节点,冗余链路不少于2条的拓扑,节点和端口的命名规则可参考four_node_ring.py,使用stp程序计算输出生成树拓扑 2 实验原理 一、…...
企业分类相似度筛选实战:基于规则与向量方法的对比分析
文章目录 企业表相似类别筛选实战项目背景介绍效果展示基于规则的效果基于向量相似的效果 说明相关文章推荐 企业表相似类别筛选实战 项目背景 在当下RAG(检索增强生成)技术应用不断发展的背景下,掌握文本相似算法不仅能够助力信息检索&…...
2024年博客之星年度评选—创作影响力评审入围名单公布
2024年博客之星活动地址https://www.csdn.net/blogstar2024 TOP 300 榜单排名 用户昵称博客主页 身份 认证 评分 原创 博文 评分 平均 质量分评分 互动数据评分 总分排名三掌柜666三掌柜666-CSDN博客1001002001005001wkd_007wkd_007-CSDN博客1001002001005002栗筝ihttps:/…...
递归40题!再见递归
简介:40个问题,有难有易,均使用递归完成,需要C/C的指针、字符串、数组、链表等基础知识作为基础。 1、数字出现的次数 由键盘录入一个正整数,求该整数中每个数字出现的次数。 输入:19931003 输出…...
社区版Dify实现文生视频 LLM+ComfyUI+混元视频
社区版Dify实现文生视频 LLMComfyUI混元视频 一、 社区版Dify实现私有化混元视频效果二、为什么社区版Dify可以在对话框实现文生视频?LLMComfyUI混元视频 实现流程图(重点)1. 文生视频模型支持ComfyUI2. ComfyUI可以轻松导出API实现封装3. Di…...
【LLM】Openai-o1及o1类复现方法
note 可以从更为本质的方案出发,通过分析强化学习的方法,看看如何实现o1,但其中的核心就是在于,如何有效地初始化策略、设计奖励函数、实现高效的搜索算法以及利用强化学习进行学习和优化。 文章目录 note一、Imitate, Explore, …...
jlatexmath-android如何实现自定义渲染字符
使用jlatexmath-android的过程,如果出现个别字符渲染不了,会导致无法显示 常用的做法是新增自定义字体切换系统默认字体渲染,下面我们介绍第2种方法。 修改流程: 1、到jlatexmath-android的官网下载源码到本地,导入…...
dockerhub上一些镜像
K8s下网络排障工具 https://hub.docker.com/r/nicolaka/netshoot ex kubectl run tmp-shell --rm -i --tty --image nicolaka/netshoot -- /bin/bash # 主机的net ns下运行 kubectl run tmp-shell --rm -i --tty --overrides{"spec": {"hostNetwork": tru…...
Python 爬虫学习指南与资料分享
Python爬虫学习资料 Python爬虫学习资料 Python爬虫学习资料 在数字化浪潮中,Python 爬虫作为强大的数据获取工具,为众多领域提供关键支持。想要系统掌握这门技术,以下的学习指南与资料分享将为你照亮前行道路。 一、学习指南 入门奠基 …...
TypeScript特有运算符和操作符
文章目录 TypeScript 特有运算符1. keyof2. typeof3. in4. extends5. is6. as 和 <Type>7. never8. readonly9. ?10. []11. | 和 &12. !13. ?? 和 ?. 总结 TypeScript 特有运算符 1. keyof 作用:用于获取对象类型的所有键(属性名&#x…...
介绍下常用的前端框架及时优缺点
以下是一些常用的前端框架及其优缺点介绍: React • 优点 • 组件化架构:可构建可复用的UI组件,提高开发效率和组件可维护性。 • 虚拟DOM:高效更新页面,减少直接操作DOM的性能开销。 • 灵活性和可扩展性…...
MATLAB算法实战应用案例精讲-【数模应用】图形变换和复杂图形组合(附python和MATLAB代码实现)
目录 前言 算法原理 变换 1二维变换 1.1缩放 1.2 翻转 1.3剪切 1.4 旋转 2齐次坐标 2.1引入齐次坐标的原因 2.2 二维齐次坐标 2.3二维仿射变换 2.4逆变换 4组合变换 5三维变换(由二维变换推理而来) 5.1三维齐次坐标 5.2 三维仿射变换 5.3 缩放和平移 5.4…...
SpringMVC 实战指南:打造高效 Web 应用的秘籍
第一章:三层架构和MVC 三层架构: 开发服务器端,一般基于两种形式,一种 C/S 架构程序,一种 B/S 架构程序使用 Java 语言基本上都是开发 B/S 架构的程序,B/S 架构又分成了三层架构三层架构: 表现…...
doris: Flink导入数据
使用 Flink Doris Connector 可以实时的将 Flink 产生的数据(如:Flink 读取 Kafka,MySQL 中的数据)导入到 Doris 中。 使用限制 需要依赖用户部署的 Flink 集群。 使用 Flink 导入数据 使用 Flink 导入数据,详…...
公众号如何做网站/搜索引擎培训班
两条斜线 链接:https://ac.nowcoder.com/acm/problem/18951 题目描述 平面上有n个点,现在你需要建造两条路,一条是斜率为1, 另一条斜率为-1 你的任务是让这两条路经过尽可能多的点 求最多经过几个点 输入描述: 第一行输入一个整数N表示点的个数 第二行…...
wordpress 很慢/百度竞价排名是什么意思
windows命令行官网教程: https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/windows-commands 截图软件用的是: FastStone Capture ————————————————————————————————————————…...
2015年做那个网站能致富/网站模板哪里好
ZAB(Zookeeper Atomic Broadcast) 协议是为分布式协调服务 ZooKeeper 专门设计的一种支持崩溃恢复的原子广播协议。在 ZooKeeper 中,主要依赖 ZAB 协议来实现分布式数据一致性,基于该协议,ZooKeeper 实现了一种主备模式…...
本地旅游网站模版/公司网站推广方法
python神器 Jupyter Notbook 简介 Jupyter Notebook是基于网页的用于交互计算的应用程序。其可被应用于全过程计算:开发、文档编写、运行代码和展示结果。 Jupyter Notebook官方 简而言之,Jupyter Notebook是以网页的形式打开,可以在网页页面…...
住房和城乡建设委网站/百度客服号码
一、 请问你是如何做接口测试的? 大体来说,经历以下过程:接口需求调研、接口测试工具选择、接口测试用例编写、接口测试执行、接口测试回归、接口测试自动化持续集成。具体来说,接口测试流程分成以下九步: 第一步&am…...
网站 中文版与英文版的后台有什么不同/色盲测试卡
导读 本文是 30 个极简任务,初学者可以尝试着自己实现;本文同样也是 30 段代码,Python 开发者也可以看看是不是有没想到的用法。 学 Python 怎样才最快,当然是实战各种小项目, 只有自己去想与写,才记得住…...