Flutter高仿微信-第36篇-单聊-语音通话

news2025/8/8 2:33:31

Flutter高仿微信系列共59篇,从Flutter客户端、Kotlin客户端、Web服务器、数据库表结构、Xmpp即时通讯服务器、视频通话服务器、腾讯云服务器全面讲解。

详情请查看

效果图:

目前市场上第三方音频接口的价格高的吓人

语音通话价格:
5元/千分钟

这里的语音通话不接第三方sdk,自己实现的音视频服务器。

详情请参考 Flutter高仿微信-第29篇-单聊 , 这里只是提取音频通话的部分代码。

实现代码:

音频监听:

/**
 * Author : wangning
 * Email : maoning20080809@163.com
 * Date : 2022/10/4 10:43
 * Description : 
 */

class VideoCallUtils {

  static final VideoCallUtils _instance = VideoCallUtils._internal();

  static VideoCallUtils getInstance(){
    return _instance;
  }

  VideoCallUtils._internal(){
  }

  Signaling? _signaling;
  //String host = "demo.cloudwebrtc.com";
  String host = CommonUtils.BASE_IP;
  Session? _session;

  var localStream;
  var remoteStream;

  void connect(BuildContext context) async {

    _signaling ??= Signaling(host, context)..connect();
    _signaling?.onSignalingStateChange = (SignalingState state) {
      switch (state) {
        case SignalingState.ConnectionClosed:
        case SignalingState.ConnectionError:
        case SignalingState.ConnectionOpen:
          break;
      }
    };

    _signaling?.onCallStateChange = (Session session, CallState state) async {
      LogUtils.d("video_call_utils 回调状态:${state}, ${session.sid} , ${session.pid}");
      switch (state) {
        case CallState.CallStateNew:
          _session = session;
          break;
        case CallState.CallStateRinging:
          String sid = session.sid;
          String mediaFlag = "";
          Map<String, dynamic>? configMap = session.pc?.getConfiguration;
          LogUtils.d("video_call_utils 是否map :${configMap}");
          if(configMap != null){
            configMap.forEach((key, value) {
              if(key == "mediaFlag"){
                LogUtils.d("video_call_utils 是否3:${key} , ${value}");
                mediaFlag = value;
              }
            });
          }

          Navigator.push(context, MaterialPageRoute(builder: (context) => ShowVideoCall(host: host, signaling: _signaling,
              session: _session,key: keyShowVideoCall,mediaFlag: mediaFlag,)));
          break;
        case CallState.CallStateBye:
          LogUtils.d("video_call_utils 33退出:${keyShowVideoCall} , , ${keyShowVideoCall.currentState}");
          keyShowVideoCall.currentState?.callStateBye();
          break;
        case CallState.CallStateInvite:
          keyShowVideoCall.currentState?.callStateInvite();
          break;
        case CallState.CallStateConnected:
          keyShowVideoCall.currentState?.callStateConnected();

          break;
        case CallState.CallStateRinging:
      }
    };

    _signaling?.onPeersUpdate = ((event) {
    });

    _signaling?.onLocalStream = ((stream) {
      localStream = stream;
      keyShowVideoCall.currentState?.onLocalStream(stream);
    });

    _signaling?.onAddRemoteStream = ((_, stream) {
      remoteStream = stream;
      keyShowVideoCall.currentState?.onAddRemoteStream(stream);
    });

    _signaling?.onRemoveRemoteStream = ((_, stream) {
      keyShowVideoCall.currentState?.onRemoveRemoteStream();
    });
  }

}

当接收到音频来电时弹出页面:

GlobalKey<ShowVideoCallState> keyShowVideoCall = GlobalKey();

/**
 * Author : wangning
 * Email : maoning20080809@163.com
 * Date : 2022/10/4 10:43
 * Description : 显示音视频通话
 */
class ShowVideoCall extends StatefulWidget {
  static String tag = 'call_sample';
  final String host;
  final Signaling? signaling;
  final Session? session;
  final String mediaFlag;

  ShowVideoCall({required Key key, required this.host,
    required this.signaling, required this.session, required this.mediaFlag}) :super(key:key);

  @override
  ShowVideoCallState createState() => ShowVideoCallState();
}

