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

Flutter与iOS和Android原生页面交互

一、Flutter 与原生页面交互的重要性和应用场景

Flutter 是一个由 Google 开发的开源框架,用于创建跨平台的移动、Web 和桌面应用程序。Flutter 允许开发者使用一套代码库为 Android 和 iOS 等平台构建美观、高性能的应用程序。然而,尽管 Flutter 提供了丰富的组件和库,某些情况下,开发者可能仍需要使用平台特有的功能或集成现有的原生页面和服务。

在以下场景中,Flutter 与原生页面交互显得尤为重要:

  1. 利用现有原生代码:在项目中可能已经存在大量的原生代码,重新用 Flutter 实现可能不现实或成本过高。
  2. 调用平台特有的API:一些平台特有的API(如支付、地图等)只能通过原生代码访问。
  3. 集成第三方原生库:一些第三方库只提供原生的接口,需要通过原生代码来集成。
  4. 性能关键任务:对于一些性能敏感的任务,原生代码往往可以提供更好的性能表现。
  5. 遵循平台设计规范:有时为了保持应用的一致性,需要使用原生组件来遵守特定平台的设计规范。

通过与原生页面的交互,Flutter 开发者可以充分利用平台的能力,同时保持应用的流畅性和用户体验的一致性。

二、基本概念

1.平台通道(Platform Channels)介绍

平台通道是Flutter中一个强大的机制,它允许Flutter与宿主平台(如Android和iOS)进行数据和任务的双向通信。这种机制解决了Flutter无法直接调用平台特定API的限制,使得Flutter应用能够实现原生平台的功能。

(1).通道的工作原理

平台通道通过使用一个消息传递的系统工作,该系统包括以下三个关键概念:

  1. 消息:传递的数据单元,可以是简单的字符串、数字或者更复杂的序列化数据结构。
  2. 编解码器:负责消息的序列化与反序列化。Flutter提供了几种标准的编解码器,例如JSONMessageCodecStandardMessageCodecStringCodecBinaryCodec
  3. 通道:连接Flutter和原生平台代码的通信通道。

(2).通道的种类

Flutter提供了三种类型的平台通道,每种通道都适合不同的应用场景:

MethodChannel用于传递方法调用及其响应。方法调用是单次的,通常用于执行原生操作并获取结果。

EventChannel用于创建一个数据流,它允许原生代码发送连续的事件数据给Flutter。这适合于监听原生端的事件或数据变化,如传感器数据或用户位置更新。

BasicMessageChannel用于传递没有固定响应的消息,支持自定义的编解码器。这适合于双向通信,比如状态同步或数据共享。

(3).序列化机制

由于消息需要在Flutter和原生代码之间传递,所以它们需要被序列化(转换为字节序列)和反序列化。Flutter框架提供了几个内置的编解码器来处理常见的数据类型,开发者也可以自定义编解码器。

(4).平台通道的使用

使用平台通道时,通常需要在Flutter端和原生端同时进行代码编写。

以下是一个简单的MethodChannel使用示例:

