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.mzph.cn/web/5982.shtml

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

相关文章

手撕spring框架(2)

相关系列 java中spring底层核心原理解析&#xff08;1&#xff09;-CSDN博客 java中spring底层核心原理解析(2)-CSDN博客 手撕spring框架&#xff08;1&#xff09;-CSDN博客 手撕spring框架&#xff08;3&#xff09;-CSDN博客 手撕spring框架&#xff08;4&#xff09;-CSDN博…

用龙梦迷你电脑福珑2.0做web服务器

用龙梦迷你电脑福珑2.0上做web服务器是可行的。已将一个网站源码放到该电脑&#xff0c;在局域网里可以访问网站网页。另外通过在同一局域网内的一台windows10电脑上安装花生壳软件&#xff0c;也可以在外网访问该内网服务器网站网页。该电脑的操作系统属于LAMP。在该电脑上安装…

Qt Creator导入第三方so库和jar包——Qt For Android

前言 之前了解了在Android Studio下导入so库和jar包&#xff0c;现在实现如何在Qt上导入so库和jar包。 实现 下面是我安卓开发&#xff08;需调用安卓接口的代码&#xff09;的目录&#xff08;图1&#xff09;&#xff0c;此目录结构和原生态环境&#xff08;Android Studi…

详细分析Java中的脱敏注解(附Demo)

目录 前言1. 基本知识2. 核心逻辑3. Demo4. 模版 前言 对于隐私信息&#xff0c;需要做特殊处理&#xff0c;比如身份证或者手机号等 对于Java的相关知识推荐阅读&#xff1a;java框架 零基础从入门到精通的学习路线 附开源项目面经等&#xff08;超全&#xff09; 1. 基本知…

软件定义汽车落地的五大关键要素

1、架构升级 1.1 软件架构&#xff1a;分层解耦、服务化、API 接口标准化 随着企业向软件定义汽车开发方法的转变&#xff0c;软件架构也需要同步进行升级&#xff0c;引入面向服务的架构&#xff08;Service-Oriented Architecture&#xff0c;简称 SOA&#xff09;方法论。…

ThreeJS:响应式画布与全屏控制

响应式画布 响应式画布&#xff1a;在用户缩放浏览器窗口时&#xff0c;为便于动态更新画布尺寸与宽高比例&#xff0c;需要通过监听resize事件&#xff0c;来实现响应式画布。 window.onresize function () {//TODO:重置渲染器宽高比renderer.setSize(window.innerWidth, wi…

为人处事电影解说,全新升级瀚海跑道一分钟一条视频,全平台可推广,轻轻松松日入1000

自古以来&#xff0c;我国流行的一种现象是&#xff0c;大多数人都会与领导或上司打交道。由于某些话题不宜公开讨论&#xff0c;因此出现了许多含蓄的表达方式。随着年龄的增长&#xff0c;人们的态度也发生了变化&#xff0c;从最初的轻视到现在的重视。 下 载 地 址&#…

VG做mirror引起的块偏移

事件起因 Oracle10.2环境 Aix操作系统使用aix的lvm技术。制作vg的mirror。以此来替换掉老的存储。 做mirror前&#xff0c;数据库已完全关闭 故障现象 在启动数据库时&#xff0c;发现IO错误。该系统的spfile&#xff0c;ctl&#xff0c;dbf均是用lv做的裸设备。其中dbf是使…

cmake的使用方法: 编译生成库文件

一. 简介 前面文章学习了针对单个 .c文件&#xff0c;cmake 工具是如何编译的&#xff1f; 针对包含多个 .c文件&#xff0c;cmake工具又是如何编译的&#xff1f;文章如下&#xff1a; cmake的使用方法: 单个源文件的编译-CSDN博客 cmake的使用方法: 多个源文件的编译-CS…

Java入门-final关键字