class ShowVideoCallState extends State<ShowVideoCall> {
  Signaling? _signaling;
  String? _selfId;
  RTCVideoRenderer _localRenderer = RTCVideoRenderer();
  RTCVideoRenderer _remoteRenderer = RTCVideoRenderer();
  bool _inCalling = false;
  Session? _session;
  bool _waitAccept = false;
  bool _isExist = false;

  //好友id
  String otherUserId = "";
  UserBean? userBean;
  //麦克风打开
  bool isMic = true;
  //扬声器
  bool isSpeaker = true;

  @override
  initState() {
    super.initState();
    _session = widget.session;
    _signaling = widget.signaling;
    otherUserId = _session?.pid??"";
    initRenderers();
    //_connect(context);
    WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
      LogUtils.d("显示视频加载完成。。。${_session} , ${_signaling}, ${_session?.sid} , ${_session?.pid}");
      callStateRinging();
    });
    loadUser();
  }

  void loadUser() async{
    userBean = await UserRepository.getInstance().findUserByAccount(otherUserId);
    if(userBean != null){
      setState(() {
      });
    }
  }

  initRenderers() async {
    await _localRenderer.initialize();
    await _remoteRenderer.initialize();

    var localStream = VideoCallUtils.getInstance().localStream;
    var remoteStream = VideoCallUtils.getInstance().remoteStream;
    LogUtils.d("show_video_call initRenderers视频 :${localStream}, ${remoteStream}");
    _localRenderer.srcObject = localStream;
    _remoteRenderer.srcObject = remoteStream;
  }

  Timer? _timer;
  //计时多少秒
  int currentTimer = 0;
  //转换结果时间
  String resultTimer = "00:00";
  void _processTimer(){
    if(_inCalling && widget.mediaFlag == CommonUtils.MEDIA_FLAG_VOICE){
      _timer = Timer.periodic(Duration(seconds: 1), (timer) {
        currentTimer++;
        resultTimer = WnDateUtils.changeSecondToMMSS(currentTimer);
        setState(() {
        });
      });
    }
  }

  @override
  deactivate() {
    super.deactivate();
    /*_signaling?.close();
    _localRenderer.dispose();
    _remoteRenderer.dispose();*/
    _stopVoice();
  }

  void callStateConnected(){
    LogUtils.d("show_video_call callStateConnected :${_waitAccept}");
    if (_waitAccept) {
    _waitAccept = false;
    Navigator.of(context).pop(false);
    }
    setState(() {
    _inCalling = true;
    _processTimer();
    });
  }

  void onRemoveRemoteStream(){
    LogUtils.d("show_video_call onRemoveRemoteStream ");
    _remoteRenderer.srcObject = null;
  }

  void onAddRemoteStream(stream){
    LogUtils.d("show_video_call onAddRemoteStream ${stream} ");
    _remoteRenderer.srcObject = stream;
    setState(() {});
  }

  void onLocalStream(stream){
    LogUtils.d("show_video_call onLocalStream ${stream} ");
    _localRenderer.srcObject = stream;
    setState(() {});
  }

  void callStateInvite(){
    LogUtils.d("show_video_call callStateInvite ");
    _waitAccept = true;
    _showInvateDialog();
  }

  void callStateBye(){
    LogUtils.d("show_video_call callStateBye ${_waitAccept}");
    /*if (_waitAccept) {
      _waitAccept = false;
      Navigator.of(context).pop(false);
    }*/
    if(!_isExist){
      Navigator.pop(context);
    }
    setState(() {
    _localRenderer.srcObject = null;
    _remoteRenderer.srcObject = null;
    _inCalling = false;
    _session = null;
    });
  }

  void callStateRinging() async{
    _playVoice();
    await _showAcceptWidget();
  }

  void callStateRingingResult(bool? accept) async{
    _stopVoice();
    if (accept!) {
      _accept();
      setState(() {
        _inCalling = true;
        _processTimer();
      });
    } else {
      _reject();
    }

  }

  //开始播放视频声音
  void _playVoice(){
    final List<String> soundList = CommonUtils.getSoundList();

    int selectedVideoCallId = SpUtils.getIntDefaultValue(CommonUtils.SETTING_VIDEO_CALL_ID, 2);
    bool videoCallSwitch = SpUtils.getBoolDefaultValue(CommonUtils.SETTING_VIDEO_CALL_SWITCH, true);

    //如果设置视频通话不响铃
    if(!videoCallSwitch){
      return;
    }
    //设置了视频通话响铃,但是选择无声音
    if(videoCallSwitch && selectedVideoCallId == 0){
      return;
    }
    String sound = "${soundList[selectedVideoCallId]}";
    AudioPlayer.getInstance().playAsset("sounds/${sound}.mp3", isLoop:true, callback:(data){
      LogUtils.d("播放视频声音:${data}");
    });

  }

  void _stopVoice(){
    AudioPlayer.getInstance().stop();
  }

  //显示邀请页面
  Widget _showAcceptWidget(){
    return Container(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          //SizedBox(height: 30,),
          Container(
            alignment: AlignmentDirectional.center,
            margin: EdgeInsets.only(top: 18),
            child: Column(
              children: [
                //Image.asset(CommonUtils.getBaseIconUrlPng("wc_chat_speaker_open"), width: 28, height: 28,),
                Text("邀请你${widget.mediaFlag == CommonUtils.MEDIA_FLAG_VIDEO? '视频通话':'语音通话'}", style: TextStyle(fontSize: 18, color: Colors.black),),
                SizedBox(height: 30,),
                CommonAvatarView.showBaseImage(userBean?.avatar??"", 100, 100),
                SizedBox(height: 10,),
                Text("${userBean?.nickName}", style: TextStyle(fontSize: 26, color: Colors.black),),
              ],
            ),
          ),

          Container(
            margin: EdgeInsets.only(bottom: 40),
            alignment: AlignmentDirectional.center,
            child: Row(
              crossAxisAlignment: CrossAxisAlignment.center,
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Container(
                  width: 80,
                  height: 80,
                  child: FloatingActionButton(
                    child: Icon(Icons.call_end, size: 38,),
                    backgroundColor: Colors.pink,
                    onPressed: (){
                      callStateRingingResult(false);
                    },
                  ),
                ),

                SizedBox(width: 40,),
                Container(
                  width: 80,
                  height: 80,
                  child: FloatingActionButton(
                    child: Icon(Icons.call_end, size: 38,),
                    backgroundColor: Colors.lightGreen,
                    onPressed: (){
                      callStateRingingResult(true);
                    },
                  ),
                ),

              ],
            ),
          ),

        ],
      ),
    );
  }

  Future<bool?> _showAcceptDialog() {
    LogUtils.d("显示对话框。。${_inCalling}");
    return showDialog<bool?>(
      context: context,
      builder: (context) {
        return AlertDialog(
          title: Text("视频通话"),
          content: Text("是否接受好友的视频请求?"),
          actions: <Widget>[
            TextButton(
              child: Text("拒绝"),
              onPressed: () => Navigator.of(context).pop(false),
            ),
            TextButton(
              child: Text("接受"),
              onPressed: () {
                Navigator.of(context).pop(true);
              },
            ),
          ],
        );
      },
    );
  }

  Future<bool?> _showInvateDialog() {
    return showDialog<bool?>(
      context: context,
      builder: (context) {
        return AlertDialog(
          title: Text("视频通话"),
          content: Text("邀请好友视频通话,请等待对方接受。"),
          actions: <Widget>[
            TextButton(
              child: Text("取消"),
              onPressed: () {
                Navigator.of(context).pop(false);
                _hangUp();
              },
            ),
          ],
        );
      },
    );
  }


  _accept() {
    LogUtils.d("show_video_call 接受1:${_session}, ${_signaling}");
    if (_session != null) {
      LogUtils.d("show_video_call 接受2:${_session}");
      _signaling?.accept(_session!.sid);
    }
  }

  _reject() {
    LogUtils.d("show_video_call 拒绝:${_session}");
    if (_session != null) {
      _signaling?.reject(_session!.sid);
    }
  }

  _hangUp() {
    LogUtils.d("show_video_call 挂起:${_session}, ${_session?.sid}");
    if (_session != null) {
      _signaling?.bye(_session!.sid);
    }
    _isExist = true;
    Navigator.pop(context);
  }

  _switchCamera() {
    LogUtils.d("show_video_call 切换摄像头:${_session}");
    _signaling?.switchCamera();
  }

  _muteMic() {
    LogUtils.d("show_video_call 切换音频:_signaling = ${_signaling}");
    _signaling?.muteMic();
  }

  enableSpeakerphone() {
    LogUtils.d("show_video_call 外放:_signaling = ${_signaling}");
    _signaling?.enableSpeakerphone();
  }

  @override
  Widget build(BuildContext context) {

    return Scaffold(
      appBar: WnAppBar.getAppBar(context, Text(widget.mediaFlag == CommonUtils.MEDIA_FLAG_VIDEO? '视频通话':'语音通话')),

      floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
      floatingActionButton: _inCalling
          ? SizedBox(
          width: double.infinity,
          child: Row(
              children: <Widget>[
                getSwitchCameraWidget(),
                getHangUpWidget(),
                getMicWidget(),
                getSpeakerWidget(),
              ]))
          : null,
      body: _inCalling?
      OrientationBuilder(builder: (context, orientation) {
        return Container(
          color: Colors.white,
          child: Stack(children: <Widget>[


            Positioned(
                left: 0.0,
                right: 0.0,
                top: 0.0,
                bottom: 0.0,
                child: Offstage(
                  offstage: widget.mediaFlag == CommonUtils.MEDIA_FLAG_VOICE,
                  child: Container(
                    margin: EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 0.0),
                    width: MediaQuery.of(context).size.width,
                    height: MediaQuery.of(context).size.height,
                    child: RTCVideoView(_remoteRenderer, filterQuality: FilterQuality.high,),
                    decoration: BoxDecoration(color: Colors.black54),
                  ),
                ),
            ),


            Positioned(
              left: 20.0,
              top: 20.0,
              child: Offstage(
                offstage: widget.mediaFlag == CommonUtils.MEDIA_FLAG_VOICE,
                child: Container(
                  width: orientation == Orientation.portrait ? 90.0 : 120.0,
                  height:
                  orientation == Orientation.portrait ? 120.0 : 90.0,
                  child: RTCVideoView(_localRenderer, mirror: true, filterQuality: FilterQuality.high,),
                  decoration: BoxDecoration(color: Colors.black54),
                ),
              ),
            ),

            Positioned(
              left: 20.0,
              right: 20.0,
              top: 30.0,
              child: Offstage(
                offstage: widget.mediaFlag == CommonUtils.MEDIA_FLAG_VIDEO,
                child: Container(
                  width: orientation == Orientation.portrait ? 190.0 : 220.0,
                  height:
                  orientation == Orientation.portrait ? 220.0 : 190.0,
                  child: Column(
                    children: [
                      Text("${resultTimer}", style: TextStyle(fontSize: 20, color: Colors.grey.shade500),),
                      SizedBox(height: 40,),
                      CommonAvatarView.showBaseImage(userBean?.avatar??"", 80, 80),
                      SizedBox(height: 8,),
                      Text("${userBean?.nickName}", style: TextStyle(fontSize: 18, color: Colors.black),),
                    ],
                  ),
                ),
              ),
            )


          ]),
        );
      }):_showAcceptWidget(),
    );
  }


  //切换摄像头
  Widget getSwitchCameraWidget(){
    return Expanded(
        child: Container(
          width: 80,
          height: 100,
          child: Column(
            children: [
              FloatingActionButton(
                child: const Icon(Icons.switch_camera),
                onPressed: _switchCamera,
              ),
              SizedBox(height: 10,),
              Text("切换摄像头", style: TextStyle(fontSize: 12, color: Colors.white),),
            ],
          ),
        )
    );
  }

  //挂断
  Widget getHangUpWidget(){
    return Expanded(
        child:Container(
          width: 80,
          height: 100,
          child: Column(
            children: [
              FloatingActionButton(
                child: Icon(Icons.call_end),
                backgroundColor: Colors.pink,
                onPressed: _hangUp,
              ),
              SizedBox(height: 10,),
              Text("挂 断", style: TextStyle(fontSize: 12, color: Colors.white),),
            ],
          ),
        )
    );
  }

  //麦克风
  Widget getMicWidget(){
    return Expanded(
        child: Container(
          width: 80,
          height: 100,
          child: Column(
            children: [
              FloatingActionButton(
                child: Icon(isMic?Icons.mic:Icons.mic_off),
                onPressed: _muteMic,
              ),
              SizedBox(height: 10,),
              Text(isMic?"麦克风已开":"麦克风已关", style: TextStyle(fontSize: 12, color: Colors.white),),
            ],
          ),
        )
    );
  }

  //扬声器
  Widget getSpeakerWidget(){
    return Expanded(
        child:Container(
            width: 80,
            height: 100,
            child: Column(
              children: [
                FloatingActionButton(
                  child: Image.asset(CommonUtils.getBaseIconUrlPng(isSpeaker?"wc_chat_speaker_open":"wc_chat_speaker_close"), width: 28, height: 28,),
                  onPressed: enableSpeakerphone,
                ),
                SizedBox(height: 10,),
                Text(isSpeaker?"扬声器已开":"扬声器已关", style: TextStyle(fontSize: 12, color: Colors.white),),
              ],
            ),
          ));
  }

}

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

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

