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

Flutter 学习之旅 之 Flutter 和 Android 原生 实现数据交互的MethodChanel和EventChannel方式的简单整理

Flutter 学习之旅 之 Flutter 和 Android 原生 实现数据交互的MethodChanel和EventChannel方式的简单整理

目录

Flutter 学习之旅 之 Flutter 和 Android 原生 实现数据交互的MethodChanel和EventChannel方式的简单整理

一、简单介绍

二、Flutter 和 Android 原生之间的数据交互

1、MethodChannel

2、EventChannel

3、MethodChannel 与 EventChannel 的主要区别

三、简单的效果预览

四、案例基础环境配置

五、使用 MethodChanel 实现交互

六、使用 MethodChanel 实现交互


一、简单介绍

Flutter 是一款开源的 UI 软件开发工具包,由 Google 开发和维护。它允许开发者使用一套代码同时构建跨平台的应用程序,包括移动设备(iOS 和 Android)、Web 和桌面平台(Windows、macOS 和 Linux)。

Flutter 使用 Dart 编程语言,它可以将代码编译为 ARM 或 Intel 机器代码以及 JavaScript,从而实现快速的性能。Flutter 提供了一个丰富的预置小部件库,开发者可以根据自己的需求灵活地控制每个像素,从而创建自定义的、适应性强的设计,这些设计在任何屏幕上都能呈现出色的外观和感觉。

二、Flutter 和 Android 原生之间的数据交互

Flutter 和 Android 原生之间的数据交互主要通过 平台通道(Platform Channels) 实现,包括 MethodChannel 和 EventChannel。

1、MethodChannel

定义: MethodChannel 是 Flutter 与原生平台之间进行通信的一种方式,允许 Dart 代码调用原生代码中的方法,并接收返回结果。它支持双向通信,即 Dart 可以调用原生方法,原生也可以调用 Dart 方法。

特点

  • 单次调用:主要用于一次性请求和响应,适合执行特定的操作并获取结果。

  • 简单高效:适合简单的数据交互,开销较低。

  • 异步通信:调用原生方法后,Dart 代码会等待原生代码处理完成并返回结果。

使用场景

  • 调用原生平台的特定功能,如获取设备信息、访问传感器等。

  • 执行一次性任务,如打开相机、保存文件等。

2、EventChannel

定义: EventChannel 是用于接收原生平台发送的连续事件流的通道。它允许原生代码向 Dart 代码发送一系列数据或事件,适合实时数据更新。

特点

  • 持续数据流:适合处理连续的事件或数据流,如传感器数据、网络状态更新等。

  • 双向通信:虽然主要用于原生向 Dart 发送数据,但也支持 Dart 向原生发送请求。

  • 异步通信:数据流是异步发送的,Dart 代码通过监听事件流来接收数据。

使用场景

  • 监听传感器数据(如加速度计、陀螺仪)。

  • 监听网络状态变化、实时消息推送等。

3、MethodChannel 与 EventChannel 的主要区别
特点MethodChannelEventChannel
通信模式单次调用,请求-响应模式持续数据流,事件监听模式
适用场景适合一次性操作,如调用原生方法适合实时数据流,如传感器数据
通信方向主要用于 Dart 调用原生方法主要用于原生向 Dart 发送事件
性能开销较低,适合简单数据交互较高,适合实时数据更新

总结

  • MethodChannel 是用于单次调用原生方法的通道,适合执行特定操作并获取结果。

  • EventChannel 是用于接收原生平台发送的连续事件流的通道,适合实时数据更新。

  • 选择哪种通道取决于具体需求:如果需要执行一次性任务,使用 MethodChannel;如果需要实时接收数据流,使用 EventChannel。

三、简单的效果预览

MethodChanel:

EventChanel:

四、案例基础环境配置

1、在 Android Studio 中创建一个 Flutter 工程

2、选择创建 一个 Flutter Module

3、再创建一个 Android 工程

4、回到 Flutter 工程,进行 Flutter - Build AAR 编译

5、编译完成后, 会有提示如何在 Android 工程中引用

6、在 Android 工程中,根据提示,在 settings.gradle 和 build.gradle 添加如下的提示内容

7、在 AndroidManifest.xml 中,添加 FlutterActivity 相关属性

五、使用 MethodChanel 实现交互

1、Flutter 端代码

