6、Flutterr聊天界面网络请求

一、准备网络数据

1.1 数据准备工作

  1. 来到网络数据制造的网址,注册登录后,新建仓库,名为WeChat_flutter;
  2. 点击进入该仓库,删掉左侧的示例接口,新建接口.

3. 接着点击右上角‘编辑’按钮,新建响应内容,类型为Array,一次生成50条

4. 点击chat_list左侧添加按钮,新建chat_list中的数据内容,此时用到一个获取随机头像的网站.到该网站中,随机复制一个图片地址,假设为:https://randomuser.me/api/portraits/women/35.jpg.将数据填上,然后保存.

5.接下来,我们想让获取的图像是个随机值,那么参考Mock.js网站中的生成规则.

6.接着回到响应内容这里,通过设置初始值规则,生成随机的图片地址.

  • https://randomuser.me/api/portraits/women/@natural(10,80).jpg

  • 接下来填充名称,消息的随机值.

  • 综上:服务器的数据准备工作完成,请求的链接地址

二、聊天界面导航条

  • 首先设置_RootPageState中进入App默认选中的NavigationBar的私有变量_currentIndex = 0,也就是默认选中微信界面.
  • 然后根据微信聊天界面的UI效果,我们先实现右上角的加号.

1. AppBar的actions就是我们需要添加操作的地方.

  • 按照这个思路继续,就需要自己实现一个弹出菜单的组件.然而这个时候,Flutter其实已经提供了一套成熟的控件以达到效果

2. PopupMenuButton组件

  • PopupMenuButton组件用来弹出一个菜单,必传参数为itemBuilder,用来实现它需要展示的内容.PopupMenuItem就是用来展示内容的类.PopupMenuButton有个onSelected属性,这个属性是个闭包,意思是选中某个PopupMenuItem的时候,会调用这个闭包.但是有个前提就是每个PopupMenuItem的value必须不为null的时候,才会执行onSelected闭包.
  • AppBar中具体内部实现为.
AppBar(
  //去除导航条黑线
  elevation: 0.0,
  backgroundColor: WeChatThemeColor,
  //设置标题默认居中、否则双端默认方式不一致
  centerTitle: true,
  title: const Text("微信", style: TextStyle(color: Colors.black),),
  actions: [
    Container(
      margin: EdgeInsets.only(right: 10),
      child: PopupMenuButton(
        onSelected: (item){
          print(item);
        },
        onCanceled: (){
          print('onCanceled');
        },
        //PopupMenuButton的背景颜色
        color: Colors.black,
        offset: Offset(0,60),
        child: Image(image: AssetImage('images/圆加.png'),width: 25,height: 25,),
        itemBuilder: (BuildContext context){
          return <PopupMenuItem>[
            _buildMenuItem('images/发起群聊.png','发起群聊'),
            _buildMenuItem('images/添加朋友.png','添加朋友'),
            _buildMenuItem('images/扫一扫1.png','扫一扫'),
            _buildMenuItem('images/收付款.png','收付款'),
          ];
        },
      ),
    )
  ],
),

3. 其中_buildMenuItem是我们封装的一个创建组件的方法,内部实现为

PopupMenuItem _buildMenuItem(String imageName,String title){
  return PopupMenuItem(
    value: {
      'imageName' : imageName,
      'title' : title,
    },
      child: Row(
        children: [
          Image(image: AssetImage(imageName), width: 25,),
          SizedBox(width: 10,),
          Text(title, style: TextStyle(color: Colors.white),),
        ],
      )
  );
}

4.关于PopupMenuButton背景颜色的设置.可以直接在其内部设置背景颜色.也可以在ThemeData中设置app的cardColor.但是优先级没有直接设置PopupMenuButton的高.如果不设置黑色背景颜色,弹出的视图显示均为白色,看不到UI效果.

