【适配鸿蒙next】Flutter 新一代混合栈管理框架

news2024/11/24 6:57:48

前言

据最新消息显示,华为今年下半年将全面转向其自主平台HarmonyOS,放弃Android系统。

报道中提到,下一版HarmonyOS预计将随华为即将推出的Mate 70旗舰系列一起发布。

据悉,HarmonyOS Next 已经扩展到4000个应用程序,其中包括支付宝和麦当劳。

按照消息人士的说法,HarmonyOS Next的应用总数将在年内增至5000个,华为的目标是在短期内达到500000个。

HarmonyOS Next从零开始设计,由华为自主开发操作系统。虽然最初这套代码是为物联网设备编写,但华为很快意识到,它需要通过开发自己的操作系统来最大限度地降低外界的打压和限制。

事实上,华为正在通过自己的努力,让大家看到第三个主流智能手机操作系统,并与苹果的iOS和Android展开竞争。

本文主要讲Flutter 新一代混合栈管理框架

简介

Fusion 是新一代的混合栈管理框架,用于 Flutter 与 Native 页面统一管理,并支持页面通信、页面生命周期监听等功能。Fusion 即 融合,我们的设计初衷就是帮助开发者在使用 Flutter 与 Native 进行混合开发时尽量感受不到两者的隔阂,提升开发体验。此外,Fusion 彻底解决了混合开发过程中普遍存在的黑屏、白屏、闪屏等问题,更加适合重视用户体验的App使用。

从 4.0 开始,Fusion 已完成纯鸿蒙平台(HarmonyOS Next/OpenHarmony,以下简称 HarmonyOS)的适配,开发者可以在Android、iOS、HarmonyOS上得到完全一致的体验。(HarmonyOS 的 Flutter SDK 可以在这里获取)
在这里插入图片描述

OSAndroidiOSHarmonyOSSDK5.0(21)+11.0+4.1(11)+
Fusion 采用引擎复用方案,在 Flutter 与 Native 页面多次跳转情况下,APP 始终仅有一份 FlutterEngine 实例,因此拥有更好的性能和更低的内存占用。
Fusion 也是目前仅有的支持混合开发时应用在后台被系统回收后,所有Flutter页面均可正常恢复的混合栈框架。

开始使用

0、准备

在开始前需要按照 Flutter 官方文档,将 Flutter Module 项目接入到 Android、iOS、HarmonyOS 工程中。

1、初始化

Flutter 侧
使用 FusionApp 替换之前使用的 App Widget,并传入所需路由表,默认路由表和自定义路由表可单独设置也可同时设置。

void main() {
  runApp(FusionApp(
    // 默认路由表
    routeMap: routeMap,
    // 自定义路由表
    customRouteMap: customRouteMap,
  ));
}

// 默认路由表,使用默认的 PageRoute
// 使用统一的路由动画
final Map<String, FusionPageFactory> routeMap = {
  '/test': (arguments) => TestPage(arguments: arguments),
  kUnknownRoute: (arguments) => UnknownPage(arguments: arguments),
};

// 自定义路由表,可自定义 PageRoute
// 比如:某些页面需要特定的路由动画则可使用该路由表
final Map<String, FusionPageCustomFactory> customRouteMap = {
  '/mine': (settings) => PageRouteBuilder(
      opaque: false,
      settings: settings,
      pageBuilder: (_, __, ___) => MinePage(
          arguments: settings.arguments as Map<String, dynamic>?)),
};

P.S: kUnknownRoute 表示未定义路由
注意:如果项目使用了 flutter_screenutil,需要在 runApp 前调用 Fusion.instance.install(),没有使用 flutter_screenutil则无须该步骤。

void main() {
  Fusion.instance.install();
  runApp(FusionApp(
    // 默认路由表
    routeMap: routeMap,
    // 自定义路由表
    customRouteMap: customRouteMap,
  ));
}

Android 侧
在 Application 中进行初始化,并实现 FusionRouteDelegate 接口

class MyApplication : Application(), FusionRouteDelegate {

    override fun onCreate() {
        super.onCreate()
        Fusion.install(this, this)
    }