import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; // 导入用于与原生平台交互的包void main() {runApp(MyApp()); // 启动应用
}// 定义一个无状态的 MyApp 组件,作为应用的根组件
class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return MaterialApp(home: Scaffold( // 使用 Scaffold 提供基本的布局结构appBar: AppBar(title: Text('Flutter Native Interaction')), // 设置应用栏标题body: Center(child: NativeStringWidget()), // 在中心放置 NativeStringWidget 组件),);}
}// 定义一个有状态的 NativeStringWidget 组件,用于与原生平台交互
class NativeStringWidget extends StatefulWidget {@override_NativeStringWidgetState createState() => _NativeStringWidgetState();
}// 定义 NativeStringWidget 的状态类
class _NativeStringWidgetState extends State<NativeStringWidget> {String _nativeString = 'Press button to get native string'; // 初始化显示的字符串String _result = ' native call flutter string'; // 初始化另一个显示的字符串// 定义一个异步方法,用于调用原生平台的方法Future<void> _getNativeString() async {try {// 通过 MethodChannel 调用原生平台的 'getNativeString' 方法final String result = await MethodChannel('com.example/native').invokeMethod('getNativeString');setState(() {_nativeString = result; // 更新显示的字符串为原生方法返回的结果});} catch (e) {setState(() {// 如果调用失败,更新显示的字符串为错误信息_nativeString = 'Failed to get native string: $e';});}}// 定义另一个异步方法,用于调用原生平台的 'jumpToNative' 方法Future<void> _incrementCounter() async {Map<String, dynamic> result = {'message': '我从Flutter页面回来了'}; // 准备传递给原生平台的参数try {// 调用原生平台的 'jumpToNative' 方法,并传递参数_result = await MethodChannel('com.example/native').invokeMethod('jumpToNative', result);} on PlatformException catch (e) {// 如果调用失败,捕获 PlatformException 并更新显示的字符串为错误信息_result = "Failed: '${e.message}'.";}setState(() {}); // 触发界面更新}@overridevoid initState() {_incrementCounter(); // 在组件初始化时调用 _incrementCounter 方法super.initState(); // 调用父类的初始化方法}@overrideWidget build(BuildContext context) {return Column( // 使用 Column 布局,垂直排列子组件mainAxisAlignment: MainAxisAlignment.center, // 将子组件居中对齐children: <Widget>[Text(_nativeString), // 显示从原生平台获取的字符串SizedBox(height: 20), // 添加一个高度为 20 的间隔ElevatedButton( // 定义一个按钮onPressed: _getNativeString, // 点击按钮时调用 _getNativeString 方法child: Text('Get Native String'), // 按钮上的文本),Text(_result), // 显示另一个字符串],);}
}

代码功能说明

  1. MyApp

    • 定义了应用的根组件,使用 MaterialAppScaffold 提供基本的布局结构。

    • Scaffoldbody 中放置了 NativeStringWidget 组件。

  2. NativeStringWidget

    • 定义了一个有状态的组件,用于与原生平台交互。

    • 包含两个状态变量 _nativeString_result,分别用于显示从原生平台获取的字符串和调用结果。

  3. _getNativeString 方法

    • 通过 MethodChannel 调用原生平台的 getNativeString 方法。

    • 如果调用成功,更新 _nativeString 为返回的结果;如果失败,显示错误信息。

  4. _incrementCounter 方法

    • 通过 MethodChannel 调用原生平台的 jumpToNative 方法,并传递一个包含消息的 Map

    • 如果调用成功,更新 _result 为返回的结果;如果失败,捕获异常并显示错误信息。

  5. initState 方法

    • 在组件初始化时调用 _incrementCounter 方法,执行与原生平台的交互。

  6. build 方法

    • 使用 Column 布局,垂直排列了两个文本组件和一个按钮。

    • 按钮点击时调用 _getNativeString 方法,从原生平台获取字符串并更新界面。

与原生平台的交互

  • 在 Dart 端,通过 MethodChannel 调用原生平台的方法。

  • 在原生平台(如 Android 或 iOS),需要定义对应的 MethodChannel,并实现相应的逻辑来处理 Dart 端的调用请求。

  • 原生平台可以通过 MethodChannel 返回结果,Dart 端通过 await 接收并处理返回值。

2、Android 端的代码

package com.example.test_android_embedding_flutter_0428;import android.widget.Toast;import io.flutter.embedding.android.FlutterActivity; // 导入 FlutterActivity,用于嵌入 Flutter
import io.flutter.embedding.engine.FlutterEngine; // 导入 FlutterEngine,用于管理 Flutter 的运行
import io.flutter.plugin.common.MethodChannel; // 导入 MethodChannel,用于与 Flutter 的方法调用交互// 定义 MainActivity,继承自 FlutterActivity
public class MainActivity extends FlutterActivity {private static final String CHANNEL = "com.example/native"; // 定义一个常量字符串,作为 MethodChannel 的名称// 重写 configureFlutterEngine 方法,用于配置 FlutterEngine@Overridepublic void configureFlutterEngine(FlutterEngine flutterEngine) {super.configureFlutterEngine(flutterEngine); // 调用父类的 configureFlutterEngine 方法// 创建一个 MethodChannel 实例new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL).setMethodCallHandler((call, result) -> { // 设置方法调用处理器// 判断调用的方法名if (call.method.equals("getNativeString")) {// 如果方法名为 "getNativeString",返回一个字符串给 Flutterresult.success("Hello from Android Native!");} else if (call.method.equals("jumpToNative")) {// 如果方法名为 "jumpToNative",处理传递的参数if (call.arguments != null) {// 如果有参数传递,显示一个 ToastToast.makeText(this, call.argument("message"), Toast.LENGTH_SHORT).show();} else {// 如果没有参数传递,显示一个提示 ToastToast.makeText(this, "回调参数为空", Toast.LENGTH_SHORT).show();}// 返回一个成功消息给 Flutterresult.success("Activity -> Flutter 接收回调的返回值成功");} else {// 如果调用的方法名不匹配,返回未实现的错误result.notImplemented();}});}
}

代码功能说明

  1. MainActivity

    • 继承自 FlutterActivity,这是 Flutter 提供的一个用于嵌入 Flutter 内容的 Android 活动类。

    • 重写了 configureFlutterEngine 方法,用于配置 Flutter 的运行环境。

  2. CHANNEL

    • 定义了一个常量字符串 "com.example/native",用于标识与 Flutter 交互的 MethodChannel。

    • 这个字符串必须与 Flutter 端的 MethodChannel 名称一致。

  3. configureFlutterEngine 方法

    • 在这个方法中,创建了一个 MethodChannel 实例,用于处理来自 Flutter 的方法调用。

    • flutterEngine.getDartExecutor().getBinaryMessenger() 是 Flutter 提供的用于与 Dart 交互的消息传递器。

  4. setMethodCallHandler

    • 设置了一个方法调用处理器,用于处理 Flutter 端调用的方法。

    • 处理器接收两个参数:

      • call:包含调用的方法名和参数。

      • result:用于返回调用结果。

  5. 方法处理逻辑

    • getNativeString

      • 如果 Flutter 调用的方法名是 "getNativeString",返回一个字符串 "Hello from Android Native!" 给 Flutter。

    • jumpToNative

      • 如果 Flutter 调用的方法名是 "jumpToNative",检查是否有参数传递。

      • 如果有参数,提取参数中的 "message" 并显示一个 Toast。

      • 如果没有参数,显示一个提示 Toast。

      • 最后返回一个成功消息 "Activity -> Flutter 接收回调的返回值成功" 给 Flutter。

    • 其他方法

      • 如果调用的方法名不匹配,返回 notImplemented,表示该方法未实现。

与 Flutter 的交互

  • 在 Android 端,通过 MethodChannel 接收 Flutter 的方法调用。

  • 根据方法名处理逻辑,返回结果给 Flutter。

  • 如果需要向 Flutter 传递数据,可以通过 result.success() 方法返回成功结果,或者通过 result.error() 方法返回错误信息。

六、使用 MethodChanel 实现交互

1、Flutter 端代码

import 'package:flutter/material.dart'; // 导入 Flutter 的 Material Design 组件库
import 'package:flutter/services.dart'; // 导入用于与原生平台交互的包void main() {runApp(MyApp()); // 启动应用,运行 MyApp 组件
}// 定义一个无状态的 MyApp 组件,作为应用的根组件
class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return MaterialApp( // 使用 MaterialApp 提供基本的 Material Design 风格home: Scaffold( // 使用 Scaffold 提供基本的布局结构appBar: AppBar( // 设置应用栏(AppBar)title: Text('Flutter Native Interaction'), // 应用栏标题),body: Center( // 将子组件居中对齐child: NativeEventsWidget(), // 在中心放置 NativeEventsWidget 组件),),);}
}// 定义一个有状态的 NativeEventsWidget 组件,用于接收原生平台发送的事件
class NativeEventsWidget extends StatefulWidget {@override_NativeEventsWidgetState createState() => _NativeEventsWidgetState();
}// 定义 NativeEventsWidget 的状态类
class _NativeEventsWidgetState extends State<NativeEventsWidget> {late Stream<String> _nativeEvents; // 声明一个 Stream 对象,用于接收原生平台的事件流@overridevoid initState() {super.initState(); // 调用父类的初始化方法// 初始化 EventChannel,用于接收原生平台发送的事件流_nativeEvents = EventChannel('com.example/native_events').receiveBroadcastStream() // 接收广播事件流.map((event) => event.toString()); // 将事件数据转换为字符串}@overrideWidget build(BuildContext context) {return StreamBuilder<String>( // 使用 StreamBuilder 构建 UI,监听事件流stream: _nativeEvents, // 监听的事件流builder: (context, snapshot) { // 构建器,根据事件流的状态构建 UIif (snapshot.hasError) { // 如果发生错误return Text('Error: ${snapshot.error}'); // 显示错误信息} else if (snapshot.hasData) { // 如果有数据return Text('Native Event: ${snapshot.data}'); // 显示接收到的事件数据} else { // 如果没有数据return CircularProgressIndicator(); // 显示一个加载指示器}},);}
}

