当前位置: 首页 > news >正文

android,flutter 混合开发,pigeon通信,传参

文章目录

        • app效果
        • native和flutter通信的基础知识
          • 1. 编解码器 一致性和完整性,安全性,性能优化
          • 2. android代码
          • 3. dart代码
        • 1. 创建flutter_module
        • 2.修改 Android 项目的 settings.gradle,添加 Flutter module
        • 3. 在 Android app 的 build.gradle 中添加依赖
        • 4. 原生和flutter_module通信BasePlugin
        • 5. io.flutter.embedding.engine.plugins.util.GeneratedPluginRegister 自动注册插件
        • 6 手动注册
        • 7. 通信关闭flutter页面
        • 8. dartEntrypointArgs传入参数
        • 9. 通信传入参数
          • 9.1 发送参数
          • 9.1 接收参数
        • 10. android代码
        • 11 源码

app效果
在这里插入图片描述
native和flutter通信的基础知识

低级的消息通道,一般不直接使用
ServicesBinding.instance.defaultBinaryMessenger
高级别的消息通道,对defaultBinaryMessenger进行了封装,提供的更简单的api发送和接收消息
BasicMessageChannel MethodChannel EventChannel
MethodChannel EventChannel底层都是基于BasicMessageChannel

1. 编解码器 一致性和完整性,安全性,性能优化

MethodChannel,EventChannel->StandardMethodCodec
BasicMessageChannel->StandardMessageCodec
StandardMessageCodec:支持基本的数据类型(字符串、数字、布尔值、字节数组、列表、映射等)。
StandardMethodCodec:除了支持基本的数据类型外,还支持方法调用的参数和返回结果的编码和解码。
StringCodec:由于只处理字符串,StringCodec 的实现更为简单,性能更高