Flutter端
import 'package:flutter/services.dart';class BatteryLevel {static const MethodChannel _channel =MethodChannel('com.example.battery');static Future<int> getBatteryLevel() async {final int batteryLevel = await _channel.invokeMethod('getBatteryLevel');return batteryLevel;}
}
Android端(Kotlin)
class MainActivity: FlutterActivity() {private val CHANNEL = "com.example.battery"override fun configureFlutterEngine(flutterEngine: FlutterEngine) {super.configureFlutterEngine(flutterEngine)MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler {call, result ->if (call.method == "getBatteryLevel") {val batteryLevel = getBatteryLevel()if (batteryLevel != -1) {result.success(batteryLevel)} else {result.error("UNAVAILABLE", "Battery level not available.", null)}} else {result.notImplemented()}}}private fun getBatteryLevel(): Int {val batteryLevel: Intif (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {val batteryManager = getSystemService(Context.BATTERY_SERVICE) as BatteryManagerbatteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)} else {val intent = ContextWrapper(applicationContext).registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED))batteryLevel = intent!!.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100 / intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1)}return batteryLevel}
}
iOS端(Swift)
import Flutter
import UIKit@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {private let batteryChannel = "com.example.battery"override func application(_ application: UIApplication,didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {GeneratedPluginRegistrant.register(with: self)let controller: FlutterViewController = window?.rootViewController as! FlutterViewControllerlet methodChannel = FlutterMethodChannel(name: batteryChannel,binaryMessenger: controller.binaryMessenger)methodChannel.setMethodCallHandler({[weak self] (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in// Handle battery messages.guard call.method == "getBatteryLevel" else {result(FlutterMethodNotImplemented)return}self?.receiveBatteryLevel(result: result)})return super.application(application, didFinishLaunchingWithOptions: launchOptions)}private func receiveBatteryLevel(result: FlutterResult) {let device = UIDevice.currentdevice.isBatteryMonitoringEnabled = trueif device.batteryState == UIDevice.BatteryState.unknown {result(FlutterError(code: "UNAVAILABLE",message: "Battery level not available.",details: nil))} else {result(Int(device.batteryLevel * 100))}}
}

2.Flutter 平台通道比较及示例

在Flutter中,MethodChannelEventChannelBasicMessageChannel是三种不同的平台通道,用于实现Dart代码和原生平台代码之间的通信。

MethodChannel

MethodChannel通常用于发送方法调用请求,并接收单次响应。它适合于执行原生操作并获取结果的场景。

Flutter端
import 'package:flutter/services.dart';// 创建MethodChannel实例
const MethodChannel methodChannel = MethodChannel('com.example.channel/method');// 调用原生方法并获取电池电量
Future<int> getBatteryLevel() async {try {// 调用原生平台的'getBatteryLevel'方法final int result = await methodChannel.invokeMethod('getBatteryLevel');return result;} on PlatformException catch (e) {// 处理异常情况return -1;}
}
Android端(Kotlin)
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannelclass MainActivity: FlutterActivity() {// 定义通道名称private val CHANNEL = "com.example.channel/method"override fun configureFlutterEngine(flutterEngine: FlutterEngine) {super.configureFlutterEngine(flutterEngine)// 创建MethodChannel实例并设置方法调用处理器MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->if (call.method == "getBatteryLevel") {// 执行获取电池电量的操作,并将结果返回给Flutter端val batteryLevel = getBatteryLevel()if (batteryLevel != -1) {result.success(batteryLevel)} else {result.error("UNAVAILABLE", "Battery level not available.", null)}} else {result.notImplemented()}}}// 模拟获取电池电量的函数private fun getBatteryLevel(): Int {// 这里只是一个示例,实际获取电池电量的方法与此不同return 100 // 假设电池电量是100%}
}
iOS端(Swift)
import Flutter
import UIKit@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {// 定义通道名称private let methodChannelName = "com.example.channel/method"override func application(_ application: UIApplication,didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {GeneratedPluginRegistrant.register(with: self)guard let controller = window?.rootViewController as? FlutterViewController else {fatalError("rootViewController is not type FlutterViewController")}// 创建MethodChannel实例并设置方法调用处理器let methodChannel = FlutterMethodChannel(name: methodChannelName,binaryMessenger: controller.binaryMessenger)methodChannel.setMethodCallHandler({[weak self] (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in// 检查方法名并执行相应操作if call.method == "getBatteryLevel" {// 执行获取电池电量的操作,并将结果返回给Flutter端self?.receiveBatteryLevel(result: result)} else {result(FlutterMethodNotImplemented)}})return super.application(application, didFinishLaunchingWithOptions: launchOptions)}private func receiveBatteryLevel(result: FlutterResult) {// 这里只是一个示例,实际获取电池电量的方法与此不同result(100) // 假设电池电量是100%}
}

EventChannel

EventChannel用于数据流(event streams),可以用来监听原生端发出的事件。例如,原生端的传感器数据或数据库更新。

Flutter端
import 'package:flutter/services.dart';// 创建EventChannel实例
const EventChannel eventChannel = EventChannel('com.example.channel/stream');// 监听来自原生平台的事件流
void listenToNativeEvents() {eventChannel.receiveBroadcastStream().listen((event) {// 处理原生平台发送的数据print('Received event: $event');}, onError: (error) {// 处理错误print('Received error: ${error.message}');});
}
Android端(Kotlin)
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.EventChannel
import java.util.*class MainActivity : FlutterActivity() {// 定义通道名称private val CHANNEL = "com.example.channel/stream"// 模拟传感器数据流private val sensorDataStream = Timer()override fun configureFlutterEngine(flutterEngine: FlutterEngine) {super.configureFlutterEngine(flutterEngine)// 创建EventChannel实例并设置事件流处理器EventChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setStreamHandler(object : EventChannel.StreamHandler {override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {// 设置定时器,模拟传送传感器数据sensorDataStream.schedule(object : TimerTask() {override fun run() {// 模拟事件数据,这里发送当前时间戳events?.success(System.currentTimeMillis())}}, 0, 1000)}override fun onCancel(arguments: Any?) {// 取消事件流sensorDataStream.cancel()}})}
}
iOS端(Swift)
import Flutter
import UIKitclass StreamHandler: NSObject, FlutterStreamHandler {private var eventSink: FlutterEventSink?private var timer: Timer?func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {eventSink = events// 设置定时器,模拟传送传感器数据timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in// 模拟事件数据,这里发送当前时间戳events(Date().timeIntervalSince1970 * 1000)}return nil}func onCancel(withArguments arguments: Any?) -> FlutterError? {// 取消定时器timer?.invalidate()timer = nileventSink = nilreturn nil}
}@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {// 定义通道名称private let streamChannelName = "com.example.channel/stream"override func application(_ application: UIApplication,didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {GeneratedPluginRegistrant.register(with: self)let controller: FlutterViewController = window?.rootViewController as! FlutterViewController// 创建EventChannel实例并设置事件流处理器let eventChannel = FlutterEventChannel(name: streamChannelName, binaryMessenger: controller.binaryMessenger)let streamHandler = StreamHandler()eventChannel.setStreamHandler(streamHandler)return super.application(application, didFinishLaunchingWithOptions: launchOptions)}
}

BasicMessageChannel

BasicMessageChannel 允许发送和接收字符串和半结构化信息,适用于自定义编码和协议,或者需要频繁通信但数据量不大的场景。

Flutter端
import 'package:flutter/services.dart';// 创建BasicMessageChannel实例
BasicMessageChannel<String> basicMessageChannel =BasicMessageChannel<String>('com.example.channel/basic', StringCodec());// 向原生发送消息
Future<void> sendMessage(String message) async {String response = await basicMessageChannel.send(message);print('Received response: $response');
}// 接收来自原生的消息
void receiveMessages() {basicMessageChannel.setMessageHandler((String message) async {print('Received message: $message');return 'Received!';});
}
Android端(Kotlin)
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.BasicMessageChannel
import io.flutter.plugin.common.StringCodecclass MainActivity: FlutterActivity() {private val CHANNEL = "com.example.channel/basic"override fun configureFlutterEngine(flutterEngine: FlutterEngine) {super.configureFlutterEngine(flutterEngine)// 创建BasicMessageChannel实例val messageChannel = BasicMessageChannel<String>(flutterEngine.dartExecutor.binaryMessenger, CHANNEL, StringCodec.INSTANCE)// 设置消息接收器messageChannel.setMessageHandler { message, reply ->// 在这里处理从Flutter接收到的消息println("Received message: $message")// 回复消息给Flutter端reply.reply("Echo: $message")}}
}
iOS端(Swift)
import Flutter
import UIKit@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {private let basicMessageChannelName = "com.example.channel/basic"override func application(_ application: UIApplication,didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {GeneratedPluginRegistrant.register(with: self)guard let controller = window?.rootViewController as? FlutterViewController else {fatalError("rootViewController is not type FlutterViewController")}// 创建BasicMessageChannel实例let messageChannel = FlutterBasicMessageChannel(name: basicMessageChannelName,binaryMessenger: controller.binaryMessenger,codec: FlutterStringCodec.sharedInstance())// 设置消息接收器messageChannel.setMessageHandler { (message: Any?, reply: FlutterReply) in// 在这里处理从Flutter接收到的消息if let messageStr = message as? String {print("Received message: \(messageStr)")// 回复消息给Flutter端reply("Echo: \(messageStr)")}}return super.application(application, didFinishLaunchingWithOptions: launchOptions)}
}

3.异步编程在平台通道中的应用

在Flutter中,平台通道的调用通常是异步的,这意味着你可以使用asyncawait关键字来等待原生代码的执行结果而不会阻塞UI线程。

例如,使用MethodChannel请求电池电量时,你通常会这样做:

Future<void> _getBatteryLevel() async {String batteryLevel;try {final int result = await NativeBridge.getBatteryLevel();batteryLevel = 'Battery level at $result % .';} on PlatformException {batteryLevel = 'Failed to get battery level.';}print(batteryLevel);
}

三、设置平台通道

1.定义通道名

我们将设置一个名为 "com.example.channel/method" 的 MethodChannel 平台通道,这个通道将用于Flutter与原生平台(iOS和Android)之间的通信。

2.在 Flutter端创建MethodChannel

在Flutter端,你需要创建一个 MethodChannel 实例,并通过它发送消息到原生平台。

// 导入相关包
import 'package:flutter/services.dart';// 定义通道名
const platformChannel = MethodChannel('com.example.channel/method');// 调用原生方法
Future<void> getNativeData() async {try {final String result = await platformChannel.invokeMethod('getNativeData');print('从原生平台获取的数据:$result');} on PlatformException catch (e) {print("调用原生平台方法失败:${e.message}");}
}

在上面的代码中,我们定义了一个方法通道名 "com.example.channel/method",并创建了 MethodChannel 实例。我们定义了一个名为 getNativeData 的函数,该函数调用原生平台的 'getNativeData' 方法,并处理来自原生平台的响应或异常。

3.在Android(Kotlin)端监听通道

在Android端,我们需要在 MainActivity 中设置 MethodChannel 并定义方法调用的处理逻辑。

// 导入相关包
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandlerclass MainActivity: FlutterActivity() {private val CHANNEL = "com.example.channel/method"override fun configureFlutterEngine(flutterEngine: FlutterEngine) {super.configureFlutterEngine(flutterEngine)// 设置MethodChannel并监听来自Flutter的调用MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->// 判断调用的方法名if (call.method == "getNativeData") {// 执行获取数据的逻辑val nativeData = "来自Android的数据"// 使用result对象发送结果回Flutterresult.success(nativeData)} else {// 如果方法未实现,返回未实现的错误result.notImplemented()}}}
}

在这段代码中,我们在 MainActivity 类中创建了一个 MethodChannel 实例,然后设置了一个 MethodCallHandler 来监听来自Flutter的方法调用。当Flutter调用 'getNativeData' 方法时,我们返回一些模拟的数据给Flutter。

3.在iOS(Swift)端监听通道

在iOS端,我们在 AppDelegate 中设置 MethodChannel 并处理方法调用。

import UIKit
import Flutter@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {// 定义通道名 与Flutter端定义的通道名称保持一致private let channelName = "com.example.channel/method"override func application(_ application: UIApplication,didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {GeneratedPluginRegistrant.register(with: self)guard let controller = window?.rootViewController as? FlutterViewController else {fatalError("rootViewController is not type FlutterViewController")}let channel = MethodChannel(name: channelName, binaryMessenger: controller.binaryMessenger)// 设置监听器以处理来自Flutter的方法调用channel.setMethodCallHandler {(call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in// 检查方法名以确定执行哪个原生方法if call.method == "getNativeData" {// 执行获取数据逻辑let nativeData = "来自iOS的数据"// 发送结果回Flutterresult(nativeData)} else {// 如果方法未实现,返回未实现的错误result(FlutterMethodNotImplemented)}}return super.application(application, didFinishLaunchingWithOptions: launchOptions)}
}

在这段Swift代码中,我们在 AppDelegate 类中同样定义了一个 MethodChannel,并设置了一个闭包作为 MethodCallHandler 来监听Flutter的方法调用。当Flutter请求 'getNativeData' 方法时,我们返回一串模拟的数据。

四、跳转到原生页面

为了实现Flutter跳转到原生页面的功能,我们需要在三个平台上编写相应的代码:Flutter、iOS和Android。

Flutter端

在Flutter端,我们使用MethodChannel来发起跳转到原生页面的请求。

// 导入必要的包
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';void main() => runApp(MyApp());class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return MaterialApp(home: HomeScreen(),);}
}class HomeScreen extends StatelessWidget {// 创建MethodChannel实例,通道名称要与原生端一致static const platformChannel = MethodChannel('com.example.channel/native');// 调用原生方法跳转到原生页面的函数Future<void> openNativeScreen() async {try {// 调用原生平台的openNativeScreen方法await platformChannel.invokeMethod('openNativeScreen');} on PlatformException catch (e) {// 如果跳转失败,捕获异常print("Failed to open native screen: ${e.message}");}}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text('Flutter Home Screen'),),body: Center(child: ElevatedButton(onPressed: openNativeScreen, // 点击按钮时发起跳转child: Text('Go to Native Screen'),),),);}
}

在上面的Flutter代码中,我们定义了一个按钮,当用户点击这个按钮时,openNativeScreen函数会通过MethodChannel发起一个名为openNativeScreen的方法调用。

Android端(Kotlin)

在Android端,在MainActivity中监听MethodChannel,并对openNativeScreen方法调用进行处理,启动一个新的Activity

// MainActivity.kt
import android.content.Intent
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannelclass MainActivity: FlutterActivity() {private val CHANNEL = "com.example.channel/native"override fun configureFlutterEngine(flutterEngine: FlutterEngine) {super.configureFlutterEngine(flutterEngine)MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->if (call.method == "openNativeScreen") {// 创建Intent并启动新的Activityval intent = Intent(this, NativeActivity::class.java)startActivity(intent)// 返回成功结果result.success(null)} else {result.notImplemented()}}}
}
// NativeActivity.kt
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity// 创建新的Activity用于展示原生页面
class NativeActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)// 设置原生页面的布局setContentView(R.layout.activity_native)}
}

在Android项目中,我们还需要在AndroidManifest.xml中注册NativeActivity

<activity android:name=".NativeActivity"><!-- 配置和其他Activity相关的属性 -->
</activity>

iOS端(Swift)

在iOS端,在AppDelegate中监听MethodChannel,并对openNativeScreen方法调用进行处理,启动一个新的UIViewController

import UIKit
import Flutter@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {private let channelName = "com.example.channel/native"override func application(_ application: UIApplication,didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {GeneratedPluginRegistrant.register(with: self)if let controller = window?.rootViewController as? FlutterViewController {let channel = FlutterMethodChannel(name: channelName, binaryMessenger: controller.binaryMessenger)channel.setMethodCallHandler { (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void inif call.method == "openNativeScreen" {// 实现UIViewController的跳转逻辑self.openNativeScreen(from: controller)result(nil)} else {result(FlutterMethodNotImplemented)}}}return super.application(application, didFinishLaunchingWithOptions: launchOptions)}private func openNativeScreen(from flutterViewController: FlutterViewController) {// 创建原生页面的UIViewController
let nativeViewController = NativeViewController()// 从当前FlutterViewController进行页面跳转flutterViewController.present(nativeViewController, animated: true, completion: nil)}
}
// NativeViewController.swift
import UIKit// 创建一个新的UIViewController子类作为原生页面
class NativeViewController: UIViewController {override func viewDidLoad() {super.viewDidLoad()// 设置原生页面的属性,例如背景色view.backgroundColor = UIColor.white}
}

五、从 Flutter 传值给原生页面并得到响应

为了实现从Flutter应用传递数据到原生页面,并在原生页面中接收和解析这些数据,我们需要分别在Flutter、Android和iOS端编写代码。

Flutter端发送数据

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'dart:convert';void main() {runApp(MyApp());
}class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return MaterialApp(home: HomeScreen(),);}
}class HomeScreen extends StatelessWidget {// 创建MethodChannel实例,通道名称要与原生端一致static const platformChannel = MethodChannel('com.example.channel/transfer');// 向原生页面发送简单数据Future<void> sendSimpleData() async {try {final String response = await platformChannel.invokeMethod('sendSimpleData', {'message': 'Hello from Flutter!'});print(response); // 打印原生页面返回的响应} on PlatformException catch (e) {print("Failed to send simple data: ${e.message}");}}// 向原生页面发送复杂数据(如JSON)Future<void> sendComplexData() async {try {final Map<String, dynamic> complexData = {'user': {'id': 1,'name': 'John Doe','email': 'johndoe@example.com',}};final String response = await platformChannel.invokeMethod('sendComplexData', {'data': json.encode(complexData)});print(response); // 打印原生页面返回的响应} on PlatformException catch (e) {print("Failed to send complex data: ${e.message}");}}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text('Flutter to Native Data Transfer'),),body: Center(child: Column(mainAxisAlignment: MainAxisAlignment.center,children: <Widget>[ElevatedButton(onPressed: sendSimpleData,child: Text('Send Simple Data'),),ElevatedButton(onPressed: sendComplexData,child: Text('Send Complex Data'),),],),),);}
}

在Flutter端,我们创建了两个函数sendSimpleDatasendComplexData,用于发送简单和复杂数据。数据通过MethodChannel的invokeMethod函数发送到原生端,同时可以从原生端接收响应。

Android端(Kotlin)

在Android端,我们监听MethodChannel,并使用Gson库来解析JSON格式的复杂数据。

// MainActivity.kt
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import com.google.gson.Gson
import com.google.gson.reflect.TypeTokenclass MainActivity: FlutterActivity() {private val CHANNEL = "com.example.channel/transfer"override fun configureFlutterEngine(flutterEngine: FlutterEngine) {super.configureFlutterEngine(flutterEngine)MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->when (call.method) {"sendSimpleData" -> {val message = call.argument<String>("message")// 使用传递的简单数据// ...// 发送响应回Flutter端result.success("Received simple data: $message")}"sendComplexData" -> {val data = call.argument<String>("data")val type = object : TypeToken<Map<String, Any>>() {}.typeval complexData: Map<String, Any> = Gson().fromJson(data, type)// 使用传递的复杂数据// ...// 发送响应回Flutter端result.success("Received complex data")}else -> result.notImplemented()}}}
}

iOS端(Swift)

在iOS端,我们监听MethodChannel,并使用Swift的Codable协议或JSONSerialization来解析JSON格式的复杂数据。

// AppDelegate.swift
import UIKit
import Flutter@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {private let channelName = "com.example.channel/transfer"override func application(_ application: UIApplication,didFinishLaunchingWithOptions launchOptions:[UIApplication.LaunchOptionsKey: Any]?) -> Bool {GeneratedPluginRegistrant.register(with: self)guard let controller = window?.rootViewController as? FlutterViewController else {fatalError("rootViewController is not type FlutterViewController")}let channel = FlutterMethodChannel(name: channelName,binaryMessenger: controller.binaryMessenger)channel.setMethodCallHandler { [weak self] (call, result) inswitch call.method {case "sendSimpleData":if let message = call.arguments as? String {// 使用传递的简单数据// ...// 发送响应回Flutter端result("Received simple data: \(message)")} else {result(FlutterError(code: "INVALID_ARGUMENT", message: "Expected a string argument", details: nil))}case "sendComplexData":if let jsonString = call.arguments as? String,let data = jsonString.data(using: .utf8) {do {// 使用Codable进行解析let user = try JSONDecoder().decode(User.self, from: data)// 使用传递的复杂数据// ...// 发送响应回Flutter端result("Received complex data")} catch {result(FlutterError(code: "JSON_PARSE_ERROR", message: "Error parsing JSON", details: nil))}} else {result(FlutterError(code: "INVALID_ARGUMENT", message: "Expected a JSON string argument", details: nil))}default:result(FlutterMethodNotImplemented)}}return super.application(application, didFinishLaunchingWithOptions: launchOptions)}
}// 定义User结构体以匹配传递的复杂数据结构
struct User: Codable {var id: Intvar name: Stringvar email: String
}

六、从原生页面回传数据到 Flutter

当需要从原生页面回传数据到Flutter时,有两种常见的方法:使用MethodChannel或者使用ActivityResult(Android端)/使用委托(delegate)/闭包(closures)(iOS端)。

  • 使用MethodChannel回传数据:此方法适用于任何时候原生代码需要主动发送数据到Flutter端的情况,不仅限于页面返回时。Flutter端通过MethodChannel与原生代码通信,可以接收来自原生端的数据。

  • 使用ActivityResult(Android)/闭包和协议委托(iOS):此方法一般用于原生页面关闭时,将数据回传到Flutter端。在Android上,可以通过setResult方法和Intent返回数据给前一个Activity。在iOS上,可以通过代理模式或闭包将数据回传给之前的控制器。

在Flutter端,我们需要设置好MethodChannel监听原生端发来的数据,或者在启动原生页面时等待结果。

方式一:远程过程调用

Flutter端设置了MethodChannel监听并处理原生端的调用,当原生代码处理完毕后,通过相同的MethodChannel返回结果。这是一种典型的RPC(远程过程调用)模式。

  • 单次请求-响应模式:Flutter发起调用,原生端返回数据,通信完成。
  • Flutter端主动请求:Flutter通过invokeMethod主动请求原生数据。
  • 异步等待原生端响应:Flutter调用后使用await关键字等待原生端完成操作并返回结果。
  • 适用场景:当Flutter需要原生端某个特定操作的结果时使用,比如获取设备信息、处理完毕的数据等。

Flutter端接收返回数据

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';class HomeScreen extends StatefulWidget {@override_HomeScreenState createState() => _HomeScreenState();
}class _HomeScreenState extends State<HomeScreen> {// 创建一个MethodChannel,用于与原生平台通信static const platformChannel = MethodChannel('com.example.channel/transfer');// 用于显示从原生平台接收到的数据String _dataFromNative = 'No data';@overridevoid initState() {super.initState();// 在 initState 中设置监听器以处理原生平台发来的方法调用platformChannel.setMethodCallHandler(_handleMethodCall);}// 处理从原生平台接收到的方法调用Future<dynamic> _handleMethodCall(MethodCall call) async {switch (call.method) {case 'onDataReturn':// 当原生平台返回数据时,更新状态以显示数据setState(() {_dataFromNative = call.arguments;});break;default:throw MissingPluginException('notImplemented');}}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text('Flutter Native Data Return'),),body: Center(// 显示从原生端接收到的数据child: Text(_dataFromNative),),);}
}

Android端(Kotlin)设置返回数据

在Android端,你可以在MainActivity中创建一个按钮,并在点击事件中使用MethodChannel向Flutter发送消息:

// ...其他导入
import io.flutter.embedding.android.FlutterActivity
import io.flutter.plugin.common.MethodChannelclass MainActivity: FlutterActivity() {// 与Flutter端相同的通道名称private val CHANNEL = "com.example.channel/transfer"override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)// 初始化MethodChannelval channel = MethodChannel(flutterEngine!!.dartExecutor.binaryMessenger, CHANNEL)// ...其他代码// 用于向Flutter发送数据fun sendDataToFlutter() {// 使用MethodChannel调用Flutter端定义的方法,并传递数据channel.invokeMethod("onDataReturn", "这是来自原生平台的数据")}// 假设有个触发发送数据到Flutter的逻辑sendDataToFlutter()}
}

iOS端(Swift)设置返回数据

在iOS端,你可以在ViewController中为一个按钮,并在这个按钮回调中使用MethodChannel向Flutter发送消息:

// ...其他导入
import Flutterclass ViewController: UIViewController {private var channel: FlutterMethodChannel?override func viewDidLoad() {super.viewDidLoad()// 初始化MethodChannelguard let controller = self as? FlutterViewController else {fatalError("ViewController is not type FlutterViewController")}channel = FlutterMethodChannel(name: "com.example.channel/transfer", binaryMessenger: controller.binaryMessenger)// ...其他代码// 用于向Flutter发送数据func sendDataToFlutter() {// 使用MethodChannel调用Flutter端定义的方法,并传递数据channel?.invokeMethod("onDataReturn", arguments: "这是来自原生平台的数据")}// 假设有个触发发送数据到Flutter的逻辑sendDataToFlutter()}
}

方式二:事件订阅模式

是一种事件订阅模式,Flutter端通过MethodChannel设置监听器,任何时候原生端都可以主动调用这个通道并发送数据到Flutter,而Flutter端则在_listener_方法中处理所有接收到的消息。

  • 持续监听模式:Flutter监听原生端的方法调用,原生端可以在任何时刻主动发送消息。
  • 原生端主动发送消息:原生代码在适当的时候(如某事件发生后)主动调用MethodChannel向Flutter发送消息。
  • 适用场景:对于原生端事件的实时监听和处理,如位置更新、传感器数据等。

Flutter端接收返回数据

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';class HomeScreen extends StatelessWidget {// 创建MethodChannel实例static const platformChannel = MethodChannel('com.example.channel/transfer');// 调用原生页面并等待返回结果Future<void> _navigateAndDisplaySelection(BuildContext context) async {final result = await platformChannel.invokeMethod('startNativeView');// 使用返回结果更新UI或状态if (result != null) {ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('原生页面返回的数据: $result')),);}}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text('Flutter Native Data Return'),),body: Center(child: ElevatedButton(onPressed: () => _navigateAndDisplaySelection(context),child: Text('打开原生页面'),),),);}
}

在上面的Flutter端代码中,我们通过platformChannel.invokeMethod调用原生端的方法打开一个原生页面,并使用await关键字等待异步结果。当原生页面关闭并返回数据时,我们可以通过result变量接收这个数据,并通过ScaffoldMessenger显示在一个SnackBar中。

Android端(Kotlin)设置返回数据

在Android端,我们可以使用setResult方法和Intent回传数据。

// 原生页面Activity
import android.app.Activity
import android.content.Intent
import android.os.Bundleclass NativeActivity : Activity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)// 设置布局、初始化等操作// ...// 假设这是一个按钮点击后触发的事件val button = findViewById<Button>(R.id.button_id)button.setOnClickListener {val returnIntent = Intent()returnIntent.putExtra("result_key", "这里是从原生返回的数据")setResult(Activity.RESULT_OK, returnIntent)finish() // 关闭当前原生页面,回传数据到Flutter}}
}

 在Android原生端,你需要定义一个MethodChannel并监听startNativeView方法调用,然后打开一个新的Activity,并在关闭时设置返回结果:

// MainActivity.kt (Android端)
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannelclass MainActivity: FlutterActivity() {private val CHANNEL = "com.example.channel/transfer"override fun configureFlutterEngine(flutterEngine: FlutterEngine) {super.configureFlutterEngine(flutterEngine)MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler {call, result ->if (call.method == "startNativeView") {// 调用原生页面val intent = Intent(this, NativeActivity::class.java)startActivityForResult(intent, REQUEST_CODE)} else {result.notImplemented()}}}// 接收原生页面返回的结果override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {super.onActivityResult(requestCode, resultCode, data)if (requestCode == REQUEST_CODE && resultCode == Activity.RESULT_OK) {val returnData = data?.getStringExtra("result_key") ?: "No data"// 使用MethodChannel返回数据到FlutterMethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).invokeMethod("onDataReturn", returnData)}}companion object {const val REQUEST_CODE = 100}
}

iOS端(Swift)设置返回数据

在iOS端,我们可以使用闭包或者协议委托来回传数据。

// 原生页面ViewController
import UIKitclass NativeViewController: UIViewController {// 定义闭包,用于回传数据var onReturnData: ((String) -> Void)?override func viewDidLoad() {super.viewDidLoad()// 设置布局、初始化等操作// ...let button = UIButton()button.addTarget(self, action: #selector(didTapButton), for: .touchUpInside)}@objc func didTapButton() {// 当按钮被点击时,通过闭包传递数据回Flutter端onReturnData?("这里是从原生返回的数据")dismiss(animated: true, completion: nil) // 关闭当前页面}
}

在iOS原生端,你需要在Flutter与原生端之间建立一个MethodChannel,并监听Flutter端的调用,然后打开一个新的ViewController,并在关闭时回传数据:

// AppDelegate.swift (iOS端)
import UIKit
import Flutter@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {private var flutterResult: FlutterResult?override func application(_ application: UIApplication,didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {if let controller = window?.rootViewController as? FlutterViewController {let channel = FlutterMethodChannel(name: "com.example.channel/transfer",binaryMessenger: controller.binaryMessenger)channel.setMethodCallHandler({[weak self] (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in// 保存FlutterResult回调self?.flutterResult = result// 检查是否是打开原生页面的调用if call.method == "startNativeView" {self?.showNativeView(from: controller)} else {result(FlutterMethodNotImplemented)}})}return super.application(application, didFinishLaunchingWithOptions: launchOptions)}private func showNativeView(from controller: FlutterViewController) {let nativeViewController = NativeViewController()// 设置回调闭包,以便在ViewController关闭时返回数据nativeViewController.onReturnData = { [weak self] data inself?.flutterResult?(data)}// 展示原生ViewControllercontroller.present(nativeViewController, animated: true, completion: nil)}
}

七、错误处理和异常安全

错误处理是编写健壮软件的重要组成部分。下面将详细介绍如何在Flutter、Android(Kotlin)、iOS(Swift)三个平台上进行错误处理和异常安全。

我们在Flutter端通过MethodChannel调用原生代码,并且在原生端使用try-catch(Kotlin)或do-try-catch(Swift)来捕获可能发生的异常。如果捕获到异常,原生端会通过MethodChannel.Result(Kotlin)或FlutterResult(Swift)将错误信息传递回Flutter。

异常处理机制确保了当原生代码执行出错时,Flutter端可以收到错误信息,并且可以根据错误信息做出相应的处理,从而提供更加健壮的用户体验。

Flutter端处理原生代码抛出的异常

在Flutter端,我们可以通过try-catch块来捕获和处理通过MethodChannel调用原生代码时可能抛出的异常。

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';class ExceptionHandlingScreen extends StatefulWidget {@override_ExceptionHandlingScreenState createState() => _ExceptionHandlingScreenState();
}class _ExceptionHandlingScreenState extends State<ExceptionHandlingScreen> {static const platformChannel = MethodChannel('com.example.channel/methods');String _result = 'No data';Future<void> _invokeNativeMethod(String methodName) async {try {final String result = await platformChannel.invokeMethod(methodName);setState(() {_result = result;});} on PlatformException catch (e) {// 捕获由原生代码抛出的异常setState(() {_result = "原生代码发生异常:${e.message}";});} catch (e) {// 捕获其他异常setState(() {_result = "未知异常:${e.toString()}";});}}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text('异常处理示例'),),body: Center(child: Column(mainAxisAlignment: MainAxisAlignment.center,children: <Widget>[ElevatedButton(onPressed: () => _invokeNativeMethod('someMethod'),child: Text('调用原生方法'),),Padding(padding: const EdgeInsets.all(8.0),child: Text(_result),),],),),);}
}

Android端(Kotlin)

在Android(Kotlin)端,我们可以使用try-catch块来处理可能在执行方法调用时发生的异常,并通过MethodChannel.Result向Flutter端传递错误信息。

// MainActivity.kt (Android端)
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannelclass MainActivity : FlutterActivity() {private val CHANNEL = "com.example.channel/methods"override fun configureFlutterEngine(flutterEngine: FlutterEngine) {super.configureFlutterEngine(flutterEngine)MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->try {when (call.method) {"someMethod" -> {// 你的原生方法处理result.success("从原生返回的数据")}else -> result.notImplemented()}} catch (e: Exception) {// 捕获异常并将错误返回到Flutterresult.error("ERROR", "方法执行出错: ${e.localizedMessage}", null)}}}
}

iOS端(Swift)

在iOS(Swift)端,我们一般通过do-try-catch块来处理方法调用时可能抛出的异常,并通过FlutterResult传递错误信息给Flutter端。

// AppDelegate.swift (iOS端)
import UIKit
import Flutter@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {private let channelName = "com.example.channel/methods"override func application(_ application: UIApplication,didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {guard let controller = window?.rootViewController as? FlutterViewController else {fatalError("rootViewController is not type FlutterViewController")}let channel = FlutterMethodChannel(name: channelName, binaryMessenger: controller.binaryMessenger)channel.setMethodCallHandler { (call, result) in// 检查方法名称并执行相应的原生代码if call.method == "someMethod" {do {// 尝试执行方法并捕获可能的异常let data = try self.someNativeMethod()result(data)} catch {// 如果有异常,将异常信息返回给Flutterresult(FlutterError(code: "ERROR",message: "方法执行出错: \(error.localizedDescription)",details: nil))}} else {result(FlutterMethodNotImplemented)}}return super.application(application, didFinishLaunchingWithOptions: launchOptions)}private func someNativeMethod() throws -> String {// 这里是你的原生方法实现,可能会抛出异常// 例如,我们这里直接抛出一个异常来模拟错误情况throw NSError(domain: "", code: 0, userInfo: [NSLocalizedDescriptionKey: "演示错误"])}
}

相关文章:

Flutter与iOS和Android原生页面交互

一、Flutter 与原生页面交互的重要性和应用场景 Flutter 是一个由 Google 开发的开源框架&#xff0c;用于创建跨平台的移动、Web 和桌面应用程序。Flutter 允许开发者使用一套代码库为 Android 和 iOS 等平台构建美观、高性能的应用程序。然而&#xff0c;尽管 Flutter 提供了…...

Chrome安装Vue插件vue-devtools

直接通过网站&#xff1a; Home | Vue Devtools (vuejs.org) chrome扩展商城中搜索&#xff1a;Vue.js devtools 参考下面edge扩展商城的图&#xff0c;两者流程相近...

内存和网卡压力测试

1.内存压力测试 1.1测试目的 内存压力测试的目的是评估开发板中的内存子系统性能和稳定性&#xff0c;以确保它能够满足特定的应用需求。开发板通常用于嵌入式系统、物联网设备、嵌入式智能家居等场景&#xff0c;这些场景对内存的要求通常比较高。 其内存压力测试的主要目的…...

法律行业案例法模型出现,OPenAI公布与法律AI公司Harvey合作案例

Harvey与OpenAl合作&#xff0c;为法律专业人士构建了一个定制训练的案例法模型。该模型是具有复杂推理广泛领域知识以及超越单一模型调用能力的任务的AI系统&#xff0c;如起草法律文件、回答复杂诉讼场景问题以及识别数百份合同之间的重大差异。 Harvey公司由具有反垄断和证…...

详解Qt网络编程

Qt的网络编程能力非常强大&#xff0c;它提供了从底层socket API到高层HTTP、FTP等协议处理的完整解决方案。下面将简要介绍Qt中网络编程的核心类及其功能&#xff0c;并给出一些基本的使用示例。 核心网络类&#xff1a; QTcpSocket 和 QTcpServer QTcpSocket 是用于TCP通信的…...

docker版Elasticsearch安装,ik分词器安装,用户名密码配置,kibana安装

1、安装es和ik分词器 创建映射目录并赋予权限&#xff1a; mkdir -p /docker_data/elasticsearch/conf mkdir -p /docker_data/elasticsearch/data mkdir -p /docker_data/elasticsearch/plugins chmod -R 777 /docker_data/elasticsearch编写配置文件&#xff1a; vi /dock…...

Python中的Requests库:HTTP请求的简单之道

目录 一、安装Requests库 二、发送请求 2.1 GET请求 2.2 POST请求 2.3 其他HTTP方法 三、处理响应 3.1 状态码 3.2 响应内容 3.3 自定义请求头 3.4 更多响应对象属性和方法 四、错误处理 五、高级请求 5.1 会话对象 5.2 SSL证书验证 5.3 设置代理 Http/Https代…...

[RK3566-Android11] 关于 a2dpsink -蓝牙支持接收播放/无PIN码连接

问题描述 1.蓝牙支持接收播放 2.蓝牙支持无PIN码连接&#xff08;不需要弹出pin配对码请求弹窗&#xff09; 3.蓝牙支持播放歌曲信息并应用层获取 解决方案&#xff1a; 1.a2dpsink-蓝牙需要支持接收播放补丁 1、device/rockchip/common/overlay/overlay/packages/apps/Blue…...

玩机进阶教程-----高通9008线刷XML脚本修改备份 檫除的操作步骤解析

在高通9008官方固件中我们可以看到刷写需要的脚本rawprogram0.xml和辅助脚本patch0.xml&#xff0c;脚本的作用在于将固件内各个分区对应写入手机内。根据分区地址段。然后判断脚本中那些分区不写入。以下步骤将分析emmc字库为例来讲解如何将默认刷入脚本修改为备份 檫除脚本。…...

前端路径问题总结

1.相对路径 不以/开头 以当前资源的所在路径为出发点去找目标资源 语法: ./表示当前资源的路径 ../表示当前资源的上一层路径 缺点:不同位置,相对路径写法不同2.绝对路径 以固定的路径作为出发点作为目标资源,和当前资源所在路径没关系 语法:以/开头,不同的项目中,固定的路径…...

YOLOv8改进 | 低照度检测 | 2024最新改进CPA-Enhancer链式思考网络(适用低照度、图像去雾、雨天、雪天)

一、本文介绍 本文给大家带来的2024.3月份最新改进机制,由CPA-Enhancer: Chain-of-Thought Prompted Adaptive Enhancer for Object Detection under Unknown Degradations论文提出的CPA-Enhancer链式思考网络,CPA-Enhancer通过引入链式思考提示机制,实现了对未知退化条件下…...

python的pip如何升级

升级pip的方法如下&#xff1a; 打开命令行工具。在Windows系统中&#xff0c;可以通过按下WinR键&#xff0c;然后输入"cmd"来打开命令提示符&#xff1b;在Mac或Linux系统中&#xff0c;可以直接打开终端。检查当前pip版本。在终端或命令行中输入以下命令&#…...

Collection与数据结构 Stack与Queue(一): 栈与Stack

1. 栈 1.1 概念 栈&#xff1a;一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶&#xff0c;另一端称为栈底。栈中的数据元素遵守后进先出LIFO&#xff08;Last In First Out&#xff09;的原则。 压栈&…...

内部类(来自类和对象的补充)

❤️❤️前言~&#x1f973;&#x1f389;&#x1f389;&#x1f389; hellohello~&#xff0c;大家好&#x1f495;&#x1f495;&#xff0c;这里是E绵绵呀✋✋ &#xff0c;如果觉得这篇文章还不错的话还请点赞❤️❤️收藏&#x1f49e; &#x1f49e; 关注&#x1f4a5;&a…...

Android 高德地图

1.获取Key 进入高德开放平台控制台&#xff0c;创建一个新应用。在创建的应用上点击"添加key"按钮&#xff0c;在弹出的对话框中&#xff0c;依次输入key名称&#xff0c;选择服务平台为“Android平台”&#xff0c;输入发布版安全码 SHA1、以及 Package。 获取 S…...

代码随想录|Day31|贪心06|738.单调递增的数字

738.单调递增的数字 思路&#xff1a; 1. 从右向左遍历 从字符串的最后一位向前遍历&#xff0c;即从低位到高位进行检查。这是因为当我们修改某一位数字时&#xff0c;可能会影响到更低位的数字。 2. 检查并修改数字 在遍历过程中&#xff0c;如果发现当前位数字小于其前一位&…...

机械制造学习笔记

一、切削加工、切削运动的基本概念及刀具切削过程 切削加工&#xff1a; 定义&#xff1a;切削加工是利用切削刀具对工件进行切削&#xff0c;以去除多余材料并得到所需形状和尺寸的加工方法之一。应用&#xff1a;广泛应用于金属加工、木材加工、塑料加工等领域&#xff0c;是…...

Golang | Leetcode Golang题解之第3题无重复字符的最长子串

题目&#xff1a; 题解&#xff1a; func lengthOfLongestSubstring(s string) int {// 哈希集合&#xff0c;记录每个字符是否出现过m : map[byte]int{}n : len(s)// 右指针&#xff0c;初始值为 -1&#xff0c;相当于我们在字符串的左边界的左侧&#xff0c;还没有开始移动r…...

SWM341系列应用(上位机应用)

SWM341系列之上位机应用 1、分级图像和PNG、JPG的应用 现象&#xff1a;客户使用SWM34SVET6HMI_0.4.1版本上位机进行UI界面布局&#xff0c;反馈在模拟运行时&#xff08;PC端&#xff09;流畅&#xff0c;在Demo平台&#xff08;设备端&#xff09;运行卡顿。 分析及解决&…...

【软件工程】详细设计(一)

1. 引言 1.1 编写目的 该文档的目的是描述《学生成绩管理系统》项目的详细设计&#xff0c;其主要内容包括&#xff1a; 系统功能简介 系统详细设计简述 各个模块的实现逻辑 最小模块组件的伪代码 本文档的预期的读者是&#xff1a; 开发人员 项目管理人员 测试人员 …...

【AIGC】如何在Windows/Linux上部署stable diffusion

文章目录 整体安装步骤windows10安装stable diffusion环境要求安装步骤注意事项参考博客其他事项安装显卡驱动安装cuda卸载cuda安装对应版本pytorch安装git上的python包Q&A linux安装stable diffusion安装anaconda安装cudagit 加速配置虚拟环境挂载oss&#xff08;optional…...

基于java实现的弹幕视频网站

开发语言&#xff1a;Java 框架&#xff1a;ssm 技术&#xff1a;JSP JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09; 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclip…...

【大数据存储】实验4 NoSQL数据库

实验4 NoSQL数据库 NoSQL数据库的安装和使用实验环境&#xff1a; Ubuntu 22.04.3 Jdk 1.8.0_341 Hadoop 3.2.3 Hbase 2.4.17 Redis 6.0.6 mongdb 6.0.12 mogosh 2.1.0 Redis 安装redis完成 新建终端启动redisredis-server新建一个终端redis-cli 建表操作 尝…...

从零学算法80

80. 删除有序数组中的重复项 II 给你一个有序数组 nums &#xff0c;请你 原地 删除重复出现的元素&#xff0c;使得出现次数超过两次的元素只出现两次 &#xff0c;返回删除后数组的新长度。 不要使用额外的数组空间&#xff0c;你必须在 原地 修改输入数组 并在使用 O(1) 额外…...

Jupyter notebook文件默认存储路径以及更改方法

初次使用Jupyter Notebook&#xff0c;确实好用啊&#xff01;但安装Anaconda后&#xff0c;打开Jupyter Notebook 的时候&#xff0c;新建文件的默认存储路径一般在C系统盘下面的XXX目录&#xff0c;那么路径是什么呢&#xff1f;我想把文件保存到其他的文件夹下应该怎么做呢&…...

WPF中通过自定义Panel实现控件拖动

背景 看到趋时软件的公众号文章&#xff08;WPF自定义Panel&#xff1a;让拖拽变得更简单&#xff09;&#xff0c;发现可以不通过Drag的方法来实现ListBox控件的拖动&#xff0c;而是通过对控件的坐标相加减去实现控件的位移等判断&#xff0c;因此根据文章里面的代码,边理解边…...

Centos7安装Docker与Docker-compose【图文教程】

个人记录 查看一下系统是否已经安装了Docker yum list installed | grep docker如下图代表没有安装Docker 卸载已有Docker yum remove docker docker-common docker-selinux docker-engine切换目录 cd /etc/yum.repos.d/查看当前目录所有的镜像源 ll安装yum-util与devi…...

mac电脑maven配置环境变量

1、下载maven https://maven.apache.org 2、配置环境变量 vim .bash_profile JAVA_HOME/Library/Java/JavaVirtualMachines/jdk-1.8.jdk/Contents/Home PATH$JAVA_HOME/bin:$PATH export JAVA_HOME export PATH#maven export MAVEN_HOME/Users/haines/desktop/work/java/a…...

后端返还二进制excl表格数据时候,如何实现在前端下载表格功能及出现表格打开失败的异常处理。

背景&#xff1a; 后端返还一个二进制流的excl表格数据&#xff0c;前端需要对其解析&#xff0c;然后可提供给客户进行下载。 思路&#xff1a;把二进制流数据转换给blob对象&#xff0c;然后利用a标签进行前端下载。 代码&#xff1a; 后端返还 类似如下的数据 前端代码…...

搞学术研究好用免费的学术版ChatGPT网站-学术AI

学术版ChatGPThttps://chat.uaskgpt.com/mobile/?user_sn88&channelcsdn&scenelogin 推荐一个非常适合中国本科硕士博士等学生老师使用的学术版ChatGPT&#xff0c; 对接了超大型学术模型&#xff0c;利用AI技术实现学术润色、中英文翻译&#xff0c;学术纠错&#…...

郑州建网站公司/乐陵市seo关键词优化

1、本系统的后台使用mysql数据库&#xff0c;SSH 框架&#xff0c;前端使用ExtJs实现。因为系统需要用到权限管理&#xff0c;所以作此记录&#xff0c;权限管理精确到前端的每一个按钮&#xff0c;甚至每一个action请求。废话不多说&#xff0c;直接进入主题&#xff08;一&am…...

做网站维护怎么找客户/百度知道答题赚钱

动态内存是一个有用的功能&#xff0c;但在配置虚拟机和主机服务器时&#xff0c;必须小心谨慎。谈及承载虚拟工作负载&#xff0c;硬件资源对整体性能的重要性可能不如物理内存。以确保每台虚拟机都具有所需内存且不会浪费进程中的内存的方式分配内存非常重要。下面是分配供 M…...

约小姐的微信小程序是什么/百度关键词排名优化

Prism框架对象DelegateCommand 1、常规命令支持 2、泛型命令支持 3、事件命令支持 4、事件聚合器-》Messenger(事件订阅发布) 定义一个基本消息类型,继承PubSubEvent 5、复合命令<prism:PrismApplication x:Class="Zhaoxi.PrismLesson.App"xmlns="http:/…...

别人买了域名做违法网站/百度地图导航

一、在keycloak中定义基础数据 1、realm 如果多个模块使用不同的用户权限&#xff0c;就分realm 如果多个模块共用一套用户权限&#xff0c;就顶一个一个realm 2、每个模块是一个client-app 3、用户组、用户、角色、权限定义 用户组支持扩展属性&#xff0c;加入组的用户自动继…...

整站优化昌吉可以吗?/网站怎么做收录

第五章&#xff1a;高级数据管理 5.2数值和字符处理函数 函数可分为数值(数学、统计、概率)函数和字符处理函数。 5.2.1数学函数 5.2.2统计函数 # 统计函数的示例 z <- mean(x, trim 0.05, na.rmTRUE) # 丢弃最大5%和最小5%的数据和所有缺失值后计算得到算术平均数 newd…...

上海网站制作电话/软文广告经典案例

PyTorch 自定义序列数据集 flyfish 实现 主要是DataLoader, TensorDataset。 数据集代码与模型训练代码解耦&#xff0c;以获得更好的可读性和模块化。PyTorch提供了两个数据原语&#xff1a;torch.utils.data.DataLoader和torch.utils.data.Dataset&#xff0c; 允许使用预…...