代码功能说明

  1. MyApp

    • 定义了应用的根组件,使用 MaterialAppScaffold 提供基本的布局结构。

    • Scaffoldbody 中放置了 NativeEventsWidget 组件。

  2. NativeEventsWidget

    • 定义了一个有状态的组件,用于接收原生平台发送的事件流。

    • initState 方法中初始化了一个 EventChannel,用于接收原生平台的事件流。

  3. EventChannel

    • 通过 EventChannelreceiveBroadcastStream 方法接收原生平台发送的事件流。

    • 使用 map 方法将事件数据转换为字符串。

  4. StreamBuilder

    • 使用 StreamBuilder 构建 UI,监听事件流的状态。

    • 根据事件流的状态(是否有数据、是否发生错误)动态更新 UI:

      • 如果发生错误,显示错误信息。

      • 如果有数据,显示接收到的事件数据。

      • 如果没有数据,显示一个加载指示器。

与原生平台的交互

  • 在 Dart 端,通过 EventChannel 接收原生平台发送的事件流。

  • 原生平台需要通过 EventChannel 发送事件流,Flutter 端通过 StreamBuilder 监听这些事件并更新 UI。

注意事项

  • 确保原生平台(如 Android 或 iOS)已经正确配置了 EventChannel,并能够发送事件流。

  • EventChannel 的名称(如 'com.example/native_events')必须与原生平台的配置一致。

2、Android 端的代码