三、请求网络数据

  1. 通过Dart packages 这个网站可以搜索flutter使用的包packages.我们使用http这个包来请求我们的网络数据.这个包是flutter官方提供的.实际项目开发的时候可能并不会使用http这个包,大部分是使用dio来请求网络数据.这里只介绍官方的http包如何使用.
  2. 导入http包.在名称后可以点击复制包名.

3.在项目的pubspec.yaml中粘贴复制的包名.

4. 粘贴完之后需要Pub get获取一下,获取包对应的代码.

  • 也可以在终端输入flutter packages get 来获取.

5.在chat_page.dart中导入http包并取别名

import 'package:http/http.dart' as http;

6.在渲染状态组件的时候发起网络请求,也就是在initState中发起网络请求.getData后采用async表示异步执行.async需要搭配await使用,await后面跟着的是耗时的代码,所以会异步执行调用.

class _ChatPageState extends State<ChatPage> {
    .....
    @override
    void initState() {
      super.initState();
      getDatas();
    }
    getDatas() async {
      var response = await http.get(Uri.parse('http://rap2api.taobao.org/app/mock/311243/api/chat/list'),);
      print(response.statusCode);//200
      print(response.body);//这里就是我们自定义的网络数据了
    }
    .....
}
  • 点击其他界面再次回到聊天界面会发现initState方法重新走了一遍,调用了网络请求,这是因为我么还没有保存住状态.后面将讲述如何保存Widget的状态.

7.处理返回数据

  • 首先介绍一下,在flutter中如何将请求返回的JSON数据转为Map,在我们iOS开发中是转为字典,而flutter中没有字典这个类型,对应的类型是Map.以及如何将Map转为JSON.在iOS中我们会使用一个NSJSONSerialization的类用来处理JSON数据.同样的,在flutter中也会有一个专门的类JsonCode来处理.

JSON和Map互相转换

  • 首先需要导入dart中的convert组件.
  • 然后我们写点测试用例,熟悉它的使用方式.
void initState() {
  super.initState();
  getDatas();
  final chat = {
    'name': '张三',
    'message': '在干嘛?',
  };
  //Map转JSON
  final jsonChat = json.encode(chat);
  print(jsonChat);
  //JSON转Map
  final mapChat = json.decode(jsonChat);
  print(mapChat);
  print(mapChat is Map);
}
  • 返回结果如下:
flutter: {"name":"张三","message":"在干嘛?"}
flutter: {name: 张三, message: 在干嘛?}
flutter: true
flutter: 200
  • 其中的json就是JsonCodec的实例. 'is'是用来判断是不是某个类型.

8. 新建聊天模型

  • 因为网络出来的数据可能为空,那么就需要用?来修饰定义的属性;
class Chat {
      final String? name;
      final String? message;
      final String? imageUrl;
      Chat(this.name,this.message,this.imageUrl);
      //工厂方法,用来初始化对象.
     factory Chat.fromJson(Map json){
       return Chat(json['name'],json['message'],json['imageUrl']);
     }
}
  • factory 关键字用来标记当前是工厂方法,是设计模式的一种,用来初始化对象.除了默认的构造方法,还可以使用这个工厂方法来实例化一个Chat对象.模型建立好了之后就可以处理响应的数据.
    • 如下: 模型数据成功转换.
//将json转为Chat模型
final chatModule = Chat.fromJson(mapChat);
print('name:${chatModule.name} message:${chatModule.message}');// name:张三 message:在干嘛? 

9.处理响应的数据

  • 首先我们会获取到通过网络接口获取的列表数据,但是不能保证网络请求一定会发送成功.所以要处理一些错误情况.在flutter中引入Future.表示接下来请求的数据,可能有值也可能没有值,一般与网络请求配合使用.
  • 因此返回值我们可以设定为.
Future<List<Chat>?> getDatas() async {}
  • 对于异常情况的处理,可以通过throw Exception的形式.