相关文章

大规模ddos攻击事件,ddos攻击会暴露ip吗

1月4日&#xff0c;谷歌云安全可靠性工程师Damian Menscher在推特上表示&#xff0c;根据CVE-2021-22205漏洞利用报告&#xff0c;有攻击者正在利用 GitLab 托管服务器上的安全漏洞来构建僵尸网络&#xff0c;并发起规模惊人的分布式拒绝服务攻击&#xff08;DDoS&#xff09;其…

产品新人必看:入职前的准备及快速适应产品工作

​我第一次做分享&#xff0c;没有什么经验&#xff0c;不知道能不能讲好。 我先自我介绍一下&#xff0c;我是从UED转产品的&#xff0c;我现在这家公司已经快干了一年了&#xff0c;我是去年的5月份入职的。 求职期间陪学也帮了我很多忙&#xff0c;我基本上是全天有什么问…

线上课和线下课各自优缺点,PLC工程师进阶上位机应该知道

先说线上课优点&#xff1a; 价格较低&#xff0c; 视频学习&#xff0c;可以随时随地学习&#xff0c;不用辞职学习&#xff0c;降低职业风险 缺点&#xff1a; 没有学习氛围&#xff0c;对于自制能力差的同学&#xff0c;很难坚持下去 没有老师指点&#xff0c;一旦遇到问题很…