2. android代码
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {basicMessageChannel = BasicMessageChannel(flutterEngine.dartExecutor.binaryMessenger, BASIC_CHANNEL, StringCodec.INSTANCE)//创建MethodChannelmethodChannel =MethodChannel(flutterEngine.dartExecutor, BASIC_CHANNEL, StandardMethodCodec.INSTANCE)//创建EventChanneleventChannel = EventChannel(flutterEngine.dartExecutor, BASIC_CHANNEL)basicMessageChannel.send("dddd")basicMessageChannel.setMessageHandler { message, reply ->reply.reply("dddd")Log.e("dddd", message.toString())}methodChannel.invokeMethod("getPlatformVersion", null)methodChannel.setMethodCallHandler { call, result ->when (call.method) {"getPlatformVersion" -> {result.success("Android ${android.os.Build.VERSION.RELEASE}")}else -> {result.notImplemented()}}}eventChannel.setStreamHandler(object : EventChannel.StreamHandler {override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {eventSink = eventseventSink?.success("Event")}override fun onCancel(arguments: Any?) {eventSink = null}})}
3. dart代码
class _StudyChannelState extends State<StudyChannel> {MethodChannel methodChannel = const MethodChannel('base_plugin');EventChannel eventChannel = const EventChannel('base_plugin');static const basicMessageChannel = BasicMessageChannel<String>('base_plugin', StringCodec());  void initState() {super.initState();//只接收eventChannel.receiveBroadcastStream().listen((value){});//监听methodChannel.setMethodCallHandler((call){call.method == "getData" ? print("getData") : print("method=$call");return Future.value("methodChannel=$call");});//执行,相当于发送methodChannel.invokeListMethod("getData");//发送basicMessageChannel.send("data");//监听basicMessageChannel.setMessageHandler((message){return Future.value("basicMessageChannel=$message");});}Widget build(BuildContext context) {return const Placeholder();}
}
1. 创建flutter_module

File->New->New Flutter Project
name为flutter_module
选择project type为module

2.修改 Android 项目的 settings.gradle,添加 Flutter module
pluginManagement {repositories {google {content {includeGroupByRegex("com\\.android.*")includeGroupByRegex("com\\.google.*")includeGroupByRegex("androidx.*")}}mavenCentral()gradlePluginPortal()}
}
dependencyResolutionManagement {repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS)repositories {google()mavenCentral()maven(url = "https://storage.googleapis.com/download.flutter.io")}
}rootProject.name = "hunheandroid"
include(":app")
//主要是这一句
apply(from = "../flutter_module/.android/include_flutter.groovy")
3. 在 Android app 的 build.gradle 中添加依赖
dependencies {implementation(project(":flutter"))
}
4. 原生和flutter_module通信BasePlugin

参考文章生成对应的io/flutter/plugins/Messages.g.kt
用pigeon kotlin swift写一个自己的插件
拷贝BasePlugin

package io.flutter.pluginsimport android.app.Activity
import android.util.Log
import androidx.annotation.NonNull
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.embedding.engine.plugins.activity.ActivityAware
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding/** BasePlugin */
/** BasePlugin */
class BasePlugin : FlutterPlugin, HostMessageApi, ActivityAware {companion object {private var flutterMessageApi: FlutterMessageApi? = nullfun getFlutterMessageApi(): FlutterMessageApi? {return flutterMessageApi}}private var activity: Activity? = nullprivate var tag = "BasePlugin"override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {Log.e(tag, "onAttachedToEngine-start")HostMessageApi.setUp(flutterPluginBinding.binaryMessenger, this)flutterMessageApi = FlutterMessageApi(flutterPluginBinding.binaryMessenger)Log.e(tag, "onAttachedToEngine-end")}override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {HostMessageApi.setUp(binding.binaryMessenger, null)}override fun flutter2Native(message: String, type: Long): String {print("flutter2Native=message=start=>$message=type=$type")return "flutter2Native=message=$message=type=$type"}override fun flutter2NativeAsync(message: String,type: Long,callback: (Result<String>) -> Unit) {print("flutter2NativeAsync=message=$message=type=$type")callback(Result.success("flutter2NativeAsync=message=$message=type=$type"))if (type == 1L) {closeCurrentActivity()}}private fun closeCurrentActivity() {activity?.finish()}override fun onAttachedToActivity(binding: ActivityPluginBinding) {this.activity = binding.activity}override fun onDetachedFromActivityForConfigChanges() {}override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {}override fun onDetachedFromActivity() {this.activity = null}
}
5. io.flutter.embedding.engine.plugins.util.GeneratedPluginRegister 自动注册插件
 public static void registerGeneratedPlugins(@NonNull FlutterEngine flutterEngine) {try {Class<?> generatedPluginRegistrant =Class.forName("io.flutter.plugins.GeneratedPluginRegistrant");Method registrationMethod =generatedPluginRegistrant.getDeclaredMethod("registerWith", FlutterEngine.class);registrationMethod.invoke(null, flutterEngine);} catch (Exception e) {Log.e(TAG,"Tried to automatically register plugins with FlutterEngine ("+ flutterEngine+ ") but could not find or invoke the GeneratedPluginRegistrant.");Log.e(TAG, "Received exception while registering", e);}}
package io.flutter.plugins;import androidx.annotation.Keep;
import androidx.annotation.NonNull;
import io.flutter.Log;import io.flutter.embedding.engine.FlutterEngine;/*** Generated file. Do not edit.* This file is generated by the Flutter tool based on the* plugins that support the Android platform.*/
@Keep
public final class GeneratedPluginRegistrant {private static final String TAG = "GeneratedPluginRegistrant";public static void registerWith(@NonNull FlutterEngine flutterEngine) {try {Log.e(TAG, "registerWith-start");flutterEngine.getPlugins().add(new BasePlugin());Log.e(TAG, "registerWith-end");} catch (Exception e) {Log.e(TAG, "Error registering plugin base_plugin, com.app.base_plugin.BasePlugin", e);}}
}
6 手动注册

flutterEngine.getPlugins().add(new BasePlugin());

7. 通信关闭flutter页面

使用flutter和原生通信,在收到消息以后,关闭当前页面
BasePlugin实现ActivityAware,获取到activity
在收到flutter传入进来的消息的时候关闭activity

8. dartEntrypointArgs传入参数
//跳转带入参数
val initialRoute = listOf(  "aaa","bbbb")startActivity(withNewEngine().dartEntrypointArgs(initialRoute)  .build(this))
//args就是传入的参数
void main(List<String> args){print("args=$args");runApp(const MyApp());
}
9. 通信传入参数
9.1 发送参数
 val initialRoute = listOf(  "/page2","bbbb")BasePlugin.getFlutterMessageApi()?.native2Flutter(initialRoute.joinToString("#"), 1){Log.e(tag, "flutter2NativeAsync: $it")FlutterLibrary.startFlutterPage(this)}
9.1 接收参数

class RouterPage extends StatefulWidget {const RouterPage({super.key});State<RouterPage> createState() => _RouterPageState();
}class _RouterPageState extends State<RouterPage> implements FlutterMessageApi {late String args;void initState() {super.initState();FlutterMessageApi.setUp(this);}String native2Flutter(String message, int type) {print("native2Flutter=$message $type");setState(() {args = message;});return "我是原生调用flutter方法返回的值=》native2Flutter";}Future<String> native2FlutterAsync(String message, int type) {args = message;print("native2Flutter=$message $type");return Future.value("我是原生调用flutter方法返回的值=》native2FlutterAsync");}Widget build(BuildContext context) {// final value = widget.args.isNotEmpty ? widget.args[0] : "/";if (args.isEmpty||!args.contains("#")) {return MyHomePage(title: "首页");}final value = args.split("#").first;switch (value) {case '/page2':return SecondPage();}print("_RouterPageState=build");return MyHomePage(title: "首页");}
}class MyHomePage extends StatefulWidget {const MyHomePage({super.key, required this.title});final String title;State<MyHomePage> createState() => _MyHomePageState();
}class _MyHomePageState extends State<MyHomePage>  {int _counter = 0;void _incrementCounter() {setState(() {_counter++;});}void initState() {super.initState();}Widget build(BuildContext context) {print("_MyHomePageState=build");return Scaffold(appBar: AppBar(title: Text(widget.title), leading: BackButton()),body: Center(child: Column(mainAxisAlignment: MainAxisAlignment.center,children: <Widget>[TextButton(onPressed: () {BasePlugin.flutter2NativeAsync("flutter2NativeAsync", 1).then((value,) {print("flutter2NativeAsync=$value");});},child: Text('flutter2NativeAsync'),),TextButton(onPressed: () {Navigator.of(context).pushNamed("/page2");},child: Text('flutter2NativeAsync'),),const Text('You have pushed the button this many times:'),Text('$_counter',style: Theme.of(context).textTheme.headlineMedium,),],),),floatingActionButton: FloatingActionButton(onPressed: _incrementCounter,tooltip: 'Increment',child: const Icon(Icons.add),), // This trailing comma makes auto-formatting nicer for build methods.);}}class SecondPage extends StatelessWidget {const SecondPage({super.key});Widget build(BuildContext context) {print("SecondPage=build");return Scaffold(appBar: AppBar(title: Text("SecondPage")),body: Center(child: TextButton(onPressed: () {Navigator.of(context).pop();},child: Text('关闭'),),),);}
}
10. android代码
package com.example.hunheandroidimport android.app.Application
import android.content.Context
import android.content.Intent
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.embedding.engine.FlutterEngineCache
import io.flutter.embedding.engine.FlutterEngineGroup
import io.flutter.embedding.engine.dart.DartExecutor
import io.flutter.plugins.BasePluginconst val ENGINE_ID = "my_engine_id"
object FlutterLibrary {fun init(application: Application) {FlutterEngineManager.getInstance().init(application)}fun startFlutterPage(context: Context) {val intent = FlutterActivity.withCachedEngine(ENGINE_ID).build(context).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)context.startActivity(intent)}
}// FlutterEngineManager.kt
class FlutterEngineManager private constructor() {private lateinit var engineGroup: FlutterEngineGroupprivate var engine: FlutterEngine? = nullcompanion object {private var instance: FlutterEngineManager? = nullfun getInstance(): FlutterEngineManager {return instance ?: synchronized(this) {instance ?: FlutterEngineManager().also { instance = it }}}}fun init(application: Application) {engineGroup = FlutterEngineGroup(application)engine = engineGroup.createAndRunEngine(application,DartExecutor.DartEntrypoint.createDefault())FlutterEngineCache.getInstance().put(ENGINE_ID, engine)engine!!.plugins.add(BasePlugin())}fun getEngine(): FlutterEngine? {return engine}fun destroyEngine() {engine?.destroy()engine = null}
}class GoFlutterActivity : FlutterActivity(), HostMessageApi {private val tag = "GoFlutterActivity"private lateinit var binding: ActivityGoFlutterBindingoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)binding = ActivityGoFlutterBinding.inflate(layoutInflater)setContentView(binding.root)binding.goFlutter.setOnClickListener {//跳转到flutterval initialRoute = listOf(  // Map 形式的参数"/","aaaa")BasePlugin.getFlutterMessageApi()?.native2Flutter(initialRoute.joinToString("#"), 1) {Log.e(tag, "flutter2NativeAsync: $it")FlutterLibrary.startFlutterPage(this)}}binding.goFlutter2.setOnClickListener {//跳转到flutterval initialRoute = listOf(  // Map 形式的参数"/page2","bbbb")BasePlugin.getFlutterMessageApi()?.native2Flutter(initialRoute.joinToString("#"), 1) {Log.e(tag, "flutter2NativeAsync: $it")FlutterLibrary.startFlutterPage(this)}}}override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {Log.e(tag, "configureFlutterEngine")
//        GeneratedPluginRegistrant.registerWith(flutterEngine)}override fun flutter2Native(message: String, type: Long): String {Log.e(tag, "$message$type")return "我是flutter调用原生方法的返回值=》flutter2Native"}override fun flutter2NativeAsync(message: String,type: Long,callback: (Result<String>) -> Unit) {Log.e(tag, "$message$type")callback(Result.success("我是flutter调用原生方法的返回值=》flutter2NativeAsync"))}}
11 源码

gitee

相关文章:

android,flutter 混合开发,pigeon通信,传参

文章目录 app效果native和flutter通信的基础知识1. 编解码器 一致性和完整性&#xff0c;安全性&#xff0c;性能优化2. android代码3. dart代码 1. 创建flutter_module2.修改 Android 项目的 settings.gradle&#xff0c;添加 Flutter module3. 在 Android app 的 build.gradl…...

at32f403a rt thread led基础bsp工程测试

1.led工程官方bsp使用 导出一个独立的AT32F403A的BSP工程 下载RTT源代码 gitee更新较慢 https://gitee.com/rtthread/rt-thread github版本更新最新 https://github.com/RT-Thread/rt-thread. 切换到V5.1.0分支(使用一个发布版本可以避免不必要的bug) 导出一个独立的AT32BSP…...

DeepSeek写贪吃蛇手机小游戏

DeepSeek写贪吃蛇手机小游戏 提问 根据提的要求&#xff0c;让DeepSeek整理的需求&#xff0c;进行提问&#xff0c;内容如下&#xff1a; 请生成一个包含以下功能的可运行移动端贪吃蛇H5文件&#xff1a; 要求 蛇和食物红点要清晰&#xff0c;不超过屏幕外 下方有暂停和重新…...

【好玩的工具和命令】 ASCII 艺术生成工具: figlet

figlet 是一款用于生成 ASCII 艺术文字的工具&#xff0c;支持多种字体样式。它能将输入的文本转换为由字符组成的大型字母图案&#xff0c;广泛应用于命令行环境下的标题展示或装饰。 核心功能 生成 ASCII 文字艺术&#xff1a;将普通文本转化为大号的、由字符构成的艺术字…...

工具--安川伺服故障代码

上传一下安川伺服故障代码&#xff0c;后续结合实际维修经验&#xff0c;逐个整理分析&#xff0c;绝对超出手册经验 故障代码 故障描述 a.020/a.02 用户参数和数检查异常 1 a.021/a.02 参数格式化异常 1 a.022/a.02 系统参数和数检查异常 1 a.023/a.02 参数密码异常…...

车载软件架构 --- OEM主机厂如何打入软件供应商内部?

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 简单,单纯,喜欢独处,独来独往,不易合同频过着接地气的生活,除了生存温饱问题之外,没有什么过多的欲望,表面看起来很高冷,内心热情,如果你身…...

AI 编程助手 cursor的系统提示词 prompt

# Role 你是一名极其优秀具有10年经验的产品经理和精通java编程语言的架构师。与你交流的用户是不懂代码的初中生&#xff0c;不善于表达产品和代码需求。你的工作对用户来说非常重要&#xff0c;完成后将获得10000美元奖励。 # Goal 你的目标是帮助用户以他容易理解的…...

Matlab写入点云数据到Rosbag

最近有需要读取一个点云并做处理后&#xff0c;重新写回rosbag。网上有很多读取的教程&#xff0c;但没有写入。自己写入时也遇到了很多麻烦&#xff0c;踩了一堆坑进行记录。 1. rosbag中一个lidar的msg有哪些信息&#xff1f; 通过如下代码&#xff0c;先读取一个rosbag的l…...

业务流程相关的权威认证和培训有哪些

业务流程的认证和培训种类繁多&#xff0c;旨在帮助专业人士掌握业务流程管理 (BPM) 的知识和技能&#xff0c;从而提升个人职业发展和组织运营效率。下面分别介绍&#xff1a; 一、 业务流程认证和培训的种类 业务流程的认证和培训可以大致分为以下几类&#xff0c;涵盖了不…...

基于Spring Boot的兴顺物流管理系统设计与实现(LW+源码+讲解)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…...

【算法系列】荷兰国旗问题:三指针法原地排序

一、题目(leetcode75 颜色分类 --三分数组) 二、思路 算法核心&#xff1a;三指针分治策略 该问题被称为“荷兰国旗问题”&#xff08;Dutch National Flag Problem&#xff09;&#xff0c;由计算机科学家Edsger Dijkstra提出。其核心思想是通过三个指针将数组划分为三个区…...

DeepSeek R1本地+私有云版医疗AI部署开发成功案例技术剖析

1. 引言 1.1 研究背景与意义 随着科技的飞速发展,人工智能(AI)在医疗领域的应用正逐渐成为推动医疗行业变革的重要力量。近年来,医疗 AI 取得了显著的进展,从疾病诊断、药物研发到医疗管理等各个环节,AI 技术都展现出了巨大的潜力。它能够处理和分析海量的医疗数据,为…...

ARM64 Trust Firmware [五]

本章介绍 ATF 中的 Runtime Service 是如何定义和被调用的。 要了解 SMC&#xff0c;必须从 SMC 指令本身开始&#xff0c;其指令如下图&#xff1a; 指令格式为&#xff1a;SMC #<imm>&#xff0c;从官方文档了解到该指令只能在 EL1 以及更高的异常等级上调用&#xff…...

rkipc main.c 中 rk_param_init函数分析

rk_param_init函数 这个函数是用来读取配置文件进行参数配置 这个函数在 luckfox-pico/project/app/rk_smart_door/smart_door/common/uvc/param/param.c 中 这个函数在main函数中被调用 //通过-c 配置文件路径 把配置文件传进来 case c:rkipc_ini_path_ optarg;//调用&am…...

正确清理C盘空间

一.系统清理 正确清理C盘空间主要是删除不需要的文件和应用程序&#xff0c;以释放磁盘空间。以下是一些常用的方法&#xff1a; 删除临时文件&#xff1a;在Windows搜索框中输入“%temp%”&#xff0c;打开临时文件夹&#xff0c;将其中的文件全部删除。 清理回收站&#xf…...

http代理IP怎么实现?如何解决代理IP访问不了问题?

HTTP代理是一种网络服务&#xff0c;它充当客户端和目标服务器之间的中介。当客户端发送请求时&#xff0c;请求首先发送到代理服务器&#xff0c;然后由代理服务器转发到目标服务器。同样&#xff0c;目标服务器的响应也会先发送到代理服务器&#xff0c;再由代理服务器返回给…...

【Gin-Web】Bluebell社区项目梳理5:投票功能分析与实现

本文目录 一、投票功能投票流程实现代码redis投票 一、投票功能 投票流程 首先我们要明确&#xff0c;就是 谁&#xff08;哪个用户&#xff1a;userID&#xff09; 给 哪个帖子&#xff08;postID&#xff09; 投了 什么票&#xff08;赞成票or反对票&#xff09;。 赞成票…...

多人协同创作gitea

多人协同创作gitea 在多台设备上协同使用Gitea&#xff0c;主要是通过网络访问Gitea服务器上的仓库来进行代码管理和协作。以下是一些关键步骤和建议&#xff0c;帮助你在多台设备上高效地使用Gitea进行协作&#xff1a; 1. 确保Gitea服务可访问 首先&#xff0c;你需要确保…...

Java 大视界 -- Java 大数据未来十年的技术蓝图与发展愿景(95)

&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎来到 青云交的博客&#xff01;能与诸位在此相逢&#xff0c;我倍感荣幸。在这飞速更迭的时代&#xff0c;我们都渴望一方心灵净土&#xff0c;而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识&#xff0c;也…...

idea连接gitee完整教程

文章目录 idea连接gitee(使用idea远程兼容gitee) 使用idea远程兼容gitee并反向创建仓库和分支...

Flask RESTful 示例

目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题&#xff1a; 下面创建一个简单的Flask RESTful API示例。首先&#xff0c;我们需要创建环境&#xff0c;安装必要的依赖&#xff0c;然后…...

Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误

HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误&#xff0c;它们的含义、原因和解决方法都有显著区别。以下是详细对比&#xff1a; 1. HTTP 406 (Not Acceptable) 含义&#xff1a; 客户端请求的内容类型与服务器支持的内容类型不匹…...

Oracle查询表空间大小

1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...

多场景 OkHttpClient 管理器 - Android 网络通信解决方案

下面是一个完整的 Android 实现&#xff0c;展示如何创建和管理多个 OkHttpClient 实例&#xff0c;分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...

EtherNet/IP转DeviceNet协议网关详解

一&#xff0c;设备主要功能 疆鸿智能JH-DVN-EIP本产品是自主研发的一款EtherNet/IP从站功能的通讯网关。该产品主要功能是连接DeviceNet总线和EtherNet/IP网络&#xff0c;本网关连接到EtherNet/IP总线中做为从站使用&#xff0c;连接到DeviceNet总线中做为从站使用。 在自动…...

爬虫基础学习day2

# 爬虫设计领域 工商&#xff1a;企查查、天眼查短视频&#xff1a;抖音、快手、西瓜 ---> 飞瓜电商&#xff1a;京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空&#xff1a;抓取所有航空公司价格 ---> 去哪儿自媒体&#xff1a;采集自媒体数据进…...

OPENCV形态学基础之二腐蚀

一.腐蚀的原理 (图1) 数学表达式&#xff1a;dst(x,y) erode(src(x,y)) min(x,y)src(xx,yy) 腐蚀也是图像形态学的基本功能之一&#xff0c;腐蚀跟膨胀属于反向操作&#xff0c;膨胀是把图像图像变大&#xff0c;而腐蚀就是把图像变小。腐蚀后的图像变小变暗淡。 腐蚀…...

安卓基础(aar)

重新设置java21的环境&#xff0c;临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的&#xff1a; MyApp/ ├── app/ …...

JS手写代码篇----使用Promise封装AJAX请求

15、使用Promise封装AJAX请求 promise就有reject和resolve了&#xff0c;就不必写成功和失败的回调函数了 const BASEURL ./手写ajax/test.jsonfunction promiseAjax() {return new Promise((resolve, reject) > {const xhr new XMLHttpRequest();xhr.open("get&quo…...

mac 安装homebrew (nvm 及git)

mac 安装nvm 及git 万恶之源 mac 安装这些东西离不开Xcode。及homebrew 一、先说安装git步骤 通用&#xff1a; 方法一&#xff1a;使用 Homebrew 安装 Git&#xff08;推荐&#xff09; 步骤如下&#xff1a;打开终端&#xff08;Terminal.app&#xff09; 1.安装 Homebrew…...