package com.example.test_android_embedding_flutter_0428;import android.os.Bundle; // 导入用于处理 Android 活动生命周期的 Bundle
import android.os.Handler; // 导入 Handler,用于在主线程上执行任务
import android.os.Looper; // 导入 Looper,用于获取主线程的 Looper 实例
import android.widget.Toast; // 导入 Toast,用于显示提示信息import io.flutter.embedding.android.FlutterActivity; // 导入 FlutterActivity,用于嵌入 Flutter
import io.flutter.embedding.engine.FlutterEngine; // 导入 FlutterEngine,用于管理 Flutter 的运行
import io.flutter.plugin.common.EventChannel; // 导入 EventChannel,用于与 Flutter 的事件流交互// 定义 MainActivity,继承自 FlutterActivity
public class MainActivity extends FlutterActivity {private static final String EVENT_CHANNEL = "com.example/native_events"; // 定义 EventChannel 的名称@Overridepublic void configureFlutterEngine(FlutterEngine flutterEngine) {super.configureFlutterEngine(flutterEngine); // 调用父类的 configureFlutterEngine 方法// 创建 EventChannel 并设置事件处理器new EventChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), EVENT_CHANNEL).setStreamHandler(new EventChannel.StreamHandler() {private Handler mainHandler = new Handler(Looper.getMainLooper()); // 获取主线程的 Handler@Overridepublic void onListen(Object arguments, EventChannel.EventSink events) {// 在子线程中模拟事件发送new Thread(() -> {for (int i = 0; i < 10; i++) { // 模拟发送 10 次事件try {Thread.sleep(1000); // 每隔 1 秒发送一次事件final String event = "Event " + i + " from Android"; // 构造事件数据// 使用主线程的 Handler 来发送事件mainHandler.post(() -> {events.success(event); // 发送成功事件});} catch (InterruptedException e) {// 如果线程被中断,发送错误事件mainHandler.post(() -> {events.error("ERROR", "Thread interrupted", null); // 发送错误事件});}}// 发送结束事件mainHandler.post(events::endOfStream); // 通知 Flutter 事件流已结束}).start();}@Overridepublic void onCancel(Object arguments) {// 取消事件监听时的处理逻辑// 这里可以添加清理资源或停止事件发送的逻辑}});}
}

代码功能说明

  1. MainActivity

    • 继承自 FlutterActivity,这是 Flutter 提供的一个用于嵌入 Flutter 内容的 Android 活动类。

    • 重写了 configureFlutterEngine 方法,用于配置 Flutter 的运行环境。

  2. EVENT_CHANNEL

    • 定义了一个常量字符串 "com.example/native_events",用于标识与 Flutter 交互的 EventChannel

    • 这个字符串必须与 Flutter 端的 EventChannel 名称一致。

  3. configureFlutterEngine 方法

    • 在这个方法中,创建了一个 EventChannel 实例,用于处理来自 Flutter 的事件流请求。

    • flutterEngine.getDartExecutor().getBinaryMessenger() 是 Flutter 提供的用于与 Dart 交互的消息传递器。

  4. setStreamHandler

    • 设置了一个事件流处理器,用于处理 Flutter 端的事件流请求。

    • 处理器需要实现两个方法:

      • onListen:当 Flutter 端开始监听事件流时调用。

      • onCancel:当 Flutter 端取消监听事件流时调用。

  5. onListen 方法

    • 在子线程中模拟事件发送:

      • 使用 for 循环模拟发送 10 次事件。

      • 每隔 1 秒发送一次事件,通过 Thread.sleep(1000) 实现延时。

      • 构造事件数据 "Event " + i + " from Android"

  1. onCancel 方法

    • 当 Flutter 端取消监听事件流时调用。

    • 这里可以添加清理资源或停止事件发送的逻辑(当前代码中未实现具体逻辑)。

与 Flutter 的交互

  • 在 Android 端,通过 EventChannel 向 Flutter 发送事件流。

  • Flutter 端通过 StreamBuilder 监听这些事件并更新 UI。

  • 确保 EventChannel 的名称(如 "com.example/native_events")与 Flutter 端的配置一致。

通过上述代码和注释,你可以清楚地理解如何在 Android 端通过 EventChannel 向 Flutter 发送事件流,并确保所有与 UI 相关的操作都在主线程上执行。

七、其他代码

1、AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"><applicationandroid:allowBackup="true"android:dataExtractionRules="@xml/data_extraction_rules"android:fullBackupContent="@xml/backup_rules"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:roundIcon="@mipmap/ic_launcher_round"android:supportsRtl="true"android:theme="@style/Theme.Test_Android_Embedding_Flutter_0428"tools:targetApi="31"><activityandroid:name=".MainActivity"android:exported="true"android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"android:hardwareAccelerated="true"android:windowSoftInputMode="adjustResize"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity></application>
</manifest>

2、settings.gradle

pluginManagement {// 配置插件管理相关的设置repositories {// 定义插件管理的仓库来源google {// 使用 Google 的 Maven 仓库content {// 定义需要包含的组(group)正则表达式includeGroupByRegex("com\\.android.*")  // 包含以 "com.android" 开头的组includeGroupByRegex("com\\.google.*")   // 包含以 "com.google" 开头的组includeGroupByRegex("androidx.*")       // 包含以 "androidx" 开头的组}}mavenCentral()  // 使用 Maven Central 仓库gradlePluginPortal()  // 使用 Gradle 插件门户}
}dependencyResolutionManagement {// 配置依赖解析相关的设置repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)  // 设置仓库模式,如果项目中有仓库配置错误则失败repositories {google()  // 使用 Google 的 Maven 仓库mavenCentral()  // 使用 Maven Central 仓库}// 定义 Flutter 模块的存储 URLString storageUrl = System.env.FLUTTER_STORAGE_BASE_URL ?: "https://storage.googleapis.com"// 如果环境变量 FLUTTER_STORAGE_BASE_URL 未设置,则使用默认的 Flutter 存储 URL// 注意:该 URL 是 Flutter 依赖项的默认存储位置,但由于网络原因,可能无法直接访问// 如果您遇到问题,请检查链接的合法性或在网络正常的情况下重试repositories {maven {// 定义本地 Maven 仓库路径url 'D:/UsingForXAN/Projects/AndroidStudioProjects/FlutterProject/test_flutter_module_0418/build/host/outputs/repo'}maven {// 定义远程 Maven 仓库路径url "$storageUrl/download.flutter.io"}}
}// 设置项目的根项目名称
rootProject.name = "Test_Android_Embedding_Flutter_0428"
// 包含子项目 'app',这是 Android 项目的主模块
include ':app'

3、build.gradle