Future<List<Chat>?> getDatas() async {
  final response = await http.get(Uri.parse('http://rap2api.taobao.org/app/mock/311243/api/chat/list'));
  print(response.statusCode);
  if (response.statusCode == 200) {

  } else {
    throw Exception('statusCode: ${response.statusCode}');
  }
}
  • 接下来我们处理返回的body中的数据
    • 获取响应数据,并且转换为Map类型
//获取响应数据,并且转换成Map类型
final responseBody = json.decode(response.body);
//转换模型数组
responseBody['chat_list'].map(
    (item) {
      print(item);
      return item;
    }
);
  • 这样可以看到item的遍历数据.
flutter: {imageUrl: https://randomuser.mflutter: {imageUrl: https://randomuser.me/api/portraits/women/12.jpg, name: 黎超, message: 音和委起度明条部过们放省。们区以号还九保把王之候包与先件能议清。江知天能能五开比点别增石次米五平。极养提立手专把示低率号容眼组是石。离维照联子象派三热始受构参元离还。相电构次色影件力计面进东把。}
flutter: {imageUrl: https://randomuser.me/api/portraits/women/23.jpg, name: 傅秀兰, message: 但保写太满果此力少合反压色生太个图。制社并更个构北不张需国些清不。没八你或况铁员三时划志有改题头感。值年改你要变程新但八传织。进化林号中不按亲天张原美多。}e/api/portraits/women/37.jpg, name: 李丽, message: 可组品且发铁直报表状传素安小全。器音天石别数业局装共习清。加然处进派变装你农速约部族利音次层。毛得理状主质所局等工型即天研走机段。}
    • 接下来将其返回结果直接遍历为模型返回为List
final responseBody = json.decode(response.body);
//转换模型数组
List<Chat>chatList = responseBody['chat_list'].map<Chat>(
    (item) {
      return Chat.fromJson(item);
    }
).toList();
print(chatList);
return chatList;
  • 此时我们可以看到输出均为实例对象
[Instance of 'Chat', Instance of 'Chat', Instance of 'Chat', Instance of 'Chat', Instance of 'Chat',...] 
  • 上述可以直接采用箭头函数.这样我们就实现了响应数据的模型转换.
List<Chat>chatList = responseBody['chat_list'].map<Chat>((item) => Chat.fromJson(item)).toList();
return chatList;

10.处理网络请求的结果

  • 接下来我们要处理返回Future类型的异步网络请求结果.
    • 可以采用try...catch
    • 也可以采用then结合的形式
  • 这里我们使用then的方式处理结果, 输出其中的value.
loadData(){
  getDatas().then((value) {
    print(value);
  });
}
    • 这里可以看到value的结果就是我们的chatList数据.
  • 其实我们还可以采用一种更为简单的方式处理网络请求.在下一章讲解.

四、利用FutureBuilder渲染微信界面

  • 在flutter中渲染网络数据专门有个控件叫做FutureBuilder.当无数据时展示默认界面,有数据时继续渲染网络请求下来的数据

  • 此时可以看到返回的结果中 先是null、然后再是连续几次的数据.

  1. 此时我们可以通过snapshot的异步连接状态来查看.
  • 也就是snapshot.connectionState
    • 当处于waiting状态时,data会返回null
    • 当处于done的状态时,data会返回正常解析结果.
flutter: data: null
flutter: state:ConnectionState.waiting
flutter: data: [Instance of 'Chat', Instance of 'Chat', Instance of 'Chat', ...]
flutter: state:ConnectionState.done

    2. 所以这个时候我们可以借助于ConnectionState状态来确定当前要渲染的界面.

    • 当waiting状态时,展示一个Loading...
    • 当done状态时,渲染界面.
  • 因此我们的FutureBuilder的渲染实现部分为
FutureBuilder(
  future: getDatas(),
  builder: (BuildContext context, AsyncSnapshot snapshot){
     //无数据时渲染默认界面,有数据时显示网络数据
    print('state:${snapshot.connectionState}');
    if (snapshot.connectionState == ConnectionState.waiting) {
      return Center(child: Text('Loading...'),);
    } else {
      return ListView(
        children: snapshot.data.map<Widget>((item){
            return ListTile(
              //右侧 标题
              title: Text(item.name),
              //右侧 子标题
              subtitle: Container(
                height: 20,width: 20,
                //TextOverflow.ellipsis 展示不下的时候省略号
                child: Text(item.message,overflow: TextOverflow.ellipsis,),
              ),
              //左侧:圆型头像
              leading: CircleAvatar(
                backgroundImage: NetworkImage(item.imageUrl),
              ),
            );
        }).toList(),
      );
    }
  },
)
  • 展示的效果图如下:

  • 但是这样的渲染方式不是最好的.因为每次进入微信界面就需要发送网络请求.每次都要重新渲染.
    • 那么这种FutureBilder直接渲染布局的方式只能应用于数据相对简单的界面.
  • 也可以将请求下来的数据放入一个缓存模型数组中,当builder的时候再从模型数组中拿取使用.

五、网络请求的处理

  1. 这里我们将网络请求的数据进行处理.首先来到 _ChatPageState中,创建一个缓存数组
List<Chat> _datas = [];

      2. 在loadData时处理返回的数据.

  • 通过then将正常返回的数据赋值给缓存,然后在setState方法中实现这个赋值操作,就会引起界面的渲染.
  • 将错误的返回结果通过日志输出.
  • 会有类型不匹配的错误: 将datas设置为可空类型,然后在赋值的时候对空的情况进行空处理就行.
loadData(){
  //当数据正常返回的时候
  getDatas().then((List<Chat>? datas) {
     setState(() {
       _datas = datas ?? [];
     });
  }).catchError((err){
    print(err);
  });
}

3. 这个时候通过缓存的数据来渲染界面

  • Scaffold中body的FutureBuilder替换回Container.通过三目运算符判断当前缓存数组中是否有值,如果没有,就设置Loading.如果有值就渲染界面
Container(
  child: _datas.length == 0 ?
  Center(child: Text('Loading...'),)
      : ListView.builder(
      itemCount: _datas.length,
      itemBuilder: (BuildContext context, int index) {
        return ListTile(
          title: Text(_datas[index].name ?? ""),
          subtitle: Container(height: 20,width: 20,child: Text(_datas[index].message ?? ""),),
          leading: CircleAvatar(
            backgroundImage: NetworkImage(_datas[index].imageUrl ?? ""),
          ),
        );
    }),
),
  • 点击其他NavigationBar界面,再点回微信界面,会发现界面的渲染过程.

4. 关于Future请求结果的处理完善

  • Future请求结果的处理方面,除了catchError之外,还有whenComplete、timeout
loadData(){
  //当数据正常返回的时候
  getDatas().then((List<Chat>? datas) {
     setState(() {
       _datas = datas ?? [];
     });
  }).catchError((err){
    print(err);
  }).whenComplete(() {
    //数据处理完毕
    print("完毕");
  }).timeout(Duration(milliseconds: 10)).catchError((timeout){
    print("加载超时 ${timeout}");//flutter: 加载超时 TimeoutException after 0:00:00.010000: Future not completed
  });
}
  • 在这里,超时并不意味着请求结束,当请求结果返回之后,仍然调用了请求完毕.
  • 在这里,我们我们需要处理: 一旦请求发出,除非异常,否则超时后其他的数据都不应该返回去调用我们的setState渲染界面.

5. 请求超时的异常处理/多次重复刷新

  • 设置一个私有bool变量_cancelConnect, 如果当前的标记不为true,那么再去setState渲染界面.避免了数据污染.

  • 这个时候我们发现界面和通讯录界面都存在一种现象:当我们点击其他界面,再回到当前界面时,数据会重新加载.这个现象说明了我们需要进行状态的保存以避免重复刷新.接下来将对这个现象进行处理.

六、保存Widget的状态

关于状态的保存,这个时候我们引入另一个概念: 混入(Mixins),用来给类增加功能,是多继承模式下的一种代码复用.使用with关键字来实现混入一个或多个类.

  1. 因为flutter渲染效率非常高,当控件不在界面上展示的话就会被销毁,再次展示时会重新渲染.
  2. 如果我们的状态需要保存的话,就需要混入一个类:AutomaticKeepAliveClientMixin,也就是对ChatPage的延展.重写wantKeepAlive属性并且在build渲染方法中调用父类的渲染方法.
//AutomaticKeepAliveClientMixin让当前界面保存状态
class _ChatPageState extends State<ChatPage> with AutomaticKeepAliveClientMixin<ChatPage>{
.....
//保留setState状态
@override
bool get wantKeepAlive => true;
@override
Widget build(BuildContext context) {
  //6.3 重写父类渲染方法
  super.build(context);
  ...
}
...
}

3. 同理我们去保存通讯录界面的状态.

4. 这个时候我们去检测设置的效果,发现貌似并没有设置成功.还是会重新渲染对应的界面.是否是设置失效了呢?

  • 其实不是.这个时候就需要考虑我们的根视图设置的问题了.因为之前预埋了坑点.

5. 回到rootpage.dart中,我们发现在设置当前显示body的时候,采用的是从_pages数组中获取对应的界面.在iOS中,这样设置并没有什么问题.

  • 但是在flutter中,这样设置,每次根据_currentIndex获取的视图显示对象并不是数组中设置的对象.
    • 因为在flutter中,在数组中创建的对象对于flutter来说只是一堆数据,通过build方法渲染到界面上.
    • 在build中有一个小部件树,这个小部件树是从MyApp开始build渲染,然后是它的home属性包含的RootPage对象,再通过RootPage中的设置的Container,去包含的ChatPage、FriendsPage等
MyApp => RootPage => Container => ChatPage/FriendsPage/DiscoverPage/MinePage
  • 一旦当前_currentIndex改变之后,那么设置的body就从_pages中获取到对应的界面对象.然而就是这个操作导致.当前Container包含的Page从一个切换到另一个,之前的界面就不在渲染树中.也就是被销毁了.

6. 回到我们在微信界面/通讯录界面混入AutomaticKeepAliveClientMixin这个类的目的:

    • 让小部件树之外指定的界面不要被销毁.也就是我们想要保留根视图下四个界面状态.当其中一个渲染在小部件树中的时候,另外三个不要被销毁.
  • 为了解决这个问题,我们引入flutter中另外一个控件PageController.

7. PageController设置rootpage根视图界面.

  • 首先创建一个私有变量PageController _controller, 并将初始界面page设置为第一个界面.
final PageController _controller = PageController(
   //初始显示的界面索引
   initialPage: 0,
);
  • 其次将Container中Scaffold的body设置为PageView
    • onPageChanged就是我们需要设置index改变的回调的地方
    • NeverScrollableScrollPhysics可以去除默认的左右滚动的效果.
PageView(
  onPageChanged: (int index ){
    _currentIndex = index;
    setState(() {
    });
  },
  //如果不设置这个属性,那么根视图事件可以左右滚动切换.
  physics: NeverScrollableScrollPhysics(),
  controller: _controller,
  children: [
    ChatPage(),
    FriendsPage(),
    DiscoverPage(),
    MinePage()
  ],
),
  • 综上:做完这些操作,我们再去点击任意界面,之前的状态就会被保留下来.

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

PAT A1032 Sharing

1032 Sharing 分数 25 作者 CHEN, Yue 单位 浙江大学 To store English words, one method is to use linked lists and store a word letter by letter. To save some space, we may let the words share the same sublist if they share the same suffix. For example, l…

如何利用ChatGPT进行论文润色-ChatGPT润色文章怎么样

ChatGPT润色文章怎么样&#xff1f; ChatGPT可以润色文章&#xff0c;使用其润色功能可以为用户提供更加整洁、清晰、文采动人的文本。但需要注意以下几点&#xff1a; 需要保持文本的一致性和完整性。当使用ChatGPT进行润色时&#xff0c;需要注意保持文本的一致性和完整性。…

和月薪5W的聊过后,才发现自己一直在打杂···

前几天和一个朋友聊面试&#xff0c;他说上个月同时拿到了腾讯和阿里的offer&#xff0c;最后选择了阿里。 我了解了下他的面试过程&#xff0c;就一点&#xff0c;不管是阿里还是腾讯的面试&#xff0c;这个级别的程序员&#xff0c;都会考察项目管理能力&#xff0c;并且权重…

Java程序设计入门教程---循环结构(while)

目录 思考 概念 语法 案例&#xff1a;求1到100的整数和&#xff1f; 案例分析 思考 1. 让你输出10000000000000000句“Hello,world!”&#xff0c;你怎么写代码&#xff1f; 2. 求1到100的整数和&#xff1f; 概念 循环结构程序多次循环执行相同或相近的任务。 while循环…

Windows在外远程桌面控制macOS 【macOS自带VNC远程】

文章目录 前言1.测试局域网内远程控制1.1 macOS打开屏幕共享1.2 测试局域网内VNC远程控制 2. 测试公网远程控制2.1 macOS安装配置cpolar内网穿透2.2 创建tcp隧道&#xff0c;指向5900端口 3. 测试公网远程控制4. 配置公网固定TCP地址4.1 保留固定TCP地址4.2 配置固定TCP端口地址…

什么?Python一行命令快速搭建HTTP服务器并公网访问?

文章目录 1.前言2.本地http服务器搭建2.1.Python的安装和设置2.2.Python服务器设置和测试 3.cpolar的安装和注册3.1 Cpolar云端设置3.2 Cpolar本地设置 4.公网访问测试5.结语 转载自远程内网穿透的文章&#xff1a;【Python】快速简单搭建HTTP服务器并公网访问「cpolar内网穿透…

springboot第19集:权限

article 文章表sys_permission 后台权限表sys_role 后台角色表sys_role_permission 角色-权限关联表sys_user 用户表sys_user_role 用户-角色关联表 image.png image.png sys_user_role id user_id(用户id) role_id(角色id) sys_role id role_name(角色名) create_time(创建时间…

基于 EKS Fargate 搭建微服务性能分析系统

背景 近期 Amazon Fargate 在中国区正式落地&#xff0c;因 Fargate 使用 Serverless 架构&#xff0c;更加适合对性能要求不敏感的服务使用&#xff0c;Pyroscope 是一款基于 Golang 开发的应用程序性能分析工具&#xff0c;Pyroscope 的服务端为无状态服务且性能要求不敏感&…

部署simple-chat项目

simple-chat介绍&#xff1a;此项目是基于openAI3.5模型的h5端人工智能聊天项目&#xff0c;无需翻墙即可体验。 simple-chat线上地址&#xff1a;simple-chat simple-chat项目地址&#xff1a;GitHub - AMxiaoming/simple-chat nginx部署前端步骤&#xff1a; https://blo…

MySQL基础(十八)MySQL8其它新特性

1. MySQL8新特性概述 MySQL从5.7版本直接跳跃发布了8.0版本&#xff0c;可见这是一个令人兴奋的里程碑版本。MySQL 8版本在功能上做了显著的改进与增强&#xff0c;开发者对MySQL的源代码进行了重构&#xff0c;最突出的一点是MySQL Optimizer优化器进行了改进。不仅在速度上得…

HashSet和HashMap内部结构分析

首先明确一点&#xff1a;HashSet的底层就是HashMap HashSet与HashMap的不同点&#xff1a; HashMap存储的是键值对&#xff08;也就是key-value&#xff09;&#xff0c;即在调用HashMap的put方法时传入的两个值&#xff0c;而HashSet其实也是存储的键值对&#xff0c;但是键…

阿里云服务器镜像怎么选?操作系统版本选择说明

阿里云服务器镜像怎么选择&#xff1f;云服务器操作系统镜像分为Linux和Windows两大类&#xff0c;Linux可以选择Alibaba Cloud Linux&#xff0c;Windows可以选择Windows Server 2022数据中心版64位中文版&#xff0c;阿里云百科来详细说下阿里云服务器操作系统有哪些&#xf…

【sop】基于灵敏度分析的有源配电网智能软开关优化配置(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

从爆火的“哇呀挖”,思考我软件开发的人生意义何在?

【 在什么样的花园里面&#xff0c;挖呀挖呀挖&#xff0c;种什么样的种子&#xff0c;开什么样的花&#xff0c;在小小的花园里面&#xff0c;挖呀挖呀挖&#xff0c;种小小的种子&#xff0c;开小小的花&#xff0c;在大大的花园里面&#xff0c;挖呀挖呀挖&#xff0c;种大大…

国内GPU渲染农场有哪些值得推荐?

GPU凭借它在图形渲染领域强大的架构和计算能力&#xff0c;给广大用户带来了一种更为高效的解决方案&#xff0c;我们启用GPU渲染加速&#xff0c;实际就是调用GPU加速图形的渲染和填充。既然聊到GPU渲染&#xff0c;CG行业的朋友们肯定也好奇国内值得推荐的GPU渲染农场有哪些&…

​射频PCB 设计​的六大条技巧

即使是最自信的设计人员&#xff0c;对于射频电路也往往望而却步&#xff0c;因为它会带来巨大的设计挑战&#xff0c;并且需要专业的设计和分析工具。这里将为您介绍六条技巧&#xff0c;来帮助您简化任何射频PCB 设计任务和减轻工作压力&#xff01; 1、保持完好、精确的射频…

Android网络代理原理及实现

网络代理简介 代理典型的分为三种类型&#xff1a; 正向代理 缓存服务器使用的代理机制最早是放在客户端一侧的&#xff0c;是代理的原型&#xff0c;称为正向代理。其目的之一 是缓存&#xff0c;另一目的是用来实现防火墙&#xff08;阻止互联网与公司内网之间的包&#x…

第十二章_Redis单线程 VS 多线程

Redis为什么选择单线程&#xff1f; 是什么 这种问法其实并不严谨&#xff0c;为啥这么说呢? Redis的版本很多3.x、4.x、6.x&#xff0c;版本不同架构也是不同的&#xff0c;不限定版本问是否单线程也不太严谨。 1 版本3.x &#xff0c;最早版本&#xff0c;也就是大家口口相…

Day965.从持续集成到持续部署 -遗留系统现代化实战

从持续集成到持续部署 Hi&#xff0c;我是阿昌&#xff0c;今天学习记录的是关于从持续集成到持续部署的内容。 只有做好任务分解和小步提交&#xff0c;才能放心大胆地 PUSH 代码&#xff0c;触发持续构建&#xff1b; 只有通过质量门禁&#xff0c;才能得到一个有信心的制…

( 位运算 ) 268. 丢失的数字 ——【Leetcode每日一题】

❓268. 丢失的数字 难度&#xff1a;简单 给定一个包含 [0, n] 中 n 个数的数组 nums &#xff0c;找出 [0, n] 这个范围内没有出现在数组中的那个数。 示例 1&#xff1a; 输入&#xff1a;nums [3,0,1] 输出&#xff1a;2 解释&#xff1a;n 3&#xff0c;因为有 3 个数…