    override fun pushNativeRoute(name: String?, arguments: Map<String, Any>?) {
        // 根据路由 name 跳转对应 Native 页面
    }

    override fun pushFlutterRoute(name: String?, arguments: Map<String, Any>?) {
        // 根据路由 name 跳转对应 Flutter 页面
        // 可在 arguments 中存放参数判断是否需要打开透明页面
    }
}

iOS 侧
在 AppDelegate 中进行初始化,并实现 FusionRouteDelegate 代理

@UIApplicationMain
@objc class AppDelegate: UIResponder, UIApplicationDelegate, FusionRouteDelegate {
    
    func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
      ...
      Fusion.instance.install(self)
      ...
    return true
  }
    
    func pushNativeRoute(name: String?, arguments: Dictionary<String, Any>?) {
        // 根据路由 name 跳转对应 Native 页面
    }
    
    func pushFlutterRoute(name: String?, arguments: Dictionary<String, Any>?) {
        // 根据路由 name 跳转对应 Flutter 页面
        // 可在 arguments 中存放参数判断是否需要打开透明页面
        // 可在 arguments 中存放参数判断是 push 还是 present
    }
}

HarmonyOS 侧

在 UIAbility 中进行初始化,并实现 FusionRouteDelegate 代理

export default class EntryAbility extends UIAbility implements FusionRouteDelegate {
  private static TAG = 'EntryAbility'
  private mainWindow: window.Window | null = null
  private windowStage: window.WindowStage | null = null

  override async onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): Promise<void> {
    await Fusion.instance.install(this.context, this)
    GeneratedPluginRegistrant.registerWith(Fusion.instance.defaultEngine!)
  }

  pushNativeRoute(name: string, args: Map<string, Object> | null): void {
    // 根据路由 name 跳转对应 Native 页面
  }

  pushFlutterRoute(name: string, args: Map<string, Object> | null): void {
    // 根据路由 name 跳转对应 Flutter 页面
    // 可在 arguments 中存放参数判断是否需要打开透明页面
  }
}

2、Flutter 容器

普通页面模式
Android 侧
通过 FusionActivity(或其子类) 创建 Flutter 容器,启动容器时需要使用 Fusion 提供的 buildFusionIntent 方法,其中参数 transparent 需设为 false。其 xml 配置参考如下(如果使用 FusionActivity 则不用配置):

    <activity
        android:name=".CustomFusionActivity"
        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
        android:exported="false"
        android:hardwareAccelerated="true"
        android:launchMode="standard"
        android:theme="@style/FusionNormalTheme"
        android:windowSoftInputMode="adjustResize" />

iOS 侧
通过 FusionViewController (或其子类)创建 Flutter 容器,push 和 present 均支持。FusionViewController 默认隐藏了 UINavigationController。
在 iOS 中需要处理原生右滑退出手势和 Flutter 手势冲突的问题,解决方法也很简单:只需在自定义的 Flutter 容器中实现 FusionPopGestureHandler 并在对应方法中启用或者关闭原生手势即可,这样可以实现如果当前 Flutter 容器存在多个 Flutter 页面时,右滑手势是退出 Flutter 页面,而当 Flutter 页面只有一个时则右滑退出 Flutter 容器。

  // 启用原生手势
    func enablePopGesture() {
        // 以下代码仅做演示,不可直接照搬,需根据APP实际情况自行实现
        navigationController?.interactivePopGestureRecognizer?.isEnabled = true
    }

    // 关闭原生手势
    func disablePopGesture() {
        // 以下代码仅做演示,不可直接照搬,需根据APP实际情况自行实现
        navigationController?.interactivePopGestureRecognizer?.isEnabled = false
    }

HarmonyOS 侧
通过 FusionEntry(或其子类) 创建 Flutter 容器,启动容器时需要使用 Fusion 提供的 buildFusionParams 方法,也可直接使用 FusionPage。默认全屏模式。

    const params = buildFusionParams(name, args, false, backgroundColor)
    this.mainLocalStorage?.setOrCreate('params', params)
    router.pushNamedRoute({name: FusionConstant.FUSION_ROUTE_NAME})