plugins {alias(libs.plugins.android.application)
}
// 定义项目使用的插件
// 这里使用了 alias 来引用预定义的插件,通常是定义在 buildSrc 或其他地方的插件别名
// 这里引用的是 Android 应用插件,用于构建 Android 应用android {namespace 'com.example.test_android_embedding_flutter_0428'// 设置项目的命名空间,用于区分不同的应用compileSdk 35// 设置编译 SDK 的版本,这里是 API 级别 35defaultConfig {applicationId "com.example.test_android_embedding_flutter_0428"// 设置应用的唯一标识符minSdk 28// 设置应用支持的最低 SDK 版本targetSdk 34// 设置应用的目标 SDK 版本versionCode 1// 设置应用的版本代码,用于内部版本管理versionName "1.0"// 设置应用的版本名称,显示给用户testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"// 设置用于运行 Android 测试的测试运行器}buildTypes {release {minifyEnabled false// 是否启用代码混淆,这里设置为 false 表示不启用proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'// 指定 ProGuard 配置文件,用于代码混淆规则}profile {initWith debug// 将 profile 构建类型初始化为 debug 构建类型的配置}}compileOptions {sourceCompatibility JavaVersion.VERSION_1_8// 设置源代码的 Java 版本兼容性targetCompatibility JavaVersion.VERSION_1_8// 设置目标字节码的 Java 版本兼容性}
}dependencies {// 定义项目的依赖项implementation libs.appcompat// 添加 AndroidX AppCompat 库的依赖,用于支持旧版本 Android 的兼容性implementation libs.material// 添加 Material Design 组件库的依赖,用于构建 Material Design 风格的界面implementation libs.activity// 添加 Activity 组件库的依赖,用于支持新的 Activity APIimplementation libs.constraintlayout// 添加 ConstraintLayout 布局库的依赖,用于构建复杂的布局testImplementation libs.junit// 添加 JUnit 测试框架的依赖,用于编写单元测试androidTestImplementation libs.ext.junit// 添加扩展的 JUnit 测试框架的依赖,用于 Android 测试androidTestImplementation libs.espresso.core// 添加 Espresso 测试框架的依赖,用于编写 UI 测试debugImplementation 'com.example.test_flutter_module_0418:flutter_debug:1.0'// 在 debug 构建类型中添加 Flutter 模块的 debug 版本依赖profileImplementation 'com.example.test_flutter_module_0418:flutter_profile:1.0'// 在 profile 构建类型中添加 Flutter 模块的 profile 版本依赖releaseImplementation 'com.example.test_flutter_module_0418:flutter_release:1.0'// 在 release 构建类型中添加 Flutter 模块的 release 版本依赖
}

相关文章:

Flutter 学习之旅 之 Flutter 和 Android 原生 实现数据交互的MethodChanel和EventChannel方式的简单整理

Flutter 学习之旅 之 Flutter 和 Android 原生 实现数据交互的MethodChanel和EventChannel方式的简单整理 目录 Flutter 学习之旅 之 Flutter 和 Android 原生 实现数据交互的MethodChanel和EventChannel方式的简单整理 一、简单介绍 二、Flutter 和 Android 原生之间的数据…...

STM32的SysTick

SysTick介绍 定义&#xff1a;Systick&#xff0c;即滴答定时器&#xff0c;是内核中的一个特殊定时器&#xff0c;用于提供系统级的定时服务。该定时器是一个24位的递减计数器&#xff0c;具有自动重载值寄存器的功能。当计数器到达自动重载值时&#xff0c;它会自动重新加载…...

【JS事件循环机制event-loop】

目录 0、总结1、Event-Loop 概念2、宏任务-微任务3、事件循环执行机制4、调用栈5、示例 0、总结 Tasks execute in order, and the browser may render between them 【宏任务按序执行&#xff0c;浏览器可以在它们之间进行渲染】Microtasks execute in order, and are execut…...

对比N+1查询和关联聚合查询

通常我们管第一种模式叫 “N1 查询”&#xff0c;第二种叫 “关联聚合查询”。下面从几个角度来比较&#xff0c;帮助你做出选择。 1. 性能与资源消耗 方案SQL 语句数网络往返次数数据库负载Java 处理N1 查询&#xff08;先查项目&#xff0c;再遍历项目查设备状态数&#xff…...

优化 Flutter 应用启动:从冷启动到就绪仅需 2 秒

冷启动序列剖析&#xff1a;冷启动时&#xff0c;Flutter 应用需经历引擎和 Dart VM 初始化、启动 Dart Isolate、渲染第一帧等步骤。Android 和 iOS 系统分别通过启动屏幕和 Storyboard 缓解启动延迟。应用大小、初始化工作、调试模式下的 JIT 编译等因素会影响冷启动时间。优…...

牟乃夏《ArcGIS Engine 地理信息系统开发教程》学习笔记 4-空间分析与高级功能开发

目录 一、核心组件与接口回顾 &#xff08;一&#xff09;空间分析基础架构 &#xff08;二&#xff09;网络分析模块 二、矢量数据空间分析实战 &#xff08;一&#xff09;缓冲区分析 &#xff08;二&#xff09;叠加分析&#xff08;以裁剪为例&#xff09; 三、栅格…...

UE 滚动提示条材质制作

需要两个贴图 先制作条纹屏闪 这里RGB输出连到alpha&#xff0c;0为白色&#xff0c;到1就为黑色了 因为这个图片是RGB输出代表三个图片&#xff0c;看贴图颜色就知道了&#xff0c;然后把这三个相加一下&#xff1b;链接自发光颜色&#xff0c; 这里设置速度变量 通过网盘分…...

金融业数字化转型——深入解读77页2024年中国金融体系指标大全【附全文阅读】

