Android --- 消息机制与异步任务

在Android中,只有在UIThread(主线程)中才能直接更新界面,

在Android中,长时间的工作联网都需要在workThread(分线程)中执行

在分线程中获取服务器数据后,需要立即到主线程中去更新UI来显示数据,

所以,如何实现线程间的通信(消息机制)

消息机制原理

消息机制原理 尚硅谷

handler发送消息到 MessageQueue 消息队列中,消息队列内部是一个链表的结构,Looper会取出消息队列中待处理的消息,调用handler的dispatchMessage()分发消息,handler中处理消息的方法有三种,最常用的是第三种

Message的callback

handler的callback

handler的handleMessage方法

消息机制相关API

Message 消息

可理解为线程间通信的数据单元,可通过message携带需要的数据

创建对象:Message.obtain()、new Message()

Message.obtain()使用了Message中的消息池,比直接new一个对象更高效

常用参数:

  • what: id标识,用以区分来自哪个线程
  • arg1/arg2: 子线程需要向主线程传递整型参数
  • obj: Object 任意对象

Handler 处理器

handler并不是只能用在处理message上,定时循环调度等工作也能使用它。

Handler 是Message 的处理器,也负责消息的发送和移除工作。

  • 发送即时消息 
public final boolean sendMessage(@NonNull Message msg),实际上调用了sendMessageDelayed()发送一个延迟时间为0的消息
public final boolean sendMessage(@NonNull Message msg) {return sendMessageDelayed(msg, 0);}public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {if (delayMillis < 0) {delayMillis = 0;}return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);}
  • 发送延时消息,并不是指延时发送,而是延时处理
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis)
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {if (delayMillis < 0) {delayMillis = 0;}return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);}
  • 发送空消息
public final boolean sendEmptyMessage(int what){return sendEmptyMessageDelayed(what, 0);}public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {Message msg = Message.obtain();msg.what = what;return sendMessageDelayed(msg, delayMillis);}public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {if (delayMillis < 0) {delayMillis = 0;}return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);}

不管是发送什么消息,最后都要调用 sendMessageAtTime 方法

public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {MessageQueue queue = mQueue;if (queue == null) {RuntimeException e = new RuntimeException(this + " sendMessageAtTime() called with no mQueue");Log.w("Looper", e.getMessage(), e);return false;}return enqueueMessage(queue, msg, uptimeMillis);}private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,long uptimeMillis) {msg.target = this;msg.workSourceUid = ThreadLocalWorkSource.getUid();if (mAsynchronous) {msg.setAsynchronous(true);}return queue.enqueueMessage(msg, uptimeMillis);}
  • 处理消息 (回调方法) 在主线程中执行
public interface Callback {/*** @param msg A {@link android.os.Message Message} object* @return True if no further handling is desired*/boolean handleMessage(@NonNull Message msg);}
  • 移除未处理消息,比如发送的延时消息:让消息队列调用 removeMessages 方法移除消息
    public final void removeCallbacks(@NonNull Runnable r) {mQueue.removeMessages(this, r, null);}

MessageQueue 消息队列

它是一个按Message的when(被处理时间)排序的优先级队列,用来存放通过 Handler 发送的消息。

实例

创建Handler对象,并重写 handleMessage 方法

在主/分线程创建Message对象,利用handler对象发送消息

在 handleMessage 中处理消息

  • 创建Handler对象,并重写 handleMessage 方法 

只要 handle 发了消息,就一定会触发 handleMessage 方法,并传入一个 Message 对象,根据Message 对象的what属性判断属于哪个线程。