透明页面模式

Android 侧
使用方式与普通页面模式相似,只是buildFusionIntent 方法的参数 transparent 需设为 true,其 xml 配置参考如下:

     <activity
         android:name=".TransparentFusionActivity"
         android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
         android:exported="false"
         android:hardwareAccelerated="true"
         android:launchMode="standard"
         android:theme="@style/FusionTransparentTheme"
         android:windowSoftInputMode="adjustResize" />

iOS 侧
使用方式与普通页面模式相似:

let fusionVc = CustomViewController(routeName: name, routeArguments: arguments, transparent: true)
navController?.present(fusionVc, animated: false)

HarmonyOS 侧
使用方式与普通页面模式相似:

    const params = buildFusionParams(name, args, true, backgroundColor)
    this.windowStage?.createSubWindow(FusionConstant.TRANSPARENT_WINDOW, (_, win) => {
      const record: Record<string, Object> = {
        'params': params
      }
      win.loadContentByName(FusionConstant.FUSION_ROUTE_NAME, new LocalStorage(record))
      win.showWindow()
    })

Flutter 侧
同时Flutter页面背景也需要设置为透明

子页面模式

子页面模式是指一个或多个 Flutter 页面同时嵌入到 Native 容器中的场景,如:使用Tab切换Flutter和原生页面,Fusion 支持多个 Flutter 页面嵌入同一个 Native 容器中
Android 侧
使用 FusionFragment 以支持子页面模式,创建 FusionFragment 对象需要使用 buildFusionFragment 方法
iOS 侧
与页面模式一样使用 FusionViewController
HarmonyOS 侧
与页面模式一样使用 FusionEntry,配合 buildFusionParams方法配置参数

自定义容器背景色

默认情况下容器的背景为白色,这是因为考虑到绝大多数的页面都是使用白色背景,但如果打开的首个Flutter页面的背景是其他颜色,比如夜间模式下页面为深灰色,此时是为了更好的视觉效果,可以自定义容器的背景色与首个Flutter页面的背景色一致。
Android 侧
在 buildFusionIntent 和 buildFusionFragment方法中参数 backgroundColor 设为所需背景色
iOS 侧
在创建 FusionViewController (或其子类)对象时,参数 backgroundColor 设为所需背景色
HarmonyOS 侧
在 buildFusionParams方法中参数 backgroundColor 设为所需背景色

3、路由API(FusionNavigator)

○push:将对应路由入栈,Navigator.pushNamed 与之等同,根据FusionRouteType分为以下几种方式:

○flutter模式: 在当前Flutter容器中将指定路由对应的Flutter页面入栈,如果没有则跳转kUnknownRoute对应Flutter页面
○flutterWithContainer模式: 创建一个新的Flutter容器,并将指定路由对应的Flutter页面入栈,如果没有则跳转kUnknownRoute对应Flutter页面。即执行FusionRouteDelegate的pushFlutterRoute
○native模式: 将指定路由对应的Native页面入栈,即执行FusionRouteDelegate的pushNativeRoute
○adaption模式: 自适应模式,默认类型。首先判断该路由是否是Flutter路由,如果不是则进入native模式,如果是再判断当前是否是页面是否是Flutter容器,如果是则进入flutter模式,如果不是则进入flutterWithContainer模式

●pop:在当前Flutter容器中将栈顶路由出栈,Navigator.pop 与之等同
●maybePop:在当前Flutter容器中将栈顶路由出栈,可被WillPopScope拦截
●replace:在当前Flutter容器中将栈顶路由替换为对应路由,Navigator.pushReplacementNamed 与之等同
●remove:在当前Flutter容器中移除对应路由

路由跳转与关闭等操作既可使用FusionNavigator的 API,也可使用Navigator中与之对应的API(仅上述提到的部分)

4、Flutter Plugin 注册

在 Android 和 iOS 平台上框架内部会自动注册插件,无须手动调用 GeneratedPluginRegistrant.registerWith 进行注册,但 HarmonyOS 必须手动调用该方法。

5、自定义 Channel