利用Amber热力学积分计算相对自由能变化

上周四&#xff0c;何博士为大家在北鲲云的直播间分享了Amber热力学积分计算相对自由能变化&#xff08;直播回放可在视频号&#xff1a;北鲲云-直播回放中查看&#xff09;。 直播结束后有很多小伙伴来向我们要PPT资料&#xff0c;这里何博士也为大家准备了文字版本的教程。将…

企业实战!基于Harbor搭建企业镜像仓库

企业实战&#xff01;基于Harbor搭建企业镜像仓库 虽然Docker官方提供了Docker Hub作为公共的Registry服务器&#xff0c;给到用户进行镜像的保存和管理工作。但对于企业而言&#xff0c;考虑到安全性和网络效率等原因&#xff0c;通常会搭建私有的Registry服务器&#xff0c;用…

<C++>深度学习多态

目录 一、概念 二、多态的定义及实现 虚函数重写的两个例外&#xff1a; C11override 和 rinal 三、抽象类 接口继承和实现继承 四、多态的原理 五、单继承和多继承中的虚函数表 六、继承和多态常见的面试题 一、概念 概念&#xff1a;通俗来说&#xff0c;就是多种形…

Redis数据库redisDb源码分析

写在前面 以下内容是基于Redis 6.2.6 版本整理总结 一、组织方式 Redis服务器将所有的数据库 都保存在src/server.h/redisServer结构中的db数组中。db数组的每个entry都是src/server.h/redisDb结构&#xff0c;每个redisDb结构代表一个数据库。Redis默认有16个数据库。 1.1…