public class HandleActivity extends AppCompatActivity {TextView textView ;String httpDate = "";@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_handle);}// 利用 handller 处理// 1.实例化一个 HandlerHandler handler = new Handler(// 只要 handle 发了消息,就一定会触发 handleMessage 方法,并传入一个 Message 对象new Handler.Callback() {@Override// 3.由handle对象接收消息并处理,在Handler的内部类中处理public boolean handleMessage(@NonNull Message msg) {Log.e("handler 处理消息", "这是handler发送的消息,此时是空");// 根据 Message 的 what 属性,区分来源于哪个线程if (msg.what == 1) {textView1 = findViewById(R.id.t1);textView1.setText(httpDate);} else if (msg.what == 2) {textView2 = findViewById(R.id.t2);textView2.setText(httpDate);} else if (msg.what == 3) {textView3 = findViewById(R.id.t3);textView3.setText("msg.what = "+msg.what+"\n"+msg.obj.toString());}return false;}});
}
  • 在主/分线程创建Message对象,利用handler对象发送消息

当消息没有包含数据时,可以使用handler.sendEmptyMessage(int what) 发送一个空消息

new Thread() {@Overridepublic void run() {super.run();getHttp();// 当 handler 需要传送数据时,需要使用到 Message,使用 sendMessage 方法Message message = new Message();message.what = 3;DataBean dataBean = new DataBean(1,"157181@qq.com","ybr","scl","asiudh");message.obj = dataBean;handler.sendMessage(message);}}.start();
  • 设置一个网络操作方法 
 // 网络操作private void getHttp() {try {// 1. 实例化一个 URL 对象URL url = new URL("https://reqres.in/api/users");// 2. 获取 HttpURLConnection 实例,使用URL的HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();// 3. 设置请求相关属性httpURLConnection.setRequestMethod("GET"); // 请求方法httpURLConnection.setConnectTimeout(6000); // 超时时间// 4. 获取响应码 200 成功 404 未请求到指定资源 500 服务器异常(此时已经发起请求)// 5. 判断响应码并获取响应数据(响应的正文)if (httpURLConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {InputStream inputStream = httpURLConnection.getInputStream(); // 获取了服务器返回的输入流byte[] bytes = new byte[1024]; // bytes 数组,每次最多可以存放 1024 个字节ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();// 存储从输入流中读取的数据int len = 0;while ((len = inputStream.read(bytes)) > -1) {// 将字节数组中的内容写入到缓存流/* 参数1:要存入的字节数组* 参数2:起点* 参数3:要存入的长度*/byteArrayOutputStream.write(bytes, 0, len);}httpDate = new String(byteArrayOutputStream.toByteArray());Log.e("GET返回的数据", httpDate);}} catch (ProtocolException ex) {throw new RuntimeException(ex);} catch (MalformedURLException ex) {throw new RuntimeException(ex);} catch (IOException ex) {throw new RuntimeException(ex);}}

 全部代码

package com.example.androidstudiostudy;import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import com.example.androidstudiostudy.data.DataBean;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;public class HandleActivity extends AppCompatActivity {TextView textView1, textView2, textView3;String httpDate = "";// 利用 handller 处理// 1.实例化一个 HandlerHandler handler = new Handler(// 只要 handle 发了消息,就一定会触发 handleMessage 方法,并传入一个 Message 对象new Handler.Callback() {@Override// 3.由handle对象接收消息并处理,在Handler的内部类中处理public boolean handleMessage(@NonNull Message msg) {Log.e("handler 处理消息", "这是handler发送的消息,此时是空");// 根据 Message 的 what 属性,区分来源于哪个线程if (msg.what == 1) {textView1 = findViewById(R.id.t1);textView1.setText(httpDate);} else if (msg.what == 2) {textView2 = findViewById(R.id.t2);textView2.setText(httpDate);} else if (msg.what == 3) {textView3 = findViewById(R.id.t3);textView3.setText("msg.what = "+msg.what+"\n"+msg.obj.toString());}return false;}});@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_handle);}// 此时有两个线程,这两线程发送的消息都能被 Handle接收,但如何区分不同线程发送的消息从而做不同处理呢?public void getDate(View view) {int id = view.getId();// 第一个线程if (id == R.id.handle1) {new Thread() {@Overridepublic void run() {super.run();getHttp();// 此时会报错:Only the original thread that created a view hierarchy can touch its views./*TextView textView = findViewById(R.id.t1);textView.setText(httpDate);*/// 解决办法 1 runOnUiThread 方法 ==> 相当于在主线程中跑 (初学阶段)/*runOnUiThread(new Runnable() {@Overridepublic void run() {TextView textView = findViewById(R.id.t1);textView.setText(httpDate);}});*/// 解决办法 2 利用 Handle 处理// 2.在子线程中发送(空)消息,此时发送的是空消息handler.sendEmptyMessage(1);}}.start();}// 第二个线程else if (id == R.id.handle2) {new Thread() {@Overridepublic void run() {super.run();getHttp();httpDate = httpDate + "\n第2个线程";handler.sendEmptyMessage(2);}}.start();}// 第三个线程else if (id == R.id.handle3) {new Thread() {@Overridepublic void run() {super.run();getHttp();// 当 handler 需要传送数据时,需要使用到 Message,使用 sendMessage 方法/* 1. 实例化一个 Message* 2. 参数** what:用于区分 handler 发送消息的不同线程来源** arg1/arg2: 子线程需要向主线程传递整型参数** obj: Object 任意对象*/Message message = new Message();message.what = 3;DataBean dataBean = new DataBean(1,"157181@qq.com","ybr","scl","asiudh");message.obj = dataBean;handler.sendMessage(message);}}.start();}}// 网络操作private void getHttp() {try {// 1. 实例化一个 URL 对象URL url = new URL("https://reqres.in/api/users");// 2. 获取 HttpURLConnection 实例,使用URL的HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();// 3. 设置请求相关属性httpURLConnection.setRequestMethod("GET"); // 请求方法httpURLConnection.setConnectTimeout(6000); // 超时时间// 4. 获取响应码 200 成功 404 未请求到指定资源 500 服务器异常(此时已经发起请求)// 5. 判断响应码并获取响应数据(响应的正文)if (httpURLConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {InputStream inputStream = httpURLConnection.getInputStream(); // 获取了服务器返回的输入流byte[] bytes = new byte[1024]; // bytes 数组,每次最多可以存放 1024 个字节ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();// 存储从输入流中读取的数据int len = 0;while ((len = inputStream.read(bytes)) > -1) {// 将字节数组中的内容写入到缓存流/* 参数1:要存入的字节数组* 参数2:起点* 参数3:要存入的长度*/byteArrayOutputStream.write(bytes, 0, len);}httpDate = new String(byteArrayOutputStream.toByteArray());Log.e("GET返回的数据", httpDate);}} catch (ProtocolException ex) {throw new RuntimeException(ex);} catch (MalformedURLException ex) {throw new RuntimeException(ex);} catch (IOException ex) {throw new RuntimeException(ex);}}
}

Looper 循环器

  • 负责循环取出 MessageQueue 中当前需要处理的Message
  • 交给对应的handler进行处理
  • 处理完后,将Message缓存到消息池中,以备复用

每个线程都可以有一个关联的Looper对象,用于处理该线程的消息队列。主要功能是接收来自消息队列的消息并将其分发给对应的Handler处理。

在Android中,主线程已经自动创建了一个Looper对象,并启动了消息循环,我们可以在主线程中方便地使用Handler来处理UI事件。

而对于其他线程,如果需要处理消息,就需要手动创建一个Looper对象,并调用Looper.loop()方法来启动消息循环。

  • 必须确保在创建Handler对象之前,在线程中调用了Looper.prepare()方法创建Looper对象并初始化。
  • 通过调用Looper.loop()方法,线程会进入一个无限循环,不断地从消息队列中取出消息,并将其分发给对应的Handler进行处理。当调用Looper.quit()方法时,消息循环会停止,线程会退出循环。
 Handler handler2;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_handle);// 开一个新的线程,用于接收主线程向子线程传递消息new Thread(new Runnable() {@Overridepublic void run() {// 系统会自动为主线程开启消息循环(自动调用这句代码),与此同时创建一个 Looper 对象,不断的从 MessageQue中读取消息,交给主线程处理Looper.prepare(); // 准备开启一个消息循环handler2 = new Handler(new Handler.Callback() {@Overridepublic boolean handleMessage(@NonNull Message msg) {Log.e("主线程向子线程中发送消息", "" + msg.what + "" + msg.arg1);return false;}});Looper.loop(); //开始消息循环,相当于 while(true) ,在这里等待消息}}).start();}// 此时有两个线程,这两线程发送的消息都能被 Handle接收,但如何区分不同线程发送的消息从而做不同处理呢?public void getDate(View view) {int id = view.getId();// 第一个线程if (id == R.id.handle1) {new Thread() {@Overridepublic void run() {super.run();getHttp();// 此时会报错:Only the original thread that created a view hierarchy can touch its views./*TextView textView = findViewById(R.id.t1);textView.setText(httpDate);*/// 解决办法 1 runOnUiThread 方法 ==> 相当于在主线程中跑 (初学阶段)/*runOnUiThread(new Runnable() {@Overridepublic void run() {TextView textView = findViewById(R.id.t1);textView.setText(httpDate);}});*/// 解决办法 2 利用 Handle 处理// 2.在子线程中发送(空)消息,此时发送的是空消息handler.sendEmptyMessage(1);}}.start();}// 第二个线程else if (id == R.id.handle2) {new Thread() {@Overridepublic void run() {super.run();getHttp();httpDate = httpDate + "\n第2个线程";handler.sendEmptyMessage(2);}}.start();}// 第三个线程else if (id == R.id.handle3) {new Thread() {@Overridepublic void run() {super.run();getHttp();                  Message message = new Message();message.what = 3;DataBean dataBean = new DataBean(1, "157181@qq.com", "ybr", "scl", "asiudh");message.obj = dataBean;handler.sendMessage(message);}}.start();}// 第四个线程- 主线程向子线程发送消息else if (id == R.id.handle4) {Message message2 = new Message();message2.what = 999;message2.arg1 = 1234;handler2.sendMessage(message2);}}

线程中更新UI

runOnUiThread 方法

==> 相当于在主线程中跑 (初学阶段)

public void getDate() {new Thread() {@Overridepublic void run() {super.run();getHttp();// 此时会报错:Only the original thread that created a view hierarchy can touch its views./*TextView textView = findViewById(R.id.t1);textView.setText(httpDate);*/// 解决办法 1 runOnUiThread 方法 ==> 相当于在主线程中跑 (初学阶段)runOnUiThread(new Runnable() {@Overridepublic void run() {TextView textView = findViewById(R.id.t1);textView.setText(httpDate);}});}}.start();}

利用Handler 

 new Thread() {@Overridepublic void run() {super.run();getHttp();// 此时会报错:Only the original thread that created a view hierarchy can touch its views.// 2.在子线程中发送(空)消息,此时发送的是空消息handler.sendEmptyMessage(1);}
}.start();
Handler handler = new Handler(new Handler.Callback() {@Override// 3.由handle对象接收消息并处理,在Handler的内部类中处理public boolean handleMessage(@NonNull Message msg) {Log.e("handler 处理消息", "这是handler发送的消息,此时是空");textView = findViewById(R.id.t1);textView.setText(httpDate);return false;}});

什么是异步任务?

逻辑上:以多线程的方式完成的功能需求

API上:指AsyncTask类

在没有 AsyncTask 前,我们可以使用 Thread+Handler 实现异步任务,而 AsyncTask 是对Thread+Handler 功能的封装,使用更简洁,效率更高效

AsyncTask 是一个抽象类,需要指定3个泛型参数

public abstract class AsyncTask<Params, Progress, Result>
  • Params: 这个泛型指定的是我们传递给异步任务执行时的参数的类型。
  • Progress: 这个泛型指定的是我们的异步任务在执行的时候将执行的进度返回给UI线程的参数的类型。
  • Result: 这个泛型指定的异步任务执行完后返回给UI线程的结果的类型。

而 AsyncTask目前已被弃用

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

NI CRIO 9045 LABVIEW2020

1.labview工程如果要访问CRIO&#xff0c;需要设置以下&#xff0c;否则在项目中连接失败。 2.项目中如果要传文件&#xff0c;需要安装WebDEV 3.使用WebDAV将文件传输到实时(RT)目标 https://knowledge.ni.com/KnowledgeArticleDetails?idkA03q000000YGytCAG&lzh-CN

Mars3d实现用一个button控制一个map.control的显示与隐藏

原生js,想做一个button,控制比如compass的显示与隐藏 点一下显示 再次单击的时候就隐藏掉 写了一个function控制显示隐藏 function addCompass(){ if(compass.showtrue) { compass.showfalse; } else{ compass.showtrue; } } 功能示例(Vue版) | Mars3D三维可视化平台 | 火星…

深入了解C/C++的内存区域划分

&#x1f525;个人主页&#xff1a;北辰水墨 &#x1f525;专栏&#xff1a;C学习仓 本节我们来讲解C/C的内存区域划分&#xff0c;文末会附加一道题目来检验成果&#xff08;有参考答案&#xff09; 一、大体有哪些区域&#xff1f;分别存放什么变量开辟的空间&#xff1f; …

【微服务】网关(详细知识以及登录验证)

微服务网关 网关网关路由快速入门路由属性 路由断言网关登录校验自定义过滤器实现登录校验网关传递用户OpenFeign传递用户 网关 网络的关口&#xff0c;负责请求的路由&#xff0c;转发&#xff0c;身份校验 当我们把一个单体项目分成多个微服务并部署在多台服务器中&#xff…

Redis__数据类型

文章目录 &#x1f60a; 作者&#xff1a;Lion J &#x1f496; 主页&#xff1a; https://blog.csdn.net/weixin_69252724 &#x1f389; 主题&#xff1a;Redis__数据类型 ⏱️ 创作时间&#xff1a;2024年04月28日 ———————————————— 这里写目录标题 文…

大模型争霸的下一站:不仅是超越GPT-4,更是寻求模型之间的平衡应用

文 | 智能相对论 作者 | 沈浪 知名科学杂志《Nature》发表了一篇关于大模型规模参数大小争议的文章《In Al, is bigger always better?》——AI大模型&#xff0c;越大越好吗&#xff1f;随着大模型应用走向实践&#xff0c;这一问题不可避免地成为了当前AI行业发展的焦点与…

迅雷永久破解

链接&#xff1a;https://pan.baidu.com/s/1ZGb1ljTPPG3NFsI8ghhWbA?pwdok7s 下载后解压 以管理员身份运行绿化.bat&#xff0c;会自动生成快捷方式&#xff0c;如果没有可以在program中运行Thunder.exe

【python】条件语句与循环语句

目录 一.条件语句 1.定义 2.条件语句格式 &#xff08;1&#xff09;if &#xff08;2&#xff09;if-else &#xff08;3&#xff09;elif功能 &#xff08;4&#xff09;if嵌套使用 3.猜拳游戏 二.循环语句 1. while循环 2.while嵌套 3.for循环 4.break和conti…

AI图书推荐:AI在语言学习教育领域的应用和挑战

这本书《AI在语言学习教育领域的应用和挑战》&#xff08;AI in Language Teaching, Learning, and Assessment&#xff09;由Fang Pan编辑&#xff0c;出版于IGI Global&#xff0c;主要探讨了人工智能&#xff08;AI&#xff09;在语言教育领域的应用、挑战以及潜在的益处。 …

JS基础:JS语法规范详解(最全!)

你好&#xff0c;我是云桃桃。 一个希望帮助更多朋友快速入门 WEB 前端的程序媛。 云桃桃-大专生&#xff0c;一枚程序媛&#xff0c;感谢关注。回复 “前端基础题”&#xff0c;可免费获得前端基础 100 题汇总&#xff0c;回复 “前端基础路线”&#xff0c;可获取完整web基础…

C++:自增运算符(++)重载

自增运算符&#xff08;&#xff09;分为前置自增和后置自增&#xff0c;它们两者主要的区别是&#xff1a;返回的值不同&#xff0c;以及执行自增操作的顺序不同。 前置自增运算符 &#xff1a; 前置自增运算符首先将操作数加1&#xff0c;然后返回自增后的值。 这意味着如果…

CNN笔记详解

CNN(卷积神经网络) 计算机视觉&#xff0c;当你们听到这一概念的是否好奇计算机到底是怎样知道这个图片是什么的呢&#xff1f;为此提出了卷积神经网络&#xff0c;通过卷积神经网络&#xff0c;计算机就可以识别出图片中的特征&#xff0c;从而识别出图片中的物体。看到这里充…

盘一盘接口测试的那些痛点,你现在会解决了吗

前言 说到接口测试&#xff0c;想必大家一定不会陌生。接口测试就是测试系统组件间&#xff0c;接口对接是否顺畅的一种测试。包括测试数据能否交换、能否传递、能否正常控制管理过程&#xff0c;以及系统间的相互逻辑依赖关系&#xff0c;等等。 由于接口测试主要是检测系统…

2024网络安全面试问题宝典(4万字)

2024网络安全厂商面试问题宝典(4万字) 目录 评分标准网络基础问题 TCP建立连接要进行3次握手&#xff08;syn-syn&#xff0c;ack-ack&#xff09;&#xff0c;而断开连接要进行4次&#xff08;fin-ack-fin-ack&#xff09;TCP&#xff0c;UDP区别&#xff1a;安全常用的协议…

数据库基础--MySQL多表查询之联表查询

联表查询 定义&#xff1a;多张表联合在一起查询&#xff0c;例如学生信息与学生班级表、部门与员工表 创建两张表&#xff0c;主表与从表 CREATE TABLE TestMain(id INT Not NULL AUTO_INCREMENT,nameVARCHAR(10),introduction VARCHAR(255),PRIMARY KEY(id) ); CREATE TAB…

自动驾驶主流芯片及平台架构(二)特斯拉自动驾驶芯片平台介绍

早期 对外采购mobileye EyeQ3 芯片摄像头半集成方案&#xff0c;主要是为了满足快速量产需求&#xff0c;且受制于研发资金不足限制&#xff1b; 中期 采用高算力NVIDIA 芯片平台其他摄像头供应商的特斯拉内部集成方案&#xff0c;mobileye开发节奏无法紧跟特斯拉需求&#xff…

select,poll,epoll

在 Linux Socket 服务器短编程时&#xff0c;为了处理大量客户的连接请求&#xff0c;需要使用非阻塞I/O和复用&#xff0c;select&#xff0c;poll 和 epoll 是 Linux API 提供的I/O复用方式。 \selectpollepoll操作方式遍历遍历回调底层实现数组链表哈希表IO效率每次调用都进…

ROS 2边学边练(43)-- 利用GTest写一个基本测试(C++)

前言 在ROS&#xff08;Robot Operating System&#xff09;中&#xff0c;gtest&#xff08;Google Test&#xff09;是一个广泛使用的C测试框架&#xff0c;用于编写和执行单元测试。这些测试可以验证ROS节点、服务和消息等的正确性和性能。 如果我们需要在写的包中添加测试&…

kubectl_入门_service详解

Service 我们知道 Pod 的生命周期是有限的。可以用 ReplicaSet 和Deployment 来动态的创建和销毁 Pod&#xff0c;每个 Pod 都有自己的 IP 地址&#xff0c;但是如果 Pod 重建了的话那么他的 IP 很有可能也就变化了。 这就会带来一个问题&#xff1a;比如我们有一些后端的 Po…

Flink时间语义 | 大数据技术

⭐简单说两句⭐ ✨ 正在努力的小叮当~ &#x1f496; 超级爱分享&#xff0c;分享各种有趣干货&#xff01; &#x1f469;‍&#x1f4bb; 提供&#xff1a;模拟面试 | 简历诊断 | 独家简历模板 &#x1f308; 感谢关注&#xff0c;关注了你就是我的超级粉丝啦&#xff01; &a…