如果需要 Native 与 Flutter 进行通信,则需要自行创建 Channel,创建 Channel 方式如下(以 MethodChannel 为例):
Android 侧
①、与容器无关的方法
在 Application 中进行注册

val channel = Fusion.defaultEngine?.dartExecutor?.binaryMessenger?.let {
    MethodChannel(
        it,
        "custom_channel"
    )
}
channel?.setMethodCallHandler { call, result -> 
}

②、与容器相关的方法
在自实现的 FusionActivity、FusionFragmentActivity、FusionFragment 上实现 FusionMessengerHandler 接口,在 configureFlutterChannel 中创建 Channel,在 releaseFlutterChannel 释放 Channel

class CustomActivity : FusionActivity(), FusionMessengerHandler {
  
    override fun configureFlutterChannel(binaryMessenger: BinaryMessenger) {
        val channel = MethodChannel(binaryMessenger, "custom_channel")
        channel.setMethodCallHandler { call, result -> 
            
        }
    }
  
    override fun releaseFlutterChannel() {
        channel?.setMethodCallHandler(null)
        channel = null
    }
}

iOS 侧

①、与容器无关的方法

在 AppDelegate 中进行注册

var channel: FlutterMethodChannel? = nil
if let binaryMessenger = Fusion.instance.defaultEngine?.binaryMessenger {
    channel = FlutterMethodChannel(name: "custom_channel", binaryMessenger: binaryMessenger)
}
channel?.setMethodCallHandler { (call: FlutterMethodCall, result: @escaping FlutterResult) in
}

②、与容器相关的方法

在自实现的 FusionViewController 上实现 FusionMessengerHandler 协议,在协议方法中创建 Channel

class CustomViewController : FusionViewController, FusionMessengerHandler {
    func configureFlutterChannel(binaryMessenger: FlutterBinaryMessenger) {
        channel = FlutterMethodChannel(name: "custom_channel", binaryMessenger: binaryMessenger)
        channel?.setMethodCallHandler { (call: FlutterMethodCall, result: @escaping FlutterResult) in
            
        }
    }
    
    func releaseFlutterChannel() {
        channel?.setMethodCallHandler(nil)
        channel = nil
    }
}

HarmonyOS 侧

①、与容器无关的方法

在 UIAbility 中进行注册

const binaryMessenger = Fusion.instance.defaultEngine?.dartExecutor.getBinaryMessenger()
const channel = new MethodChannel(binaryMessenger!, 'custom_channel')
channel.setMethodCallHandler({
  onMethodCall(call: MethodCall, result: MethodResult): void {
    
  }
})

②、与容器相关的方法

在自实现的 FusionEntry 上实现 FusionMessengerHandler 接口,在 configureFlutterChannel 中创建 Channel,在 releaseFlutterChannel 释放 Channel

export default class CustomFusionEntry extends FusionEntry implements FusionMessengerHandler, MethodCallHandler {
  private channel: MethodChannel | null = null

  configureFlutterChannel(binaryMessenger: BinaryMessenger): void {
    this.channel = new MethodChannel(binaryMessenger, 'custom_channel')
    this.channel.setMethodCallHandler(this)
  }

  onMethodCall(call: MethodCall, result: MethodResult): void {
    result.success(`Custom Channel:${this}_${call.method}`)
  }

  releaseFlutterChannel(): void {
    this.channel?.setMethodCallHandler(null)
    this.channel = null
  }
}

BasicMessageChannel 和 EventChannel 使用也是类似

P.S.: 与容器相关的方法是与容器生命周期绑定的,如果容器不可见或者销毁了则无法收到Channel消息。

6、生命周期

应用生命周期监听:

①、在 Flutter 侧任意处注册监听皆可,并implements FusionAppLifecycleListener

②、根据实际情况决定是否需要注销监听

void main() {
  ...
  FusionAppLifecycleBinding.instance.register(MyAppLifecycleListener());
  runApp(const MyApp());
}

class MyAppLifecycleListener implements FusionAppLifecycleListener {
  @override
  void onBackground() {
    print('onBackground');
  }