TDengine安装使用

引言 近期&#xff0c;听说了时序数据库TDengine&#xff0c;本人的好奇心又出来了&#xff0c;同是时序数据库的InfluxDB不也挺好的嘛&#xff1f;通过一些网上的资料以及些简单的实际操作&#xff0c;本人得出的结论是&#xff1a; 数据量少时&#xff0c;InfluxDB的性能好些…

MCE | TGF-β 信号通路

转化生长因子 (Transforming growth factor beta&#xff0c;TGF-β) 是一类多功能的细胞因子&#xff0c;可由多种组织细胞产生。TGF-β 信号通路是由众多成员的多功能细胞因子&#xff0c;与相应的受体、细胞内信号转导分子组成的通路&#xff0c;能影响疾病发生和发展&#…

win10利用minikube在自己的电脑上搭建k8s

首先默认你的电脑上装了docker&#xff0c;没有的话参考这篇 下面开始步入正题&#xff1a; 步骤讲解 首先下载minikube,点击这个链接&#xff0c;根据自己的环境生成相应的配置命令&#xff0c;我自己的话是64位win10系统&#xff0c;管理员打开cmd运行命令如下&#xff1a…

Flutter高仿微信-第33篇-单聊-图片

Flutter高仿微信系列共59篇&#xff0c;从Flutter客户端、Kotlin客户端、Web服务器、数据库表结构、Xmpp即时通讯服务器、视频通话服务器、腾讯云服务器全面讲解。 详情请查看 效果图&#xff1a; 详情请参考 Flutter高仿微信-第29篇-单聊 &#xff0c; 这里只是提取图片实现的…

更简洁的参数校验,使用 SpringBoot Validation 对参数进行校验

在开发接口时&#xff0c;如果要对参数进行校验&#xff0c;你会怎么写&#xff1f;编写 if-else 吗&#xff1f;虽然也能达到效果&#xff0c;但是不够优雅。 今天&#xff0c;推荐一种更简洁的写法&#xff0c;使用 SpringBoot Validation 对方法参数进行校验&#xff0c;特…

k8s dashboard安装部署实战详细手册

文章目录一、k8s dashboard搭建1.选择版本2.下载yaml3.执行yaml4.访问dashboard5.token登录6.配置权限结尾一、k8s dashboard搭建 1.选择版本 dashboard和k8s存在版本对应关系&#xff0c;具体可以去github查找https://github.com/kubernetes/dashboard/releases 由于我的k8s…

亮相2022南京软博会,创邻科技携Galaxybase图平台展现信创硬核实力

11月23日&#xff0c;2022中国&#xff08;南京&#xff09;国际软件产品和信息服务交易博览会&#xff08;以下简称”软博会“&#xff09;在南京博览中心隆重开幕。此次展会以“软件赋能 数智转型”为主题&#xff0c;由江苏省工业和信息化厅、南京市人民政府、中国工业技术软…

iwebsec靶场 数据库漏洞通关1-MySQL数据库漏洞

iwebsec靶场的数据库漏洞第一项内容为MySQL弱口令漏洞渗透&#xff0c;如下所示我们可以使用kali的msf模块对其进行渗透。 一、获取iwebsec虚拟机环境的MySQL服务映射的端口号 由于iwebsec靶场是通过docker搭建在ubuntu这个宿主虚拟机中 接下来在机器在ubuntu64位这台虚拟机里…

基于JAVA的物流信息管理平台【数据库设计、源码、开题报告】

数据库脚本下载地址&#xff1a; https://download.csdn.net/download/itrjxxs_com/86406113 摘要 随着全球供应链持续受到 COVID-19 的影响&#xff0c;许多物流公司正在考虑如何重构新常态运营环境&#xff0c;实现降本增效。对于业务网点遍布全球的物流公司而言&#xff0…

【看球和学Go】错误和异常、CGO、fallthrough

这篇文章将详解「Go必知必会」的知识点&#xff1a; 错误和异常的对比、发生panic后如何执行代码&#xff1f;会执行到defer代码段吗&#xff1f;CGO是什么&#xff1f;CGO的作用是什么&#xff1f;switch中的fallthrough 错误&异常 错误指的是可能出现问题的地方出现了…

云原生系列 六【轻松入门容器基础操作】

✅作者简介&#xff1a; CSDN内容合伙人&#xff0c;全栈领域新星创作者&#xff0c;阿里云专家博主&#xff0c;华为云享专家博主&#xff0c;掘金后端评审团成员 &#x1f495;前言&#xff1a; 最近云原生领域热火朝天&#xff0c;那么云原生是什么&#xff1f;何为云原生&a…

深度学习第一次作业 - 波士顿房价预测

文章目录划分训练集和测试集建立线性回归模型特征选择重建模型尝试使用GradientBoostingimport pandas as pd import numpy as np import seaborn as sns from sklearn.linear_model import LinearRegression from sklearn.model_selection import train_test_split from sklea…

第一个SpringBoot项目的创建

目录 一、SpringBoot是什么&#xff1f; 初识springboot springboot的优点 二、SpringBoot项目的创建与简单运行 &#x1f351;使用idea创建springboot项目 &#x1f351; Spring Boot 项目目录介绍 &#x1f351;springboot项目的简单运行与使用 一、SpringBoot是什么&a…