final关键字 修饰基本类型 变量为只读&#xff0c;不能修改变量的内容。 final int SIZE 3;修饰引用类型 引用的对象不能改变&#xff0c;但是对象的内容可以修改。 final Car c new Car( );c.setColor("红色");修饰类的属性 类的属性不能被修改。 第一种方式&…

Linux 进程间通信之命名管道

&#x1f493;博主CSDN主页:麻辣韭菜&#x1f493;   ⏩专栏分类&#xff1a;Linux知识分享⏪   &#x1f69a;代码仓库:Linux代码练习&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习更多Linux知识   &#x1f51d; 目录 前言 命名管道 创建一个命名管道 …

Leetcode—976. 三角形的最大周长【简单】(ranges::sort函数)

2024每日刷题&#xff08;122&#xff09; Leetcode—976. 三角形的最大周长 实现代码 class Solution { public:int largestPerimeter(vector<int>& nums) {ranges::sort(nums);for(int i nums.size() - 1; i > 1; i--) {if(nums[i - 1] nums[i - 2] > nu…

洛谷 P1377 [TJOI2011]:树的序 ← 笛卡尔树

【题目来源】https://www.luogu.com.cn/problem/P1377【题目描述】 众所周知&#xff0c;二叉查找树的形态和键值的插入顺序密切相关。准确的讲&#xff1a; 1.空树中加入一个键值 k&#xff0c;则变为只有一个结点的二叉查找树&#xff0c;此结点的键值即为 k。 2.在非空树中插…

智能物联网与Web3:连接未来数字生活的桥梁

随着科技的不断进步&#xff0c;智能物联网&#xff08;IoT&#xff09;和Web3技术正成为数字化时代的关键驱动力。智能物联网将各种物理设备连接到互联网&#xff0c;使其能够感知环境、收集数据并与其他设备通信&#xff0c;而Web3技术则以去中心化、安全性和透明性为核心&am…

Linux开发板 FTP 服务器移植与搭建

VSFTPD&#xff08;Very Secure FTP Daemon&#xff09;是一个安全、稳定且快速的FTP服务器软件&#xff0c;广泛用于Unix和Linux操作系统。它以其轻量级、高效和易于配置而受到赞誉。VSFTPD不仅支持标准的FTP命令和操作&#xff0c;还提供了额外的安全特性&#xff0c;如匿名F…

Python日志记录库之logbook使用详解

概要 在软件开发和运维中,日志记录是一项至关重要的任务。Python 的 Logbook 库是一个强大而灵活的日志记录工具,提供了丰富的功能和易用的接口。本文将深入探讨 Logbook 库的特性、用法,并通过丰富的示例代码展示其在实际项目中的应用。 Logbook 简介 Logbook 是一个为 P…

本地搭建llama大模型及对话UI

环境说明&#xff1a;MBP 2023 M2Pro芯片 用到的工具/组件/技术&#xff1a;ollama、llama3:8b、docker、open-webui 1.下载ollama ollama官网下载地址&#xff1a;https://ollama.com/download 到ollama官网地址下载对应操作系统版本的ollama平台&#xff0c;按照安装指引…

springboot 获取maven打包时间

springboot 获取maven打包时间 pom <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.1.13.RELEASE</version><relativePath /> <!-- lookup parent…

民航电子数据库:mysql与cae(insert语法差异)

目录 示例1、cae插入数据时不支持value关键字&#xff0c;只能使用values2、insert时&#xff0c;就算是自增主键&#xff0c;只要新增时包含了主键&#xff0c;该主键就必须有值&#xff0c;否则会报错&#xff1a;字段xxx不能取空值 对接民航电子数据库&#xff0c;本篇记录i…

用栈实现队列——leetcode刷题

题目要求我们只用栈的基本操作 push to top 入栈&#xff0c;peek from top 返回栈顶元素&#xff0c;pop from top 移除并返回栈顶元素&#xff0c;size 栈的大小&#xff0c;is_empty 判断栈是否为空&#xff0c;这几个函数来实现队列&#xff0c;也就是说&#xff0c;我们在…