  @override
  void onForeground() {
    print('onForeground');
  }
}

FusionAppLifecycleListener 生命周期回调函数:

●onForeground: 应用进入前台会被调用(首次启动不会被调用,Android 与 iOS 保持一致)
●onBackground: 应用退到后台会被调用

页面生命周期监听:

●①、在需要监听生命周期页面的 State 中 implements FusionPageLifecycleListener
●②、在 didChangeDependencies 中注册监听
●③、在 dispose 中注销监听

class LifecyclePage extends StatefulWidget {
  const LifecyclePage({Key? key}) : super(key: key);

  @override
  State<LifecyclePage> createState() => _LifecyclePageState();
}

class _LifecyclePageState extends State<LifecyclePage>
    implements FusionPageLifecycleListener {
  @override
  Widget build(BuildContext context) {
    return Container();
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    FusionPageLifecycleBinding.instance.register(this);
  }

  @override
  void onPageVisible() {}

  @override
  void onPageInvisible() {}

  @override
  void onForeground() {}

  @override
  void onBackground() {}

  @override
  void dispose() {
    super.dispose();
    FusionPageLifecycleBinding.instance.unregister(this);
  }
}

PageLifecycleListener 生命周期回调函数:

●onForeground: 应用进入前台会被调用,所有注册了生命周期监听的页面都会收到
●onBackground: 应用退到后台会被调用,所有注册了生命周期监听的页面都会收到
●onPageVisible: 该 Flutter 页面可见时被调用,如:从 Native 页面或其他 Flutter 页面 push 到该 Flutter 页面时;从 Native 页面或其他 Flutter 页面 pop 到该 Flutter 页面时;应用进入前台时也会被调用。
●onPageInvisible: 该 Flutter 页面不可见时被调用,如:从该 Flutter 页面 push 到 Native 页面或其他 Flutter 页面时;如从该 Flutter 页面 pop 到 Native 页面或其他 Flutter 页面时;应用退到后台时也会被调用。

7、全局通信

支持消息在应用中的传递,可以指定 Native 还是 Flutter 或者全局接收和发送。

注册消息监听

Flutter侧

●①、在需要监听消息的类中 implements FusionNotificationListener,并复写 onReceive 方法,该方法可收到发送过来的消息
●②、在合适时机注册监听
●③、在合适时机注销监听

class TestPage extends StatefulWidget {

  @override
  State<TestPage> createState() => _TestPageState();
}

class _TestPageState extends State<TestPage> implements FusionNotificationListener {

  @override
  void onReceive(String name, Map<String, dynamic>? body) {
    
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    FusionNotificationBinding.instance.register(this);
  }

  @override
  void dispose() {
    super.dispose();
    FusionNotificationBinding.instance.unregister(this);
  }
}

Native侧

●①、在需要监听消息的类中实现 FusionNotificationListener 接口,并复写 onReceive 方法,该方法可收到发送过来的消息
●②、在适当时机使用 FusionNotificationBinding 的 register 方法注册监听
●③、在适当时机使用 FusionNotificationBinding 的 unregister 方法注销监听

发送消息

三端均可使用FusionNavigator 的 sendMessage 方法来发送消息,根据使用FusionNotificationType 不同类型有不同效果:

●flutter: 仅 Flutter 可以收到
●native: 仅 Native 可以收到
●global(默认): Flutter 和 Native 都可以收到

8、返回拦截

在纯 Flutter 开发中可以使用WillPopScope组件拦截返回操作,Fusion 也完整支持该功能,使用方式与在纯 Flutter 开发完全一致,此外使用FusionNavigator.maybePop的操作也可被WillPopScope组件拦截。

9、状态恢复

Fusion 支持 Android 和 iOS 平台 APP 被回收后 Flutter 路由的恢复。

总结

总的来说,华为鸿蒙不再兼容安卓,对中年程序员来说是一个挑战,也是一个机会。随着鸿蒙的不断发展以及国家的大力支持,未来鸿蒙职位肯定会迎来一个大的爆发,只有积极应对变化,不断学习和提升自己,我们才能在这个变革的时代中立于不败之地。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1807232.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Python学习1】matplotlib和pandas库绘制人口数变化曲线

