阿里巴巴Flutter技术全解析 阿里巴巴fastjson
liebian365 2024-10-23 13:55 26 浏览 0 评论
作者|阿里巴巴科技
谷歌的 Flutter 为开发人员提供了一种构建 Android 和 iOS 原生用户界面的方法,为开发人员减少了很多相关的负担,包括帮助他们节省时间。有些人可能想知道 Flutter 项目和原生 Android 或 iOS 项目有何不同,例如它们的渲染机制或事件传递机制,或者在出现问题时开发人员如何修复错误和实现项目。
为了回答这些问题,这篇文章将以“hello_flutter”为例,首先介绍 Flutter 的设计原则,然后讨论定制和优化,并为对 Flutter 感兴趣的开发人员提供了一些可遵循的步骤。
Flutter 基础
架构
Flutter 的架构包含三个不同的层:框架、引擎和嵌入器。
Flutter 的框架使用 Dart 实现,提供了 Material 风格的小部件、Cupertino 风格的小部件(用于 iOS)、文本 / 图像 / 按钮小部件、渲染、动画、手势等。该层的核心代码包含了 flutter 代码库的包和 sky_engine 代码库的包(dart:ui 库提供了 flutter 框架和引擎之间的接口),例如 io、async 和 ui 包。
Flutter 的引擎是用 C++ 实现的,并包含了 Skia、Dart 和 Text。Skia 是一个开源的 2D 图形库,为各种硬件和软件平台提供通用 API。它是谷歌 Chrome、Chrome OS、Android、Mozilla Firefox、Firefox OS 等产品的图形引擎。支持的平台包括 Windows7+、macOS 10.10.5+、iOS8+、Android4.1+、Ubuntu14.04+ 等。
引擎的 Dart 部分主要包括 Dart 运行时和垃圾回收(GC)。如果 Flutter 在调试模式下运行,则还包括 JIT(Just in Time)支持,而如果是在发布模式下,则通过 AOT(Ahead of Time)将 Dart 代码编译为原生的“arm”代码,这个时候就没有 JIT。Text 是指以下的文本渲染库:libtxt 库(用于字体选择和分隔线),派生自 minikin 和 HartBuzz(用于字形和图形)。Skia 充当渲染后端,在 Android 上使用 FreeType 渲染,在 iOS 上使用 Fuchsia 和 CoreGraphics 渲染。
嵌入器可将 Flutter 嵌入到各种平台中。它的主要任务是渲染 Surface 设置、线程设置和插件。我们可以看到,Flutter 的平台相关层是最小的,其中平台(例如 iOS)只提供画布,其余与渲染相关的逻辑发生在 Flutter 内部,从而实现良好的跨平台一致性。
工程结构
本文中使用的开发环境是 Flutter beta v0.3.1,对应的引擎提交标签为 09d05a389。
“hello_flutter”示例项目的工程结构如下:
在上面的例子中,“ios”是 iOS 的代码,使用 CocoaPods 来管理依赖项,“android”是 Android 的代码,使用 Gradle 来管理依赖项,“lib”是 Dart 代码,使用 pub 来管理依赖项。pub 中与 Cocoapods 的 Podfile 和 Podfile.lock 相对应的分别是 pubspec.yaml 和 pubspec.lock。
模式
Flutter 支持常见的模式,包括调试、发布和分析,但它们之间存在一些区别。
Flutter 的调试模式对应于 Dart 的 JIT 模式,也称为检查模式或慢模式,并支持 iOS 和 Android 的设备和模拟器。在这个模式下,可以启用断言功能,包括所有调试信息、服务扩展和调试辅助工具,如“observatory”。这个模式针对快速开发进行了优化,但并没有针对运行速度、程序包大小或部署进行过优化。在这种模式下,采用了基于 JIT 的编译技术,支持流行的亚秒级有状态热重载。
Flutter 的发布模式对应于 Dart 的 AOT 模式,该模式的目标是部署到最终用户的设备上,支持真实设备而不是模拟器。在此模式下,所有断言都被禁用,为了尽可能多地删除调试信息,还会禁用所有调试工具。这个模式针对快速启动、快速运行和包大小进行了优化,同时禁止所有调试辅助和服务扩展。
Flutter 的分析模式与发布模式类似,只是添加了对服务扩展和跟踪的支持,并最小化使用跟踪信息所需的依赖性。例如,“observatory”可以连到进程上。分析模式不支持模拟器,因为模拟器上的诊断不能代表实际性能。
由于 Flutter 的分析模式和发布模式在编译方面没有差异,因此本文仅讨论调试模式和发布模式。
实际上,使用 Flutter 开发的 iOS 或 Android 项目仍然是标准的 iOS 或 Android 项目。Flutter 通过在 BuildPhase 中添加 shell 来生成并嵌入 App.framework 和 Flutter.framework(iOS),并通过 Gradle 添加 flutter.jar 和 vm/isolate_snapshot_data/instr(Android)来编译相关代码并将其嵌入到原生应用程序中。因此,本文主要讨论 Flutter 引入的构建和运行原则。尽管编译目标包括 arm、x64、x86 和 arm64,但它们的原理都很类似,所以本文仅讨论与 arm 相关的原则。(如果没有特殊描述,Android 默认为 armv7。)
iOS 的代码编译和执行
在发布模式下编译
在发布模式下,iOS 项目的 Dart 代码构建过程如下:
在图中,gen_snapshot 是 Dart 编译器,它使用摇树优化技术(类似于可生成最小包的依赖树逻辑,因此在 Flutter 中禁用 Dart 支持的反射)生成汇编形式的机器码,然后通过编译工具链(如 xcrun)生成最终 App.framework。换句话说,对于所有的 Dart 代码,包括业务逻辑代码和第三方软件包代码,它们所依赖的 Flutter 框架(Dart)代码最终会变成 App.framework。
摇树优化功能位于 gen_snapshot 中。要查看相应的逻辑,可以访问:
engine/src/third_party/dart/runtime/vm/compiler/aot/precompiler.cc
Dart 代码最终对应于 App.framework 的符号如下:
事实上,与 Android 类似,App.framework 也包括了四个部分:kDartVmSnapshotData、kDartVmSnapshotInstructions、kDartIsolateSnapshotData 和 kDartIsolateSnapshotInstructions。为什么 iOS 使用 App.framework 而不是像 Android 那样的四个文件?由于 iOS 系统的限制,Flutter 引擎无法在运行时将内存页标记为可执行,而在 Android 下则可以。
Flutter.framework 对应于 Flutter 架构中的引擎和嵌入器。实际上,Flutter.framework 位于 flutter 代码库的 /bin/cache/artifacts/engine/ios * 中,默认是从谷歌的代码库中提取的。当需要自定义变更时,可以使用 Ninja 系统下载和构建与引擎相关的代码。
Flutter 相关代码的最终产物是 App.framework(由 Dart 代码生成)和 Flutter.framework(引擎)。在 Xcode 项目中,Generated.xcconfig 描述了与 Flutter 相关的环境配置信息,在 Runner 项目设置中新增的 xcode_backend.sh 实现了一个副本(从 flutter 框架代码库到 Runner 项目根目录下的 Flutter 目录)和 Flutter.framework 的嵌入以及 App.framework 的编译和嵌入。
最终生成的 Runner.app 中与 Flutter 相关的文件如下:
flutter_assets 是 Flutter 资源,App.framework 和 Flutter.framework 是代码,位于 Frameworks 目录中。
在发布模式下运行
与 Flutter 相关的渲染、事件和通信处理的逻辑如下:
Dart 主函数的调用栈如下:
在调试模式下编译
在调试模式下,Flutter 的编译结构与发布模式中的编译结构类似。差异主要表现在两点:
1.Flutter.framework
在调试模式下,框架包含 JIT 支持,而在发布模式下没有 JIT 支持。
2.App.framework
与 Flutter.framework 不同,App.framework 是原生机器码,与 AOT 模式中的 Dart 代码对应,而在 JIT 模式下,App.framework 只有几个简单的 API,Dart 代码存在于 snapshot_blob.bin 文件中。这部分代码的快照是带有简单标记的源代码的脚本快照。所有的注释和空格字符都被移除,常量被规格化,不存在机器代码、摇树优化或代码混淆。
App.framework 中的符号表如下:
针对 Runner.app/flutter_assets/snapshot_blob.bin 上运行 strings 命令可以查看以下内容:
调试模式下主入口的调用栈如下:
Android 的代码编译和执行
除了一些与平台相关的功能之外,Android 其他逻辑(例如对应于 AOT 的发布模式和对应于 JIT 的调试模式)与 iOS 非常相似,只需注意两个关键差异。
在发布模式下编译
在发布模式下,Android Flutter 项目中的 Dart 代码结构如下:
vm/isolate_snapshot_data/instr 是 arm 指令,引擎会在运行时加载它们并将其标记为可执行。vm_snapshot_data/instr 用于初始化 DartVM,调用入口为 Dart_Initialize(Dart_api.h)。isolate_snapshot_data/instr 对应于创建新隔离的 App 代码,调用入口为 Dart_CreateIsolate(Dart_api.h)。
Flutter.jar 类似于 iOS 的 Flutter.framework,包括引擎代码(Flutter.jar 中的 libflutter.so)以及一组将 Flutter 嵌入到 Android 中的类和接口(FlutterMain、FlutterView、FlutterNativeView 等)。事实上,flutter.jar 位于 Flutter 代码库的 /bin/cache/artifacts/engine/android* 中,默认从谷歌代码库中拉取。当需要自定义更改时,可以使用 Ninja 系统下载引擎源代码来生成 flutter.jar。
以 isolate_snapshot_data/instr 为例,运行 disarm 命令的结果如下:
其 APK 结构如下:
在全新安装 APK 之后,使用时间戳(结合 versionCode 与 packageinfo 的 lastUpdateTime)来决定是否将 Flutter 相关文件复制到本地 app 数据目录。复制的内容如下:
isolate/vm_snapshot_data/instr 最终位于 app 本地数据目录中,该目录是可写的。因此,可以通过下载和替换这些快照就可以完成 app 的替换和更新。
在发布模式下执行
下图显示了发布模式下的执行流程:
在调试模式下编译
与 iOS 的情况一样,Android 中的调试模式和发布模式之间的区别主要在于以下两个组件:
1.flutter.jar
这里的区别与之前针对 iOS 所描述的完全相同。
2.app 代码
app 代码位于 flutter_assets 下的 snapshot_blob.bin 中,就像 iOS 一样。
在介绍完 Flutter 有关 iOS 和 Android 的编译原理后,我们将重点介绍如何配置 Flutter 及其引擎,以进行自定义和优化。因为 Flutter 使用了敏捷开发模式,所以当前出现的问题在未来可能就不是问题。因此,以下部分不着重于如何解决问题,而是着重于不同类型的场景,这些场景体现了 Flutter 自定义和优化方面的原则。
在 Flutter 中进行定制和优化开发
Flutter 是一个复杂的系统。除了上面提到的三层架构,它还包括 Flutter Android Studio(Intellij)插件、pub 代码库管理和其他各种组件。不过,定制和优化通常与 Flutter 的工具链有关,代码位于 Flutter 代码库的 flutter_tools 包中。我们现在将分别介绍如何针对 Android 和 iOS 进行自定义。
自定义 Android
自定义 Android 涉及 flutter.jar、libflutter.so(在 flutter.jar 中)、gen_snapshot、flutter.gradle 和 flutter_tools。在自定义 Flutter 时,需要注意以下事项:
1. 将 Android 中的目标设置为 armeabi
这是构建过程的一部分,逻辑是在 flutter.gradle 中定义的。如果要应用程序通过 armeabi 支持 armv7/arm64,必须修改 Flutter 的默认逻辑,如下所示:
由于 Gradle 本身的特性,这部分可以在修改后生效。
2. 将 Android 设置为在启动时默认使用第一个可启动的 Activity
这部分与 flutter_tools 有关,修改如下:
这里的要点不是如何修改它,而是如何让更改生效。原则上,使用“flutter run/build/analyze/test/upgrade”这样的命令实际上运行的是 Flutter 脚本(flutter_repo_dir/bin/flutter),然后再通过这个脚本运行 flutter_tools.snapshot(由 packages/flutter_tools 生成)。逻辑如下:
if [[ ! -f "SNAPSHOT_PATH" ]] || [[ ! -s "STAMP_PATH" ]] || [[ "(cat "STAMP_PATH")" != "revision" ]] || [[ "FLUTTER_TOOLS_DIR/pubspec.yaml" -nt "$FLUTTER_TOOLS_DIR/pubspec.lock" ]]; then
rm -f "$FLUTTER_ROOT/version"
touch "$FLUTTER_ROOT/bin/cache/.dartignore"
"$FLUTTER_ROOT/bin/internal/update_dart_sdk.sh"
echo Building flutter tool...
if [[ "$TRAVIS" == "true" ]] || [[ "$BOT" == "true" ]] || [[ "$CONTINUOUS_INTEGRATION" == "true" ]] || [[ "$CHROME_HEADLESS" == "1" ]] || [[ "$APPVEYOR" == "true" ]] || [[ "$CI" == "true" ]]; then
PUB_ENVIRONMENT="$PUB_ENVIRONMENT:flutter_bot"
fi
export PUB_ENVIRONMENT="$PUB_ENVIRONMENT:flutter_install"
if [[ -d "$FLUTTER_ROOT/.pub-cache" ]]; then
export PUB_CACHE="${PUB_CACHE:-"$FLUTTER_ROOT/.pub-cache"}"
fi
while : ; do
cd "$FLUTTER_TOOLS_DIR"
"$PUB" upgrade --verbosity=error --no-packages-dir && break
echo Error: Unable to 'pub upgrade' flutter tool. Retrying in five seconds...
sleep 5
done
"$DART" --snapshot="$SNAPSHOT_PATH" --packages="$FLUTTER_TOOLS_DIR/.packages" "$SCRIPT_PATH"
echo "$revision" > "$STAMP_PATH"
fi
很明显,如果要重建 flutter_tools,可以删除 flutter_repo_dir/bin/cache/flutter_tools.stamp(以便重新生成它),或者注释掉 if/fi(每次都重新生成)。
3. 在调试模式下发布 Flutter
如果你发现 Flutter 在开发中出现延迟,并且猜测这可能是由逻辑或调试模式引起的,那么可以在发布模式下构建 APK,或者将 Flutter 强制改为为发布模式,如下所示:
自定义 iOS
与自定义 iOS 相关的内容包括 Flutter.framework、gen_snapshot、xcode_backend.sh 和 flutter_tools。在自定义 Flutter 时,需要注意以下事项:
1. 在优化期间重复替换 Flutter.framework 导致的重新编译
这部分的逻辑与构建有关,位于 xcode_backend.sh 中。为了确保每次都能获得正确的 Flutter.framework,Flutter 每次都会根据配置查找并替换 Flutter.framework(请参阅 Generated.xcconfig 配置)。不过,这会导致重新编译依赖于这个框架的项目代码。必要的修改如下:
2. 在调试模式下发布 Flutter
要进行这个自定义,请将 Generated.xcconfig 中的 FLUTTER_BUILD_MODE 更改为“Release”,将 FLUTTER_FRAMEWORK_DIR 更改为与“Release”对应的路径。
3. 设置对 armv7 的支持
有关此方案的原始文档,请参阅 https://github.com/flutter/engine/wiki/iOS-Builds-Supporting-ARMv7。
事实上,Flutter 本身在 iOS 中支持 armv7,但目前官方还没有提供支持,因此必须修改相关逻辑,如下所示:
a. 生成默认逻辑:
Flutter.framework(arm64)
b. 修改 Flutter,以便每次都可以重建 flutter_tools。修改 build_aot.Dart 和 mac.Dart,将 iOS 的相关 arm64 更改为 armv7,并将 gen_snapshot 更改为 i386。
可以通过以下命令生成 i386 的 gen_snapshot:
./flutter/tools/gn --runtime-mode=debug --ios --ios-cpu=arm
ninja -C out/ios_debug_arm
这里有一种隐含的逻辑:
构造 gen_snapshot 的预定义宏(x86_64/__ i386 等)。目标 gen_snapshot 的结构和最终的 App.framework 结构必须保持一致。也就是说,使用 x86_64->x86_64->arm64 或 i386->i386->armv7。
c. 在 iPhone4S 上,当 gen_snapshot 生成不受支持的 SDIV 命令时会发生 EXC_BAD_INSTRUCTION(EXC_ARM_UNDEFINED)错误,这可以通过向 gen_snapshot(位于 build_aot.Dart 中)添加参数“——no-use-integer-division”来解决。其背后的逻辑如下:
d.“lipo -create”在 a 步骤和 b 步骤中生成的 Flutter.framework,以便生成支持 armv7 和 arm64 的 Flutter.framework。
e. 修改 Flutter.framework 中的 Info.plist,并删除:
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>arm64</string>
</array>
同样,你必须在 App.framework 上执行相同的操作,以避免受到 AppStore 中应用程序细化的影响。
调试 Flutter 工具
在调试模式下构建 APK 时,如果你想知道 Flutter 的特定执行逻辑,可以采用以下方法:
a. 了解 flutter_tools 命令的参数。
b. 将 packages/flutter_tools 作为 Dart 项目打开,并添加新的“Dart Command Line App”配置。将 Dart 文件设置为“flutter_tools.Dart”,将工作目录设置为 Flutter 项目的路径,并将 Program 参数设置为先前获得的参数。
自定义和调试引擎
请考虑以下情形。假设我们基于 Flutter beta v0.3.1 定制和开发服务,为了确保稳定性,SDK 在某段时间内不会升级。同时,Flutter v0.3.1 的 master 分支上修改了一个 bug,标记为 fix_bug_commit。你将如何应对这种情况?
1.Flutter beta v0.3.1 将其相应的引擎代码提交指定为 09d05a389。
请参阅:flutter/bin/internal/engine.version。
2. 获取引擎代码。
3. 由于 master 代码是在第二步中获得的,我们需要的是与特定提交相对应的代码(09d05a389),所以需要从这次提交拉取一个新分支:custom_beta_v0.3.1。
4. 在 custom_beta_v0.3.1(commit:09d05a389)上运行“gclient sync”,获取与 flutter beta v0.3.1 相对应的所有引擎代码。
5. 使用“git cherry-pick fix_bug_commit”将 master 的变更同步到 custom_beta_v0.3.1。如果变更依赖了最新依赖项,则可能会发生编译错误。
6. 运行以下命令应用与 iOS 相关的变更:
./flutter/tools/gn --runtime-mode=debug --ios --ios-cpu=arm
ninja -C out/ios_debug_arm
./flutter/tools/gn --runtime-mode=release --ios --ios-cpu=arm
ninja -C out/ios_release_arm
./flutter/tools/gn --runtime-mode=profile --ios --ios-cpu=arm
ninja -C out/ios_profile_arm
./flutter/tools/gn --runtime-mode=debug --ios --ios-cpu=arm64
ninja -C out/ios_debug
./flutter/tools/gn --runtime-mode=release --ios --ios-cpu=arm64
ninja -C out/ios_release
./flutter/tools/gn --runtime-mode=profile --ios --ios-cpu=arm64
ninja -C out/ios_profile
要调试 Flutter.framework 源代码,请使用以下命令:
./flutter/tools/gn --runtime-mode=debug --unoptimized --ios --ios-cpu=arm64
ninja -C out/ios_debug_unopt
用生成的文件替换 Flutter 中的 Flutter.framework 和 gen_snapshot,这样就可以调试引擎源代码。
7. 最后,运行以下命令应用与 Android 相关的变更:
./flutter/tools/gn --runtime-mode=debug --android --android-cpu=arm
ninja -C out/android_debug
./flutter/tools/gn --runtime-mode=release --android --android-cpu=arm
ninja -C out/android_release
./flutter/tools/gn --runtime-mode=profile --android --android-cpu=arm
ninja -C out/android_profile
你可以使用生成的文件替换 flutter/bin/cache/artifacts/engine/android* 下的 gen_snapshot 和 flutter.jar,以便生成 Android 的 arm 和 debug/release/profile 文件包。
相关推荐
- go语言也可以做gui,go-fltk让你做出c++级别的桌面应用
-
大家都知道go语言生态并没有什么好的gui开发框架,“能用”的一个手就能数的清,好用的就更是少之又少。今天为大家推荐一个go的gui库go-fltk。它是通过cgo调用了c++的fltk库,性能非常高...
- 旧电脑的首选系统:TinyCore!体积小+精简+速度极快,你敢安装吗
-
这几天老毛桃整理了几个微型Linux发行版,准备分享给大家。要知道可供我们日常使用的Linux发行版有很多,但其中的一些发行版经常会被大家忽视。其实这些微型Linux发行版是一种非常强大的创新:在一台...
- codeblocks和VS2019下的fltk使用中文
-
在fltk中用中文有点问题。英文是这样。中文就成这个样子了。我查了查资料,说用UTF-8编码就行了。edit->Fileencoding->UTF-8然后保存文件。看下下边的编码指示确...
- FLTK(Fast Light Toolkit)一个轻量级的跨平台Python GUI库
-
FLTK(FastLightToolkit)是一个轻量级的跨平台GUI库,特别适用于开发需要快速、高效且简单界面的应用程序。本文将介绍Python中的FLTK库,包括其特性、应用场景以及如何通过代...
- 中科院开源 RISC-V 处理器“香山”流片,已成功运行 Linux
-
IT之家1月29日消息,去年6月份,中科院大学教授、中科院计算所研究员包云岗,发布了开源高性能RISC-V处理器核心——香山。近日,包云岗在社交平台晒出图片,香山芯片已流片,回片后...
- Linux 5.13内核有望合并对苹果M1处理器支持的初步代码
-
预计Linux5.13将初步支持苹果SiliconM1处理器,不过完整的支持工作可能还需要几年时间才能完全完成。虽然Linux已经可以在苹果SiliconM1上运行,但这需要通过一系列的补丁才能...
- Ubuntu系统下COM口测试教程(ubuntu port)
-
1、在待测试的板上下载minicom,下载minicom有两种方法:方法一:在Ubuntu软件中心里面搜索下载方法二:按“Ctrl+Alt+T”打开终端,打开终端后输入“sudosu”回车;在下...
- 湖北嵌入式软件工程师培训怎么选,让自己脱颖而出
-
很多年轻人毕业即失业、面试总是不如意、薪酬不满意、在家躺平。“就业难”该如何应对,参加培训是否能改变自己的职业走向,在湖北,有哪些嵌入式软件工程师培训怎么选值得推荐?粤嵌科技在嵌入式培训领域有十几年经...
- 新阁上位机开发---10年工程师的Modbus总结
-
前言我算了一下,今年是我跟Modbus相识的第10年,从最开始的简单应用到协议了解,从协议开发到协议讲解,这个陪伴了10年的协议,它一直没变,变的只是我对它的理解和认识。我一直认为Modbus协议的存...
- 创建你的第一个可运行的嵌入式Linux系统-5
-
@ZHangZMo在MicrochipBuildroot中配置QT5选择Graphic配置文件增加QT5的配置修改根文件系统支持QT5修改output/target/etc/profile配置文件...
- 如何在Linux下给zigbee CC2530实现上位机
-
0、前言网友提问如下:粉丝提问项目框架汇总下这个网友的问题,其实就是实现一个网关程序,内容分为几块:下位机,通过串口与上位机相连;下位机要能够接收上位机下发的命令,并解析这些命令;下位机能够根据这些命...
- Python实现串口助手 - 03串口功能实现
-
串口调试助手是最核心的当然是串口数据收发与显示的功能,pzh-py-com借助的是pySerial库实现串口收发功能,今天痞子衡为大家介绍pySerial是如何在pzh-py-com发挥功能的。一、...
- 为什么选择UART(串口)作为调试接口,而不是I2C、SPI等其他接口
-
UART(通用异步收发传输器)通常被选作调试接口有以下几个原因:简单性:协议简单:UART的协议非常简单,只需设置波特率、数据位、停止位和校验位就可以进行通信。相比之下,I2C和SPI需要处理更多的通...
- 同一个类,不同代码,Qt 串口类QSerialPort 与各种外设通讯处理
-
串口通讯在各种外设通讯中是常见接口,因为各种嵌入式CPU中串口标配,工业控制中如果不够还通过各种串口芯片进行扩展。比如spi接口的W25Q128FV.对于软件而言,因为驱动接口固定,软件也相对好写,因...
- 嵌入式linux为什么可以通过PC上的串口去执行命令?
-
1、uboot(负责初始化基本硬bai件,如串口,网卡,usb口等,然du后引导系统zhi运行)2、linux系统(真正的操作系统)3、你的应用程序(基于操作系统的软件应用)当你开发板上电时,u...
你 发表评论:
欢迎- 一周热门
- 最近发表
-
- go语言也可以做gui,go-fltk让你做出c++级别的桌面应用
- 旧电脑的首选系统:TinyCore!体积小+精简+速度极快,你敢安装吗
- codeblocks和VS2019下的fltk使用中文
- FLTK(Fast Light Toolkit)一个轻量级的跨平台Python GUI库
- 中科院开源 RISC-V 处理器“香山”流片,已成功运行 Linux
- Linux 5.13内核有望合并对苹果M1处理器支持的初步代码
- Ubuntu系统下COM口测试教程(ubuntu port)
- 湖北嵌入式软件工程师培训怎么选,让自己脱颖而出
- 新阁上位机开发---10年工程师的Modbus总结
- 创建你的第一个可运行的嵌入式Linux系统-5
- 标签列表
-
- wireshark怎么抓包 (75)
- qt sleep (64)
- cs1.6指令代码大全 (55)
- factory-method (60)
- sqlite3_bind_blob (52)
- hibernate update (63)
- c++ base64 (70)
- nc 命令 (52)
- wm_close (51)
- epollin (51)
- sqlca.sqlcode (57)
- lua ipairs (60)
- tv_usec (64)
- 命令行进入文件夹 (53)
- postgresql array (57)
- statfs函数 (57)
- .project文件 (54)
- lua require (56)
- for_each (67)
- c#工厂模式 (57)
- wxsqlite3 (66)
- dmesg -c (58)
- fopen参数 (53)
- tar -zxvf -c (55)
- 速递查询 (52)