本文主要介绍了金融业通行宝典中国金融体系指标大全的内容,包括央行体系、商业银行体系、非银金融机构与地方金融组织的各项指标。文章详细分析了美联储资产负債表的结构,并概述了美日欧等主要经济体资产负债表状况。 重点内容: 1. 央行体系是金融分析的重点。 2. 美联储资产…...

研究:大模型输出一致性:确定性与随机性的场景化平衡

大模型在相同输入下的输出是否一致,本质上取决于其设计目标、任务性质以及技术实现方式。这一问题需要从技术原理、应用场景、用户需求三个维度进行深度分析: 一、技术实现:确定性与随机性的平衡 模型架构的确定性基础 大模型的核心参数(如权重矩阵)在训练完成后是固定的…...

数据分析1

一、常用数据处理模块Numpy Numpy常用于高性能计算&#xff0c;在机器学习常常作为传递数据的容器。提供了两种基本对象&#xff1a;ndarray、ufunc。 ndarray具有矢量算术运算和复杂广播能力的快速且节省空间的多维数组。 ufunc提供了对数组快速运算的标准数学函数。 ndar…...

vmare pro安装报错用户在命令行上发出了EULAS_AGREED=1,表示不接受许可协议的错误解决方法

问题现状和原因 用户在命令行上发出了EULAS_AGREED1&#xff0c;表示不接受许可协议的错误。 以上错误主要原因是因为机器安装过了vmare 卸载时没有卸载干净导致的。 解决方法&#xff1a; 1、控制面板-程序和功能-卸载程序。找到vamre卸载掉。 2、打开开始菜单输入注册表 …...

《Linux篇》基础开发工具——vim详细介绍

文章目录 1.软件包管理1.1 什么是软件包1.2 Linux软件生态 2.编辑器vim2.1 vim的正常/命令模式2.2 vim的末行模式2.3 vim的插入模式 3.配置vim 1.软件包管理 我们先来看一下再Linux是那个如何安装软件&#xff1f; 源码安装&#xff1a;软件是存在相互依赖的关系的&#xff0…...

AI图片跳舞生成视频,animate X本地部署。

本期内容打包限时免费下载https://www.kdocs.cn/l/cnQ5lNU5DFZB 对比不同算法&#xff0c;使用同一组图片和舞蹈视频。animate X官网&#xff0c;下载项目解压。按照官方教程下载模型&#xff0c;项目包和命名好的模型包已上传网盘&#xff0c;放到解压目录下即可。 安装好cond…...

Web技术与Apache网站部署

一、Web 基础与 HTTP 协议 1.1 静态网页与动态网页 静态网页 定义&#xff1a;由纯 HTML、CSS、JavaScript 构成&#xff0c;文件扩展名为 .htm 或 .html。内容在服务器生成后固定不变&#xff0c;仅通过客户端脚本&#xff08;如 JS&#xff09;实现视觉动态效果&#xff08…...

第七章:Server/Client Communication

Chapter 7: Server/Client Communication 从工具集成到服务器通信&#xff1a;如何让AI“远程协作”&#xff1f; 在上一章的工具与LLM集成中&#xff0c;我们已经能让AI调用真实世界的工具。但你是否想过&#xff1a;如果多个用户同时请求天气查询&#xff0c;或者需要远程控…...

Linux调试器 - gdb使用指南

目录 一、背景知识 二、开始使用 gdb &#xff08;一&#xff09;查看源代码相关指令 &#xff08;二&#xff09;程序执行控制指令 &#xff08;三&#xff09;断点相关指令 &#xff08;四&#xff09;变量操作相关指令 &#xff08;五&#xff09;其他常用指令 在Li…...

C++面试常青客:LRUCache最近最少使用算法

C面试常青客&#xff1a;LRUCache最近最少使用算法 文章目录 C面试常青客&#xff1a;LRUCache最近最少使用算法1.背景&#x1f3c6;2.原理&#x1f680;2.1基本原理2.2核心特性 3.结构3.1为什么需要 list<pair<int,int>>&#xff08;双向链表&#xff09;&#xf…...

【含文档+PPT+源码】基于微信小程序的社交摄影约拍平台的设计与实现

项目介绍 本课程演示的是一款基于微信小程序的社交摄影约拍平台的设计与实现&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的 Java 学习者。 1.包含&#xff1a;项目源码、项目文档、数据库脚本、软件工具等所有资料 2.带你从零开始部署运行本套系…...

jetson nano上Ubuntu系统调用摄像头bug

今天在做一个比赛的时候&#xff0c;通过调用摄像头做检测并输出目标角度和距离。刚开始用的是 cv::VideoCapture cap; cap.open("/dev/video0");没有任何问题&#xff0c;使用pnp解算得到的角度和距离都是正确的&#xff0c;画面也是小画面。 后面加了一些功能&…...

用Python做有趣的AI项目5:AI 画画机器人(图像风格迁移)

这个项目将使用 PyTorch 实现图像风格迁移&#xff08;Neural Style Transfer&#xff09;&#xff0c;让一张图片看起来具有另一张图片的“艺术风格”。 &#x1f527; 开发环境建议 Python 3.8 PyTorch&#xff08;pip install torch torchvision&#xff09; PIL&#x…...

一种用于从视网膜图像中识别疾病的 BERT 式自监督学习 CNN

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 抽象 在医学成像领域&#xff0c;深度学习的出现&#xff0c;尤其是卷积神经网络 &#xff08;CNN&#xff09; 的应用&#xff0c;彻底改变了医学影像的分析和解释。然而&#xff0c;深度学习方法通常依…...

OpenCV 图形API(68)图像与通道拼接函数------垂直拼接两个图像/矩阵的函数concatVert()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 对给定的矩阵执行垂直拼接。该函数将两个 GMat 矩阵&#xff08;列数相同&#xff09;垂直连接&#xff1a; GMat A { 1, 7,2, 8,3, 9 }; GMat…...