✍&#x1f3fb;记录学习过程中的输出&#xff0c;坚持每天学习一点点~ ❤️希望能给大家提供帮助~欢迎点赞&#x1f44d;&#x1f3fb;收藏⭐评论✍&#x1f3fb;指点&#x1f64f; 一、Python库说明 Matplotlib Matplotlib是一个功能强大的Python 2D绘图库&#xff0c;它允…

【实战项目二】Python爬取豆瓣影评

目录 一、环境准备 二、编写代码 一、环境准备 pip install beautifulsoup4 pip intall lxml pip install requests我们需要爬取这些影评 二、编写代码 我们发现每个影评所在的div的class都相同&#xff0c;我们可以从这入手 from bs4 import BeautifulSoup import request…

修改云主机配置 - 内存增容

文章目录 一、修改云主机配置缘由二、修改云主机配置步骤1、查看云主机概述2、查看master云主机3、更改master云主机配置4、查看master云主机 三、使用Spark Shell玩Saprk SQL1、启动HDFS服务2、启动Spark集群3、启动集群模式Spark Shell4、读取文件生成单例数据帧5、将单列数据…

给浮躁的面试者一个建议

哈喽&#xff0c;大家好&#xff0c;我叫人宅&#xff0c;关于找工作&#xff0c;大家心态非常浮躁&#xff0c;尤其是零零后&#xff0c;或者是九五后。本次为大家分享一下关于就业问题和就业态度。 我讲解的这些其实适合所有高科技行业。我这边就拿程序员为例。 如果你是刚毕…

Python Mistune库:Markdown解析和处理

更多Python学习内容&#xff1a;ipengtao.com Mistune是一个用于Python的快速且功能强大的Markdown解析库。它以其高性能和灵活性著称&#xff0c;能够轻松扩展和定制。Mistune支持标准的Markdown语法&#xff0c;并且可以通过插件扩展支持更多功能&#xff0c;例如数学公式、高…

云服务器Ubuntu系统的vim-plus(youcompleteme)完整安装

一. 安装vim-plus PS&#xff1a;需要在那个用户下配置vim-plus&#xff0c;就到那个用户下执行代码 git clone https://github.com/chxuan/vimplus.git ~/.vimplus cd ~/.vimplus ./install.sh二. 解决没有代码自动补全的问题 随便创建一个Test.cpp文件&#xff0c;vim打开…

线性代数|机器学习-P10最小二乘法的四种方案

文章目录 1. 概述2. SVD奇异值分解3. 最小二乘法方程解4. 最小二乘法图像解释5. Gram-Schmidt 1. 概述 当我们需要根据一堆数据点去拟合出一条近似的直线的时候&#xff0c;就会用到 最小二乘法 .根据矩阵A的情况&#xff0c;有如下四种方法 在r n m 时&#xff0c;SVD奇异…

Apple - Quartz 2D Programming Guide

本文翻译自&#xff1a;Quartz 2D Programming Guide&#xff08;更新时间&#xff1a;2017-03-21 https://developer.apple.com/library/archive/documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/Introduction/Introduction.html#//apple_ref/doc/uid/TP300010…

SpringCloud中注册中心Nacos的下载与使用步骤

1.前言 Nacos&#xff08;Dynamic Naming and Configuration Service&#xff09;是阿里巴巴开源的一款服务发现和配置管理工具。它可以帮助用户自动化地进行服务注册、发现和配置管理&#xff0c;是面向微服务架构的一个重要组成部分。 2.下载 链接&#xff1a;https://pan.b…

002-链路聚合

链路聚合 链路聚合是一个重要的计算机网络术语&#xff0c;它涉及将多个物理端口汇聚在一起&#xff0c;形成一个逻辑端口&#xff0c;从而增加网络带宽、实现链路传输的弹性和工程冗余。 定义与基本原理 定义&#xff1a;链路聚合&#xff08;英语&#xff1a;Link Aggrega…

6.7-6.10作业

