Flutter 插件开发入门
1、初识 Flutter Plugin
Flutter 的插件类似于我们在 Android 中说的第三方库,通过使用插件,可以借助插件中的代码实现一些额外功能。
Flutter 的插件以 package 的形式存在,使用 package 的目的是为了达到模块化,可以让代码被共享和复用。这些 package 可以直接在 pubspec.yaml 中被依赖。
一个 package 至少包含两个部分:
- pubspec.yaml 文件:元数据文件,声明了 package 的名称、版本、作者等信息
- lib 文件夹:包含 package 的公开代码,至少会存在
<pakcage-name>.dart
这个文件,该文件用于使用者快速 import 这个 package 以便使用代码内容,因此必须存在
package 的种类分为两种:
- Dart packages:纯 Dart 代码的 package。它包含 Flutter 的特定功能,因此它依赖于 Flutter Framework,只能用在 Flutter 上
- Plugin packages:包含 Dart 代码编写的 API,也包含平台(Android/iOS)特定实现的 package,可以被 Android 和 iOS 调用
2、认识插件结构
2.1 通过源码了解插件工作原理
我们创建一个 Flutter 插件项目,它的目录结构如下:
关注其中的三个目录:
- android:Android 原生代码,相对于 Flutter 而言,就是 Native 代码
- example:AS 生成的使用 Flutter 插件的示例项目
- lib:Flutter 插件源码
在 /example/lib/ 目录下有一个 main.dart 文件是示例程序的入口,在页面中央会展示平台版本 _platformVersion
,该变量通过插件内定义的 getPlatformVersion() 获取:
class _MyAppState extends State<MyApp> {String _platformVersion = 'Unknown';final _pluginDemoPlugin = PluginDemo();void initState() {super.initState();initPlatformState();}Future<void> initPlatformState() async {String platformVersion;try {platformVersion =await _pluginDemoPlugin.getPlatformVersion() ?? 'Unknown platform version';} on PlatformException {platformVersion = 'Failed to get platform version.';}if (!mounted) return;setState(() {_platformVersion = platformVersion;});}Widget build(BuildContext context) {return MaterialApp(home: Scaffold(appBar: AppBar(title: const Text('Plugin example app'),),body: Center(child: Text('Running on: $_platformVersion\n'),),),);}
}
插件 PluginDemo 通过 AS 创建的模板代码生成:
import 'plugin_demo_platform_interface.dart';class PluginDemo {Future<String?> getPlatformVersion() {return PluginDemoPlatform.instance.getPlatformVersion();}
}
插件在这里进行了一层封装,需要再进一步看 PluginDemoPlatform:
import 'package:plugin_platform_interface/plugin_platform_interface.dart';import 'plugin_demo_method_channel.dart';abstract class PluginDemoPlatform extends PlatformInterface {PluginDemoPlatform() : super(token: _token);static final Object _token = Object();// _instance 是一个 MethodChannelPluginDemostatic PluginDemoPlatform _instance = MethodChannelPluginDemo();// instance 的 getter 和 setterstatic PluginDemoPlatform get instance => _instance;static set instance(PluginDemoPlatform instance) {PlatformInterface.verifyToken(instance, _token);_instance = instance;}// 默认的方法没有提供实现Future<String?> getPlatformVersion() {throw UnimplementedError('platformVersion() has not been implemented.');}
}
PluginDemoPlatform.instance
实际上是子类对象 MethodChannelPluginDemo,创建一个 MethodChannel 来执行 getPlatformVersion():
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';import 'plugin_demo_platform_interface.dart';/// An implementation of [PluginDemoPlatform] that uses method channels.
class MethodChannelPluginDemo extends PluginDemoPlatform {/// The method channel used to interact with the native platform.final methodChannel = const MethodChannel('plugin_demo');Future<String?> getPlatformVersion() async {final version = await methodChannel.invokeMethod<String>('getPlatformVersion');return version;}
}
MethodChannel 需要输入一个名字作为参数:
class MethodChannel {// name 是 Channel 的名字// codec 是消息编解码器const MethodChannel(this.name, [this.codec = const StandardMethodCodec(), BinaryMessenger? binaryMessenger ]): _binaryMessenger = binaryMessenger;
}
MethodChannel 这里我们要注意两个参数:
- name:String 类型,Channel 的名字,用于区分 Channel,因此必须独一无二
- codec:消息编解码器,Flutter 插件的作用包含在 Flutter 应用与各个 Platform(Android/iOS)之间进行通信,比如方法调用与数据传递都是通信的一部分,那么这个通信过程可以看作协议,发送方需要通过编码器对协议内容进行编码,接收方需要通过解码器对协议内容进行解码
这里我们是从源码角度来看 Channel,后续我们也会再从架构角度对 Platform Channel 进行解析,还会再看到相关内容。
然后在执行 invokeMethod() 时会调用 Native(Flutter 的 Native 就是 Android 或 iOS)的插件源码,比如 Android 端的 Flutter/plugin_demo/android/src/main/kotlin/<PackageName>/PluginDemoPlugin.kt
:
class PluginDemoPlugin: FlutterPlugin, MethodCallHandler {private lateinit var channel : MethodChanneloverride fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {channel = MethodChannel(flutterPluginBinding.binaryMessenger, "plugin_demo")channel.setMethodCallHandler(this)}override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {if (call.method == "getPlatformVersion") {result.success("Android ${android.os.Build.VERSION.RELEASE}")} else {result.notImplemented()}}override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {channel.setMethodCallHandler(null)}
}
onMethodCall() 内在被调用的方法名是 getPlatformVersion 时会返回一个字符串,标记出 Android 的系统版本。
需要注意的是,在 Flutter 插件的 MethodChannelPluginDemo 内创建 MethodChannel 时传入的名字(plugin_demo),需要与 Native 这边创建的 MethodChannel 传入的名字一致,才可成功调用方法。
2.2 实现一个简单的插件
熟悉了插件结构,就可以开发一个简单的插件了。比如 Flutter 应用中打印 log 只能通过 print() 或 debugPrint() 打印,默认都是 Info 等级,使用起来并不友好。因此我们可以通过 Flutter 插件提供打印各种日志等级的方法,具体操作过程如下:
-
Native 端:修改 Android 端的 PluginDemoPlugin,在 onMethodCall() 中添加输出 Error 级别 Log 的方法:
override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {if (call.method == "getPlatformVersion") {result.success("Android ${android.os.Build.VERSION.RELEASE}")} else if (call.method == "logE") {val tag = call.argument("tag");val msg = call.argument("message");android.util.Log.e(tag, msg);} else {result.notImplemented()}}
-
Flutter 插件:
-
先在插件提供的平台基类 PluginDemoPlatform 中提供 logE 的默认实现:
Future<void> logE(String tag, String message) {throw UnimplementedError('logE() has not been implemented.');}
-
然后在 PluginDemoPlatform 的子类 MethodChannelPluginDemo 中提供 logE 的实现,通过 MethodChannel 调用 Native 的方法:
Future<void> logE(String tag, String message) async {// arguments 是 dynamic 的,因为我们要传两个参数,因此做成一个 Mapawait methodChannel.invokeMethod<String>('logE', {'tag': tag, 'message': message});}
-
最后在 PluginDemo 中提供一个封装方法:
void logE(String tag, String message) {PluginDemoPlatform.instance.logE(tag, message);}
-
-
example 示例:在示例的 main 文件中通过插件调用 logE():
class _MyAppState extends State<MyApp> {final _pluginDemoPlugin = PluginDemo();void initState() {super.initState();initPlatformState();// 通过插件调用输入 Error 等级 log 的方法_pluginDemoPlugin.logE('FlutterPlugin', 'init work');} }
3、Plugin 通信原理
介绍 Plugin 之前需要先了解一下 Flutter 框架。
3.1 Flutter 框架
Flutter 框架包括 Framework 和 Engine,他们运行在各自的 Platform 上:
Framework 由 Dart 语言开发,包含 Material Design 风格(Android)和 Cupertino 风格(iOS)的 Widget,还有文本、图片、按钮等基础 Widget;此外,还包括渲染、动画、绘制、手势等基础能力。
Engine 是 C++ 实现的,包括 Skia(二维图形库)、Dart VM(Dart Runtime)、Text(文本渲染)等。Flutter 的上层能力实际上是 Engine 提供的。通过 Engine 将各个 Platform 的差异抹平,Flutter Plugin 就是通过 Engine 提供的 Platform Channel 实现的通信。
Flutter 插件调用 Android Native 代码的过程,实际上就是从 Framework 到 Engine,Engine 到 Android 底层再向上到 Java/Kotlin 的 U 型结构。
3.2 Platform Channel
Flutter Plugin 本质上是一个特殊的 package,它提供了 Android 或 iOS 的底层封装,在 Flutter 层提供组件功能,使得 Flutter 可以方便的调取 Native 代码。
很多平台相关性或者对于 Flutter 实现起来比较复杂的部分,都可以封装成 Plugin,其原理图如下:
图中可见,Flutter app 通过 Plugin 创建的 Platform Channel 调用 Native API,下面来详细介绍 Platform Channel 的各个部分。
还是先看上面的图,Flutter app 作为 client,通过 MethodChannel 向其他 Platform(Android/iOS)发送调用消息,而 Android Platform 作为 Host 通过 MethodChannel 接收调用消息,而另一个 Host iOS Platform 则通过 FlutterMethodChannel 接收调用消息。
Android Platform 中有一个 FlutterActivity 作为 Android Plugin 的管理器,它记录了所有 Plugin 并将它们绑定到 FlutterView 中。
Flutter 提供了三种不同类型的 Channel:
- BasicMessageChannel:用于传递字符串和半结构化的信息
- MethodChannel:用于传递方法调用(method invocation),方法调用也可以反向发送调用消息
- EventChannel: 用于数据流(event streams)的通信
三种 Channel 相互独立,各有用途,但是在设计上非常相近,均有三个重要成员变量:
- name: String 类型,代表 Channel 的名字,也是其唯一标识符。Flutter 应用可能会有多个 Channel,它们通过 name 区分,因此每个 Channel 必须指定一个独一无二的名字。并且,消息从 Flutter 发送到 Platform 时,在 Platform 这边也要通过传递过来的 channel name 找到该 Channel 对应的 Handler(消息处理器)
- messager:BinaryMessenger 类型,代表消息信使,是消息的发送与接收的工具
- codec: MessageCodec 类型或 MethodCodec 类型,代表消息的编解码器。消息编解码器,是 JSON 格式的二进制序列化,所以调用方法的参数类型必须是可 JSON 序列化的。
平台之间传递的数据必须是支持二进制序列化的,否则无法通过消息编解码器传递消息。标准平台通道使用标准消息编解码器,以支持简单的类似 JSON 值的高效二进制序列化,例如 booleans、numbers,、Strings,、byte buffers,、List,、Maps(详细信息参考 StandardMessageCodec)。 当发送和接收值时,这些值在消息中的序列化和反序列化会自动进行。
下表显示了如何在宿主上接收 Dart 值,反之亦然:
Dart | Android | iOS |
---|---|---|
null | null | nil (NSNull when nested) |
bool | java.lang.Boolean | NSNumber numberWithBool: |
int | java.lang.Integer | NSNumber numberWithInt: |
int, if 32 bits not enough | java.lang.Long | NSNumber numberWithLong: |
int, if 64 bits not enough | java.math.BigInteger | FlutterStandardBigInteger |
double | java.lang.Double | NSNumber numberWithDouble: |
String | java.lang.String | NSString |
Uint8List | byte[] | FlutterStandardTypedData typedDataWithBytes: |
Int32List | int[] | FlutterStandardTypedData typedDataWithInt32: |
Int64List | long[] | FlutterStandardTypedData typedDataWithInt64: |
Float64List | double[] | FlutterStandardTypedData typedDataWithFloat64: |
List | java.util.ArrayList | NSArray |
Map | java.util.HashMap | NSDictionary |
4、开发步骤总结
4.1 开发 Dart package
AS 创建新的 Flutter 项目,Project type 选择 Package,或者通过 flutter create
命令,将 template 参数指定为 package 创建:
flutter create --template=package hello
生成的 Package 模板结构如下:
lib/hello.dart
:Package 的 Dart 代码test/hello_test.dart
:Package 的单元测试代码- 实现 Package:
- 对于纯 Dart 包,需要在
lib/<package name>.dart
文件内或在 lib 下添加新文件实现 - 对于测试包,在 test 目录中添加单元测试代码
- 对于纯 Dart 包,需要在
4.2 开发 Plugin package
创建 Plugin
AS 创建新的 Flutter 项目,Project type 选择 Plugin,或者通过 flutter create
命令,将 template 参数指定为 plugin,同时将 org 参数使用反向域名表示法指定组织名并创建:
flutter create --org com.example --template=plugin hello
生成的 Plugin 模板结构如下:
-
lib/hello.dart
:插件包的 Dart API -
android/src/main/java/com/<yourcompany>/hello/HelloPlugin.java
:插件包 API 的 Android 实现代码 -
ios/Classes/HelloPlugin.m
:插件包 API 的 iOS 实现代码 -
example 目录:依赖于 Plugin 的示例程序。默认情况下,示例代码在 Android 和 iOS 上分别使用 Java 和 Objective-C,如果想使用 Kotlin 或 Swift,可以用过 -i 或 -a 参数指定语言:
flutter create --template=plugin -i swift -a kotlin hello
实现 Plugin
因为 Plugin 是包含多个平台的代码的,因此实现 Plugin 需要在多个平台上联动:
- 用 Dart 语言实现 Flutter Plugin(代码位置在根目录的 lib 文件夹下)
- 用 Kotlin/Java 实现 Android 原生代码(代码位置在根目录下的 /android/src/main 文件夹下)
- 用 Objective-C/Swift 实现 iOS 原生代码(代码位置在根目录下的 ios 文件夹下)
编辑 Android 代码之前需要确保代码至少构建过一次,可以在 AS 的终端执行 cd 命令进入到示例代码的目录,然后用 flutter 命令进行 apk 构建:
cd hello/example
flutter build apk
最后需要通过 Platform Channel 将 Dart 代码与平台相关的特定实现连接起来,这一部分在认识插件结构一节中已经讲过。
添加文档
建议将以下文档添加到所有软件包:
- README.md:介绍包的文件
- CHANGELOG.md:记录每个版本中的更改
- LICENSE:包含软件包许可条款的文件
4.3 发布 package
实现完毕的 package 可以在 Pub 上发布供他人使用。发布的步骤如下:
-
发布前,需要先检查
pubspec.yaml
、README.md
以及CHANGELOG.md
文件,以确保其内容的完整性和正确性 -
运行 dry-run 命令查看是否都准备 OK 了(如果有问题需要根据错误信息修改):
flutter packages pub publish --dry-run
-
最后,运行发布命令:
flutter packages pub publish
发布 package 需要使用 Google 账号,在运行命令时可能会因为网络环境延迟或失败。
相关文章:
Flutter 插件开发入门
1、初识 Flutter Plugin Flutter 的插件类似于我们在 Android 中说的第三方库,通过使用插件,可以借助插件中的代码实现一些额外功能。 Flutter 的插件以 package 的形式存在,使用 package 的目的是为了达到模块化,可以让代码被共…...
2024.12.25在腾讯云服务器上使用docker部署flask
2024.12.25在腾讯云服务器上使用docker部署flask 操作系统:Ubuntu 根据腾讯云的说明文档安装 Docker 并配置镜像加速源,注意需要安装腾讯云的加速源,使用官网的加速源连接极其不稳定,容易导致运行失败。使用哪个公司的云服务器就…...
2024 年12月英语六级CET6听力原文(Lecture部分)
2024 年12月英语六级CET6听力原文(Long Conersation和Passage) 1 牛津大学关于普遍道德准则的研究及相关观点与建议 译文 2 食物颜色对味觉体验及大脑预期的影响 译文 3 财务资源对意义与幸福之间关系的影响研究 译文...
centos 释放系统预留内存并关闭Kdump服务
背景:Kdump是Linux系统的一种内核崩溃转储机制,它允许在系统发生内核崩溃(例如内核panic)时,捕获内存的转储信息,从而帮助事后分析故障原因。该过程需要一块预留内存(称为crashkernel内存&#…...
基于WEB的房屋出租管理系统设计
摘 要 随着城市化程度的推进,越来越多的人涌入城市,同时也带来的旺盛的租房需求,传统的房屋出租管理依赖人 工记录的方式难以满足人们对房屋出租管理的需求。因此,本文根据房屋出租信息化的需求设计一款基于房屋出租 的管理系统。…...
云边端架构的优势是什么?面临哪些挑战?
一、云边端架构的优势 降低网络延迟:在传统集中式架构中,数据需传输到云计算中心处理,导致网络延迟较高。而云边端架构将计算和存储推向边缘设备,可在离用户更近的地方处理数据,大大降低了网络延迟,提升了用…...
clickhouse解决suspiciously many的异常
1. 问题背景 clickhouse安装在虚拟机上,持续写入日志时,突然关机,然后重启,会出现clickhouse可以正常启动,但是查询sql语句,提示suspiciously many异常,如图所示 2. 问题修复 touch /data/cl…...
爬虫 APP 逆向 ---> shopee(虾皮) 电商
shopee 泰国站点:https://shopee.co.th/ shopee 网页访问时,直接弹出使用 app 登录查看,那就登录 shopee 泰国站点 app。 手机抓包:分类接口 接口:https://mall.shopee.co.th/api/v4/pages/get_category_tree 请求参…...
用例图和活动图的区别与联系
在软件开发过程中,需求分析是至关重要的一步。为了更好地理解和描述系统的功能需求,开发人员通常会使用各种图形化工具。其中,用例图和活动图是两种非常常用的工具。虽然它们都用于描述系统的行为,但各自具有不同的特点和适用场景…...
接口请求中调试可以看到Origin,其具体的作用
接口请求中带 Origin 是浏览器在跨域请求时自动添加的一个 HTTP 请求头,用于标识请求的来源(源)。Origin 头的主要作用是支持跨域资源共享(CORS)和增强安全性。 Origin 的含义 Origin 包含发起请求的源信息࿰…...
知识图谱+大模型:打造全新智慧城市底层架构
在数字化时代,智慧城市的建设正迎来新一轮的变革。本文将探讨如何结合知识图谱和大模型技术,构建智慧城市的全新底层架构,以应对日益增长的数据量和复杂性,提升城市管理的智能化水平。 知识图谱:智慧城市的知识库 知识…...
Android基于Path的addRoundRect,Canvas剪切clipPath简洁的圆形图实现,Kotlin(2)
Android基于Path的addRoundRect,Canvas剪切clipPath简洁的圆形图实现,Kotlin(2) import android.content.Context import android.graphics.BitmapFactory import android.graphics.Canvas import android.graphics.Path import a…...
计算机图形学知识点汇总
一、计算机图形学定义与内容 1.图形 图形分为“图”和“形”两部分。 其中,“形”指形体或形状,存在于客观世界和虚拟世界,它的本质是“表示”;而图则是包含几何信息与属性信息的点、线等基本图元构成的画面,用于表达…...
【EtherCATBasics】- KRTS C++示例精讲(2)
EtherCATBasics示例讲解 目录 EtherCATBasics示例讲解结构说明代码讲解 项目打开请查看【BaseFunction精讲】。 结构说明 EtherCATBasics:应用层程序,主要用于人机交互、数据显示、内核层数据交互等; EtherCATBasics.h : 数据定义…...
AI 简史:从神经元到现代大模型
AI 简史:从神经元到现代大模型 人工智能 (AI) 和深度学习 (DL) 在过去的几十年中飞速发展,推动了计算机视觉、自然语言处理和机器人等领域的进步。今年的诺贝尔物理学奖更是颁给了美国科学家约翰霍普菲尔德 (John Hopfield)和英国科学家杰弗…...
Kotlin入门到深入加强(1):基本介绍和第一个程序
吧, 一.关于本章和Kotlin 本章内容如要简单介绍一下Kotlin和创建并执行第一个Kotlin程序 什么是Kotlin,优势是什么,它和JAVA的关系又是什么 Kotlin是一种静态类型的编程语言,它运行在Java虚拟机上,并且可以与现有的Java代码无缝集成。Kotl…...
走进 Web3:探索分布式网络的未来
随着互联网的不断演变,我们正站在一个全新阶段的门槛上——Web3。它是对传统互联网(Web2)的一次深刻反思与升级,标志着一个去中心化、更加自主的数字世界的到来。Web3不仅仅是技术的革新,更代表着对互联网价值体系的根…...
信管通低代码信息管理系统应用平台
目前,国家统一要求事业单位的电脑都要进行国产化替代,替代后使用的操作系统都是基于linux的,所有以前在WINDOWS下运行的系统都不能使用了,再者,各单位的软件都很零散,没有统一起来。需要把日常办公相关的软…...
C语言从入门到放弃教程
C语言从入门到放弃 1. 介绍1.1 特点1.2 历史与发展1.3 应用领域 2. 安装2.1 编译器安装2.2 编辑器安装 3. 第一个程序1. 包含头文件2. 主函数定义3. 打印语句4. 返回值 4. 基础语法4.1 注释4.1.1 单行注释4.1.2 多行注释 4.2 关键字4.2.1 C语言标准4.2.2 C89/C90关键字…...
硬件设计:RS232电平标准
RS232是一种常用的串行通信接口标准,主要用于计算机和外部设备之间的数据传输。以下是RS232电平标准的详细介绍: 1. 电气特性: 信号电平: 逻辑1 (MARK):-3V到-15V之间。通常,-5V到-15V之间为有效的逻辑1。…...
前端初学基础
一.Web开发 前端三件 HTML ,页面展现 CSS,样式 JS(JavaScript),动起来 二,HTML 1.HTML概念 网页,网站中的一个页面,网页是构成网站的基本元素,是承载各种网站应用的平台。通俗的说,网站就…...
在 CentOS 7 上安装 Node.js 20 并升级 GCC、make 和 glibc
在 CentOS 7 上安装 Node.js 20 并升级 GCC、make 和 glibc 📖 前言 在 CentOS 7 上使用 NVM 安装 Node.js 后,可能会遇到如下问题: node: /lib64/libm.so.6: version GLIBC_2.27’ not found (required by node) node: /lib64/libc.so.6:…...
mac中idea菜单工具栏没有git图标了
1.右击菜单工具栏 2.选中VCS,点击添加 3.搜索你要的工具,选中点击确定就添加了 4.回到上面一个界面,选中你要放到工具栏的工具,点击应用就好了 5.修改图标,快捷键或者右击选中编辑图标 6.选择你要的图标就好了...
C++简明教程(10)(初识类)
类的教程 C 类的完整教程 C 中,类(class)是面向对象编程的核心概念,用于定义对象的属性(数据成员)和行为(成员函数)。本教程将带你从零开始,循序渐进地学习如何定义和使…...
微机接口课设——基于Proteus和8086的打地鼠设计(8255、8253、8259)
原理图设计 汇编代码 ; I/O 端口地址定义 IOY0 EQU 0600H IOY1 EQU 0640H IOY2 EQU 0680HMY8255_A EQU IOY000H*2 ; 8255 A 口端口地址 MY8255_B EQU IOY001H*2 ; 8255 B 口端口地址 MY8255_C EQU IOY002H*2 ; 8255 C 口端口地址 MY8255_MODE EQU IOY003H*2 ; …...
ctfshow-jwt
将信息进行安全的封装,以json的方式传递 三部分分别是: Header { typ: JWT token类型 alg: HS256 算法名称 } 将这个信息进行base64加密,构成了第一部分 payload载荷,存放主要信息的地方 { "sub": 1234567890, 标准中注册的…...
植物大战僵尸融合版,真无双版,喵版,抽卡版,杂交版v3
我用夸克网盘分享了「植物大战僵尸」链接: https://pan.quark.cn/s/11a45054a4da 融合版的作者为B站up主蓝飘飘fly。该版本在原版植物基础上创新地将两种不同的植物种植在一起进行融合,创造出独特的游戏体验。抽卡版、喵版、无双版是由B站UP主【时…...
将Minio设置为Django的默认Storage(django-storages)
这里写自定义目录标题 前置说明静态文件收集静态文件 使用django-storages来使Django集成Minio安装依赖settings.py测试收集静态文件测试媒体文件 前置说明 静态文件 Django默认的Storage是本地,项目中的CSS、图片、JS都是静态文件。一般会将静态文件放到一个单独…...
MaaS(Model as a Service)
1.MasS是什么? Model as a Service(MaaS)是一种云计算服务模式,它允许用户通过互联网访问和使用机器学习模型,而不需要自行构建和维护这些模型。MaaS提供了模型的托管、管理和监控,使用户能够专注于应用程…...
Docker怎么关闭容器开机自启,批量好几个容器一起操作?
环境: WSL2 docker v25 问题描述: Docker怎么关闭容器开机自启,批量好几个容器一起操作? 解决方案: 在 Docker 中,您可以使用多种方法来关闭容器并配置它们是否在系统启动时自动启动。以下是具体步骤和…...
训练时开Dropout,推理时不开Dropout的原因以及推理过程是怎样的?(中英双语)
Dropout的概念与应用:基于Transformer模型的深入解析 在深度学习中,Dropout 是一种常用的正则化技术,主要用于防止神经网络的过拟合。在训练过程中,Dropout通过随机丢弃神经网络中的一部分神经元,降低了网络的复杂度&…...
STL heap原理和用法
在C STL(标准模板库)中,heap(堆)并不是一个独立的容器,而是一组基于容器(通常是 vector)实现的算法函数,用于将一段数据组织成堆的数据结构形式,并提供了一些…...
【DRAM存储器四十三】LPDDR5介绍--寻址
👉个人主页:highman110 👉作者简介:一名硬件工程师,持续学习,不断记录,保持思考,输出干货内容 参考资料:《某LPDDR5数据手册》 、《JESD209-5C》 前面我们介绍的LPDDR5一个die的数据位宽...
mac启ssh服务用于快速文件传输
x.1 在mac上启SSH服务 方法一:图形交互界面启ssh(推荐) 通过sharing - advanced - remote login来启动ssh;(中文版mac应该是 “系统设置 → 通用 → 共享”里打开“远程登录”来启动) 查看自己的用户名和…...
Java设计模式 —— 【结构型模式】享元模式(Flyweight Pattern) 详解
文章目录 概述结构案例实现优缺点及使用场景 概述 享元模式也叫蝇量模式:运用共享技术有效地支持大量细粒度的对象; 常用于系统底层开发,解决系统的性能问题。像数据库连接池,里面都是创建好的连接对象,在这些连接对象…...
前端最新Vue2+Vue3基础入门到实战项目全套教程,自学前端vue就选黑马程序员,一套全通关!
Vue 快速上手 Vue概念 Vue 是一个用于构建用户界面的渐进式框架 构建用户界面:基于数据渲染出用户看到的页面 渐进式:循序渐进 框架:一套完整的项目解决方案 Vue 的两种使用方式: ① Vue 核心包开发 场景:局部 模块改造 ② Vue 核心包 &am…...
肿瘤电场治疗费用
肿瘤电场治疗作为一种前沿的肿瘤治疗方法,近年来备受关注。该方法通过利用特定频率的交流电场,作用于恶性肿瘤细胞,以达到抑制肿瘤生长的目的。然而,随着这种治疗方法的普及,其费用问题也逐渐成为患者和家属关注的焦点…...
datatables快速入门
官网 进入官网https://datatables.net/ 点击下载 支持多种方式下载 快速入门 这里以cdn的方式演示 https://cdn.datatables.net/ <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport&…...
ReactPress 1.6.0:重塑博客体验,引领内容创新
ReactPress 是一个基于Next.js的博客&CMS系统, Github项目地址:https://github.com/fecommunity/reactpress 欢迎Star。 体验地址:http://blog.gaoredu.com/ 今天,我们自豪地宣布ReactPress 1.6.0版本的正式发布,…...
我的创作纪念日(五年)
慕然回首 平平无奇的周一早晨,收到来自csdn的提醒,创作纪念日五周年了,这也意味着我从事开发行业差不多有整整五年了,五年啊!你知道这五年我是怎么过的吗?一句Just do IT,我做it整整做了五年&am…...
企业AI助理背后的技术架构:从数据到智能决策
在当今数字化时代,企业AI助理已经成为推动企业数字化转型和智能化升级的重要工具。它们通过整合企业内外部数据资源,运用先进的算法和模型,为企业提供高效、精准的智能决策支持。本文将深入探讨企业AI助理背后的技术架构,从数据收…...
主流AI视频生成工具|Sora零基础入门指南
Sora是什么? Sora 是 OpenAI 推出的新一代 AI 视频生成工具。它能让用户通过简单的文本描述或图片提示,快速生成高质量的视频内容。无论是广告短片、创意视频,还是实验性艺术作品,Sora 都能帮助创作者以极低的门槛实现自己的想法。…...
单元测试/系统测试/集成测试知识总结
🍅 点击文末小卡片,免费获取软件测试全套资料,资料在手,涨薪更快 一、单元测试的概念 单元测试是对软件基本组成单元进行的测试,如函数或一个类的方法。当然这里的基本单元不仅仅指的是一个函数或者方法࿰…...
前端文件下载多方式集合
基于vueelement UI框架 // ts-ignore import axios from "axios"; import { ElMessage } from "element-plus"; import webConfig from "/config";class FileDownload {/*** 文件流下载* param url string 下载地址* param params object 请求…...
PG备份恢复--pg_dump
pg_dump pg_dump 是一个逻辑备份工具。使用 pg_dump 可以在数据库处于使用状态下进行一致 性的备份,它不会阻塞其他用户对数据库的访问 。 一致性备份是 pg_dump 开始运行时,给数据库打了一个快照,且在 pg_dump 运行过程 中发生的更新将不会被备份。 …...
Java进阶学习笔记|面向对象
第一章.类和对象 1.面向对象的介绍 1.面向过程:自己的事情自己干,代表语言C语言洗衣服:每一步自己要亲力亲为 -> 找个盆,放点水,找个搓衣板,搓搓搓 2.面向对象:自己的事情别人帮忙去干,代表语言Java语言 洗衣服:自己的事情别人干 -> 全自动洗衣机3.为啥要使用面向对…...
ESP32_H2(IDF)学习系列-ADC模数转换(连续转换)
一、简介(节选手册) 资料参考https://docs.espressif.com/projects/esp-idf/zh_CN/latest/esp32h2/api-reference/peripherals/adc_calibration.html 1 概述 ESP32-H2 搭载了以下模拟外设: • 一个 12 位逐次逼近型模拟数字转换器 (SAR ADC)&…...
二叉树的右视图
199. 二叉树的右视图 已解答 中等 相关标签 相关企业 给定一个二叉树的 根节点 root,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。 # Definition for a binary tree node. # class TreeNode(object): #…...
Pinpoint 是一个开源的分布式追踪系统
pinpointagent2.2.2.tar 是 Pinpoint 的一个版本,Pinpoint 是一个开源的分布式追踪系统,专门用于对 Java 应用程序进行性能监控、日志记录和故障诊断。它可以帮助开发人员和运维人员追踪和分析微服务架构中服务之间的调用链,并进行性能分析。…...
React 脚手架使用指南
React 脚手架使用指南 目录 概述创建项目项目结构常用命令配置说明最佳实践 概述 什么是 React 脚手架? React 脚手架(Create React App)是 Facebook 官方提供的创建 React 单页应用的工具。它提供了一个零配置的现代构建设置。 为什么使用脚手架?…...