重测序关系矩阵构建方式汇总

样本间亲缘关系矩阵&#xff08;kinship matrix&#xff09;和同源性矩阵&#xff08;IBS matrix&#xff09;构建的方式 1. 可以使用plink的–make-rel计算个体之间的亲缘关系&#xff08;强调个体之间的遗传相似性&#xff09; /opt/software/plink --bfile vcf_bfile--mak…...

OpenCV 图形API(70)图像与通道拼接函数-----创建一个图像或矩阵(GMat)的副本的操作函数copy()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 制作输入图像的一个副本。请注意&#xff0c;这个副本可能不是实际存在的&#xff08;没有实际复制数据&#xff09;。使用此函数来维护图的契约…...

30天通过软考高项-第六天

30天通过软考高项-第六天 任务&#xff1a;项目质量管理 思维导图阅读 知识点集锦阅读 知识点记忆 章节习题练习 知识点练习 手写回忆ITTO 听一遍喜马拉雅关于范围的内容 质量管理 -背 1. 过程定义 龟管控 要求标准规划定&#xff0c;计划转化看过程&#xf…...

JUC中各种锁机制的应用和原理及死锁问题定位

JUC中各种锁机制的应用和原理及死锁问题定位 在互联网大厂Java求职者的面试中&#xff0c;经常会被问到关于JUC&#xff08;Java Util Concurrency&#xff09;中的各种锁机制及其应用和原理的问题。本文通过一个故事场景来展示这些问题的实际解决方案。 第一轮提问 面试官&…...

区块链vs实体经济:一场金融、医疗、政务与物流的“效率革命”

区块链技术作为一种去中心化、不可篡改的分布式账本技术&#xff0c;正在重塑多个行业的运行模式。从金融交易的透明化到医疗数据的安全共享&#xff0c;从政务服务的效率提升到物流供应链的全程可追溯&#xff0c;区块链的跨行业应用展现出巨大的潜力与价值。以下是其在金融、…...

FTP-网络文件服务器

部署思路 单纯上传下载ftp系统集成间的共享 samba网络存储服务器 NFS 网络文件服务器&#xff1a;通过网络共享文件或文件夹&#xff0c;实现数据共享 NAS &#xff08; network append storage):共享的是文件夹 FTP&#xff1a;文件服务器samba&#xff1a;不同系统间的文件…...

嵌入式RTOS实战:uC/OS-III最新版移植指南(附项目源码)

文章目录 前言一、uC/OS简介二、工程移植2.1 下载ucos源码2.2 创建空白工程2.3 拷贝ucosiii源码文件2.3.1 UC-CONFIG2.3.2 UC-CPU2.3.3 UC-LIB2.3.4 UC-OS3 2.3 添加工程文件分组及路径2.4 代码首次编译2.5 源码修改2.5.1 cpu_cfg.h2.5.2 os_cpu_c.c2.5.3 lib_cfg.h2.5.4 sys.h…...

10.Excel:快速定位目标值

一 批量删除 1.如何使用 快捷键 CTRLG 补充&#xff1a;直接选择定位条件。 2.作用 1.批量删除工作表中的图片 补充&#xff1a;无法通过框选的方式选中这些图片进行删除。 这样只框选了表格&#xff0c;无法框选图片。因为图片在excel中被认为是一个对象&#xff0c;对象无法通…...

状态模式 (State Pattern)

状态模式(State Pattern)是一种行为型设计模式,它允许对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。该模式将状态封装成独立的类,并将请求委托给当前的状态对象,当对象的内部状态发生变化时,其行为也会随之改变。 一、基础部分 1. 意图 允许一个…...

【Java面试题04】MySQL 篇

文章目录 一、前言&#x1f680;&#x1f680;&#x1f680;二、MySQL 篇&#xff1a;☀️☀️☀️1、MySQL 是如何实现事务的? 后序还在更新中~~~三、总结&#xff1a;&#x1f353;&#x1f353;&#x1f353; 一、前言&#x1f680;&#x1f680;&#x1f680; ☀️ 你每一…...

同时安装多个版本的golang

https://golang.google.cn/dl/ go install golang.org/dl/go1.20latest 这样就会将 go1.20.exe 下载到 GOPATH/bin&#xff0c;但是此时并没有 go1.20 的源码包&#xff0c;也就不能正常执行 build/run 等指令。 然后执行 go1.20 download下载源码包 > go1.20 download …...

【Web应用服务器_Tomcat】三、Tomcat 性能优化与监控诊断

在企业级 Java Web 应用的运行过程中&#xff0c;Apache Tomcat 作为广泛使用的 Servlet 容器和 Web 服务器&#xff0c;其性能表现直接影响用户体验和业务稳定性。本篇文章将深入探讨 Tomcat 性能优化的实用技巧&#xff0c;以及如何通过有效的监控诊断手段&#xff0c;及时发…...

stm32week13

stm32学习 九.stm32与HAL库 4.时钟树 stm32f103所拥有的时钟源&#xff1a; 外部时钟的稳定性比内部的高&#xff0c;但是成本高&#xff0c;需要在外部额外接 关于上述时钟树的简图&#xff1a; 右下四个是HAL库中的初始化函数 F4的时钟树简图&#xff1a; F7的时钟树简图…...

深入探究C++ 中的stack、queue和deque

目录 一、stack&#xff08;栈&#xff09; 二、queue&#xff08;队列&#xff09; 三、deque&#xff08;双向队列&#xff09; 四、容器适配器总结 在C 的标准模板库&#xff08;STL&#xff09;中&#xff0c;stack、queue和priority_queue是非常实用的容器适配器&…...