1. /*1.使用switch实现银行系统&#xff0c;默认用户为A&#xff0c;密码为1234&#xff0c;余额2000 如果登录失败&#xff0c;则直接结束 如果登录成功&#xff0c;则显示银行页面 1.查询余额 2.取钱 3.存钱 如果是1&#xff0c;则打印余额 如果是2&#xff0c;则输入取钱金…

纳什均衡:博弈论中的运作方式、示例以及囚徒困境

文章目录 一、说明二、什么是纳什均衡&#xff1f;2.1 基本概念2.2 关键要点 三、理解纳什均衡四、纳什均衡与主导策略五、纳什均衡的例子六、囚徒困境七、如何原理和应用7.1 博弈论中的纳什均衡是什么&#xff1f;7.2 如何找到纳什均衡&#xff1f;7.3 为什么纳什均衡很重要&a…

python 判断点和线段相交

python 判断点和线段相交 import numpy as np import cv2 import numpy as npdef point_to_line_distance(points, line_segments):# line_segments [[549, 303], [580, 303]]# points [565, 304]x0, y0, x1, y1line_segments[0][0], line_segments[0][1], line_segments[1]…

手把手带你做一个自己的网络调试助手(1) - TCP服务器准备

程序设计流程图 TCP 服务器 ui界面搭建 Tcp服务器建立连接 - listen() connect() 1.在构造函数中进行如下初始化: 通过 QNetworkInterface 类的 allAddresses 这一API 获得可用IP地址&#xff08;包括IPv4 和 IPv6&#xff09; 然后通过QHostAddress类的 protocol 这一AP…

前端-a-date-picker如何设置禁选时间段

想要做到如图所示的效果&#xff0c;代码如下&#xff1a; 第一个是只能选择某一天&#xff0c;第二个是只能选择某一个时间段 <a-date-pickerv-model:value"record.onTimeStr":show-time"{ format: HH:mm }"valueFormat"YYYY-MM-DD HH:mm:ss&qu…

【算法小记】深度学习——时间序列数据分析 Time series Data Analysis

在本篇博客中将简单介绍常见的几种循环神经网络和一维卷积神经网络&#xff0c;并使用一些简答的数据进行拟合分析。本文相对适合刚入门的同学&#xff0c;同时也作为自己过去一段时间学习的总结和记录&#xff0c;现在神经网络框架已经非常完善的支持了很多常见和有效的深度学…

【学永远不嫌晚】Linux操作系统,linux教程,动力节点linux,老杜linux

碎碎念 总是遇到一些恶心的事情 看最新教程 老师安装的是 vm17 pro&#xff0c;想着也去安装&#xff0c;搜了一大堆&#xff0c;都指向官网下载。 https://support.broadcom.com/group/ecx/productdownloads?subfamilyVMwareWorkstationPro 安装显示没有 entitlement&#…

“GPT-4o深度解析:技术演进、能力评估与个人体验综述“

文章目录 每日一句正能量前言对比分析模型架构性能应用场景用户体验技术创新社区和生态系统总结 技术能力语言生成能力语言理解能力技术实现总结 个人感受关于GPT-4o的假设性观点&#xff1a;关于当前语言模型的一般性观点&#xff1a; 后记 每日一句正能量 又回到了原点&#…

2024年CKA模拟系统制作 | step-by-step | 1、基础环境准备

目录 一、软件环境 二、虚拟网络环境准备 1、编辑虚拟网络 2、网络设置 三、新建虚拟主机 1、新建目录 2、新建虚拟主机 四、系统安装 1、装载系统镜像 2、开启虚拟机 3、选择语言 4、键盘选择 5、网络配置 6、代理设置 7、设置软件源 8、存储设置 9、名称设置 …

记一次postgresql拼接函数string_agg() 和row_number() 使用

PG两个函数使用需求和简单介绍 需求背景介绍第一个需求背景是这样的需求升级一下接下来讲讲STRING_AGG()基本语法排序 然后我们再说说ROW_NUMBER()基本语法使用 row_number() over (partition by) 进行分组统计使用 row_num限定每组数量 需求背景介绍 第一个需求背景是这样的 …