第十二节:性能优化高频题-shallowRef/shallowReactive使用场景

适用场景&#xff1a;大型对象/列表仅需第一层响应式变化&#xff08;如JSON配置数据&#xff09; Vue3 浅层响应式 API&#xff08;shallowRef/shallowReactive&#xff09;使用场景深度解析 一、核心使用场景与性能优化原理 大型 JSON 配置数据管理 • 场景特征&#xff1a;…...

openGauss DB4AI与scikit-learn模块对比探究

openGauss当前版本支持了原生DB4AI能力&#xff0c;引入原生AI算子&#xff0c;简化操作流程&#xff0c;充分利用数据库优化器、执行器的优化与执行能力&#xff0c;获得高性能的数据库内模型训练能力。 本文介绍了笔者采用鸢尾花数据集&#xff0c;对openGauss DB4AI功能进行…...

基于大模型的公安预审办案笔录分析的挑战与应对策略-3

引言 &#xff1a;在基于大模型的公安预审办案笔录分析应用过程中&#xff0c;虽然取得了一定的成果&#xff0c;但也面临着诸多挑战。本文将分析这些挑战&#xff0c;并提出相应的应对策略&#xff0c;以推动该技术在公安领域的更好地发展和应用。 引文&#xff1a;https://c…...

ubantu18.04(Hadoop3.1.3)之Flink安装与编程实践(Flink1.9.1)

说明&#xff1a;本文图片较多&#xff0c;耐心等待加载。&#xff08;建议用电脑&#xff09; 注意所有打开的文件都要记得保存。 第一步&#xff1a;准备工作 本文是在之前Hadoop搭建完集群环境后继续进行的&#xff0c;因此需要读者完成我之前教程的所有操作。 注意本次实…...

AI辅助编程-cursor开发煤矿持证上岗管理程序需求与设计篇

​ Cursor 是一款由人工智能驱动的智能代码编辑器&#xff0c;深度融合AI技术以提升开发效率。其核心功能基于GPT-4等先进模型&#xff0c;支持代码生成、错误修复、智能补全及自然语言编程。开发者可通过对话交互直接描述需求&#xff0c;AI即时生成对应代码片段&#xff0c;显…...

如何使用极狐GitLab 议题看板?

极狐GitLab 是 GitLab 在中国的发行版&#xff0c;关于中文参考文档和资料有&#xff1a; 极狐GitLab 中文文档极狐GitLab 中文论坛极狐GitLab 官网 议题看板 (BASIC ALL) 议题看板是一个软件项目管理工具&#xff0c;用于计划、组织和可视化功能或产品发布的工作流程。它可…...

计网分层体系结构(包括OSI,IP,两者对比和相关概念)

1. 应用层&#xff1a; 用户与网络的界面&#xff0c;FTP&#xff0c;SMTP, HTTP 2. 表示层(Presentation Layer)&#xff1a; 解决用户信息的语法表示问题 数据压缩&#xff0c;加密解密 表示变换 3. 对话层(Session Layer)&#xff1a; 功能&#xff1a;允许不同主机的各个进…...

爬虫过程中如何确保数据准确性

在爬虫过程中&#xff0c;确保数据的准确性是非常重要的。数据不准确可能会导致分析结果的偏差&#xff0c;甚至影响决策。以下是一些确保爬虫数据准确性的方法和技巧&#xff1a; 一、验证数据来源 确保数据来源的可靠性是确保数据准确性的第一步。选择信誉良好的网站作为数…...

Maven多模块工程版本管理:flatten-maven-plugin扁平化POM

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;精通Java编…...

数据库基础与核心操作:从概念到实战的全面解析

目录 1 基本概念2 基本操作2.1 DCL2.2 DDL2.3 DML2.4 DQL(高级查询) 3 高级功能3.1 视图&#xff08;无参函数&#xff09;3.2 存储过程(有参函数)3.3 触发器 4 约束4.1 主键约束4.2 UNIQUE KEY&#xff08;唯一键约束&#xff09;4.3 FOREIGN KEY&#xff08;外键约束&#xf…...

网络原理 - 10(HTTP/HTTPS - 1)

前面的网络原理 1 - 9&#xff0c;按照 TCP/IP 五层协议栈&#xff0c;介绍了各个层次的核心协议。 应用层&#xff1a;自定义协议&#xff08;xml&#xff0c;json....&#xff09; 传输层&#xff1a;UDP/TCP 网络层&#xff1a;IP 数据链路层&#xff1a;以太网 我们这…...

UDP协议详解+代码演示

1、UDP协议基础 1. UDP是什么&#xff1f; UDP&#xff08;User Datagram Protocol&#xff0c;用户数据报协议&#xff09;是传输层的核心协议之一&#xff0c;与TCP并列。它的主要特点是&#xff1a;​​​​ 无连接&#xff1a;通信前不需要建立连接&#xff08;知道对端的…...

QT事件Trick

拖动 void DWidget::mousePressEvent(QMouseEvent *event) {if(event->button()Qt::LeftButton){QListWidgetItem *selItem currentItem();if(selItem! nullptr){m_startPosevent->pos(); //记录鼠标按下时的起始位置}}QListWidget::mousePressEvent(event); }void DW…...

解答UnityShader学习过程中的一些疑惑(持续更新中)

一、坐标系相关 shader中会有几种空间&#xff1a; 模型空间&#xff1a;以物体自己为中心原点 世界空间&#xff1a;就是unity的世界坐标 观察空间&#xff08;视图空间&#xff09;&#xff1a;以相机为中心的坐标系 裁剪空间&#xff1a;是一个4d空间&#xff0c;有x,y,z,w…...