当前位置: 首页 > news >正文

uniapp:实现手机端APP登录强制更新,从本地服务器下载新的apk更新,并使用WebSocket,实时强制在线用户更新

实现登录即更新,或实时监听更新

本文介绍的是在App打开启动的时候调用更新,点击下方链接,查看使用WebSocket实现实时通知在线用户更新。

uniapp:全局消息是推送,实现app在线更新,WebSocket,apk上传:

登录更新流程

    • 1.在app每次启动的时候请求java后端,
    • 2.后端接口获取最新的版本:
    • 3.打开更新页面
    • 4.后端下载接口去指定目录下载apk

在这里插入图片描述

  • 背景:内部手持机app开发功能,需要更新的到车间各个手持机上。
  • 最初的方案:开发人员开发完后,去现成给每台手持机安装更新
  • 设想:实现在线发布,手持机检测版本后更新。

实现手持机更新
1.发布到应用商店
2.uiniapp自带版本更新
3.自己开发功能,检测需要更新后从自己的服务器上下载下来更新

这里我们选择自己开发,毕竟不需要证书,和依托于其他平台

app更新更新我们需要解决哪些问题?

1app什么时候知道自己需要更新?
2检测到需要更新后从哪里获取文件?
3如果app一直在线运行,如何实时通知它需要更新?

下面我们逐一解决:
1app什么时候知道自己需要更新?

这里我们使用的是在app每次打开的时候去请求我们后台的接口,拿到最新的app版本(自己定义的),和当前app的版本进行比较。

前提是,你在自己的服务器上上传了apk之后并且记录了在自己的业务表里面。这样你才能比较是否需要更新。(意思就是最好你维护一张表,每次上传插入一条记录)

在这里插入图片描述
实现:

1.在app每次启动的时候请求java后端,

因此要卸载App.vue里面

<script>function requestToJavaBackend() {uni.request({url: 'http://*.*.*.*:8080/app/getNewestVersion', // 替换成你的后端 Java 服务的API地址method: 'GET', // 或 'POST',根据你的需求选择请求方法success: (res) => {if(res.data.code === 200){console.log(res.data.data);console.log();console.log(uni.getSystemInfoSync().appVersion+"111111111");console.log();console.log(uni.getSystemInfoSync().appVersionCode+"222222");console.log(platform+"33333333333333333333")const newVersionName = res.data.data.newVersionName //线上最新版本名const newVersionCode = res.data.data.newVersionCode; //线上最新版本号// const selfVersionCode = Number(uni.getSystemInfoSync().appVersion) //当前App版本号 const selfVersionCode = Number(uni.getSystemInfoSync().appVersionCode) //当前App版本号const platform = uni.getSystemInfoSync().platform //手机平台//线上版本号高于当前,进行在线升级if (selfVersionCode < newVersionCode) {//安卓手机弹窗升级 platform === 'android'   || windows// uni.navigateTo({// 	url: '/pages/index/upgrade'// })if (platform === 'android') {uni.navigateTo({url: '/pages/index/upgrade'})} else {uni.showModal({title: '发现新版本 ' + newVersionName,content: '请到App store进行升级',showCancel: false})}}}else{uni.showModal({title: '版本校验错误',content: '版本校验错误,联系管理员',showCancel: false})}},fail: (err) => {uni.showModal({title: '版本校验错误',content: '版本校验错误,联系管理员',showCancel: false})// 在这里处理请求失败后的逻辑},});}export default {onLaunch() {// 加载系统信息this.$store.dispatch('SystemInfo');// 在应用启动时执行一次任务requestToJavaBackend()// 每隔一段时间执行一次任务// setInterval(() => {// 	requestToJavaBackend()// }, 5000); // 30秒},onShow() {},onHide() {},methods: {},}
</script><style lang="scss">@import "@/uni_modules/uview-ui/index.scss";@import "@/static/style.scss";
</style>

2.后端接口获取最新的版本:

@GetMapping("/getNewestVersion")public AjaxResult getNewestVersion(){HashMap<String, Object> map = new HashMap<>();map.put("newVersionName", "v");map.put("newVersionCode", 2);//你的最新版本,建议每次开发完上传之后放在表里,然后去表里拿最新的记录,的版本号return AjaxResult.success(map);}

这里检测到2>1之后,就要打开更新页面强制更新(当然你可以不更新,看业务需求)

3.打开更新页面

注意从自己的服务器下载,当前版本小于服务器版本,跳转下载。

自定义下载地址:this.downloadUrl = ‘http://...:8080/app/download’; //下载链接

目录结构
在这里插入图片描述
upgrade.vue 更新弹窗

<template><view class="upgrade-popup"><image class="header-bg" src="" mode="widthFix"></image><view class="main"><view class="version">发现新版本{{versionName}}</view><view class="content"><text class="title">更新内容</text><view class="desc" v-html="versionDesc"></view></view><!--下载状态-进度条显示 --><view class="footer" v-if="isStartDownload"><view class="progress-view" :class="{'active':!hasProgress}" @click="handleInstallApp"><!-- 进度条 --><view v-if="hasProgress" style="height: 100%;"><view class="txt">{{percentText}}</view><view class="progress" :style="setProStyle"></view></view><view v-else><view class="btn upgrade force">{{ isDownloadFinish  ? '立即安装' :'下载中...'}}</view></view></view></view><!-- 强制更新 --><view class="footer" ><view class="btn upgrade force" @click="handleUpgrade">立即更新</view></view><!-- 可选择更新 --><!-- <view class="footer" v-else><view class="btn close" @click="handleClose">以后再说</view><view class="btn upgrade" @click="handleUpgrade">立即更新</view></view> --></view></view>
</template><script>import {downloadApp,installApp} from './upgrade.js'export default {data() {return {isForceUpdate: false, //是否强制更新versionName: '', //版本名称versionDesc: '', //更新说明downloadUrl: '', //APP下载链接isDownloadFinish: false, //是否下载完成hasProgress: false, //是否能显示进度条currentPercent: 0, //当前下载百分比isStartDownload: false, //是否开始下载fileName: '', //下载后app本地路径名称}},computed: {//设置进度条样式,实时更新进度位置setProStyle() {return {width: (510 * this.currentPercent / 100) + 'rpx' //510:按钮进度条宽度}},//百分比文字percentText() {let percent = this.currentPercent;if (typeof percent !== 'number' || isNaN(percent)) return '下载中...'if (percent < 100) return `下载中${percent}%`return '立即安装'}},onLoad() {this.init()},onBackPress(options) {// 禁用返回if (options.from == 'backbutton') {return true;}},methods: {//初始化获取最新APP版本信息init() {//模拟接口获取setTimeout(() => {//演示数据请根据实际修改this.versionName = 'V1.2.0'; //版本名称this.versionDesc = "修复若干bug"; //更新说明this.downloadUrl = 'http://*.*.*.*:8080/app/download'; //下载链接this.isForceUpdate = false; //是否强制更新}, 200)},//更新handleUpgrade() {if (this.downloadUrl) {this.isStartDownload = true//开始下载AppdownloadApp(this.downloadUrl, current => {//下载进度监听this.hasProgress = truethis.currentPercent = current}).then(fileName => {//下载完成this.isDownloadFinish = truethis.fileName = fileNameif (fileName) {//自动安装Appthis.handleInstallApp()}}).catch(e => {console.log(e, 'e')})} else {uni.showToast({title: '下载链接不存在',icon: 'none'})}},//安装apphandleInstallApp() {//下载完成才能安装,防止下载过程中点击if (this.isDownloadFinish && this.fileName) {installApp(this.fileName, () => {//安装成功,关闭升级弹窗uni.navigateBack()})}},//关闭返回handleClose() {uni.navigateBack()},}}
</script><style>page {background: rgba(0, 0, 0, 0.5);/**设置窗口背景半透明*/}
</style>
<style lang="scss" scoped>.upgrade-popup {width: 580rpx;height: auto;position: fixed;top: 50%;left: 50%;transform: translate(-50%, -50%);background: #fff;border-radius: 20rpx;box-sizing: border-box;border: 1px solid #eee;}.header-bg {width: 100%;margin-top: -112rpx;}.main {padding: 10rpx 30rpx 30rpx;box-sizing: border-box;.version {font-size: 36rpx;color: #026DF7;font-weight: 700;width: 100%;text-align: center;overflow: hidden;text-overflow: ellipsis;white-space: nowrap;letter-spacing: 1px;}.content {margin-top: 60rpx;.title {font-size: 28rpx;font-weight: 700;color: #000000;}.desc {box-sizing: border-box;margin-top: 20rpx;font-size: 28rpx;color: #6A6A6A;max-height: 80vh;overflow-y: auto;}}.footer {width: 100%;display: flex;justify-content: center;align-items: center;position: relative;flex-shrink: 0;margin-top: 100rpx;.btn {width: 246rpx;display: flex;justify-content: center;align-items: center;position: relative;z-index: 999;height: 96rpx;box-sizing: border-box;font-size: 32rpx;border-radius: 10rpx;letter-spacing: 2rpx;&.force {width: 500rpx;}&.close {border: 1px solid #E0E0E0;margin-right: 25rpx;color: #000;}&.upgrade {background-color: #026DF7;color: white;}}.progress-view {width: 510rpx;height: 90rpx;display: flex;position: relative;align-items: center;border-radius: 6rpx;background-color: #dcdcdc;display: flex;justify-content: flex-start;padding: 0px;box-sizing: border-box;border: none;overflow: hidden;&.active {background-color: #026DF7;}.progress {height: 100%;background-color: #026DF7;padding: 0px;box-sizing: border-box;border: none;border-top-left-radius: 10rpx;border-bottom-left-radius: 10rpx;}.txt {font-size: 28rpx;position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);color: #fff;}}}}
</style>

upgrade.js

/*** @description H5+下载App* @param downloadUrl:App下载链接* @param progressCallBack:下载进度回调*/
export const downloadApp = (downloadUrl, progressCallBack = () => {}, ) => {return new Promise((resolve, reject) => {//创建下载任务const downloadTask = plus.downloader.createDownload(downloadUrl, {method: "GET"}, (task, status) => {console.log(status,'status')if (status == 200) { //下载成功resolve(task.filename)} else {reject('fail')uni.showToast({title: '下载失败',duration: 1500,icon: "none"});}})//监听下载过程downloadTask.addEventListener("statechanged", (task, status) => {switch (task.state) {case 1: // 开始  break;case 2: //已连接到服务器  break;case 3: // 已接收到数据  let hasProgress = task.totalSize && task.totalSize > 0 //是否能获取到App大小if (hasProgress) {let current = parseInt(100 * task.downloadedSize / task.totalSize); //获取下载进度百分比progressCallBack(current)}break;case 4: // 下载完成       break;}});//开始执行下载downloadTask.start();})}
/*** @description H5+安装APP* @param fileName:app文件名* @param callBack:安装成功回调*/
export const installApp = (fileName, callBack = () => {}) => {//注册广播监听app安装情况onInstallListening(callBack);//开始安装plus.runtime.install(plus.io.convertLocalFileSystemURL(fileName), {}, () => {//成功跳转到安装界面}, function(error) {uni.showToast({title: '安装失败',duration: 1500,icon: "none"});})}
/*** @description 注册广播监听APP是否安装成功* @param callBack:安装成功回调函数*/
const onInstallListening = (callBack = () => {}) => {let mainActivity = plus.android.runtimeMainActivity(); //获取activity//生成广播接收器let receiver = plus.android.implements('io.dcloud.android.content.BroadcastReceiver', {onReceive: (context, intent) => { //接收广播回调  plus.android.importClass(intent);mainActivity.unregisterReceiver(receiver); //取消监听callBack()}});let IntentFilter = plus.android.importClass('android.content.IntentFilter');let Intent = plus.android.importClass('android.content.Intent');let filter = new IntentFilter();filter.addAction(Intent.ACTION_PACKAGE_ADDED); //监听APP安装     filter.addDataScheme("package");mainActivity.registerReceiver(receiver, filter); //注册广播}

4.后端下载接口去指定目录下载apk

@GetMapping("/download")public void download(String path, HttpServletResponse response) {try {//拿到最新的版本//SysAppVersion 是我自己表的实体,我每次上传后会在这个表里插入一条记录//下载的时候拿最新的SysAppVersion newestVersion = sysAppVersionService.getNewestVersion();File file = new File("d://app//"+newestVersion.getFileName());String filename = file.getName();String ext = filename.substring(filename.lastIndexOf(".") + 1).toLowerCase();FileInputStream fileInputStream = new FileInputStream(file);InputStream fis = new BufferedInputStream(fileInputStream);byte[] buffer = new byte[fis.available()];fis.read(buffer);fis.close();response.reset();// 设置response的Headerresponse.setCharacterEncoding("UTF-8");//Content-Disposition的作用:告知浏览器以何种方式显示响应返回的文件,用浏览器打开还是以附件的形式下载到本地保存//attachment表示以附件方式下载 inline表示在线打开 "Content-Disposition: inline; filename=文件名.mp3"// filename表示文件的默认名称,因为网络传输只支持URL编码的相关支付,因此需要将文件名URL编码后进行传输,前端收到后需要反编码才能获取到真正的名称response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8"));// 告知浏览器文件的大小response.addHeader("Content-Length", "" + file.length());OutputStream outputStream = new BufferedOutputStream(response.getOutputStream());response.setContentType("application/octet-stream");outputStream.write(buffer);outputStream.flush();} catch (IOException ex) {ex.printStackTrace();}}

结束,非常简单,以上代码cv即用。

相关文章:

uniapp:实现手机端APP登录强制更新,从本地服务器下载新的apk更新,并使用WebSocket,实时强制在线用户更新

实现登录即更新&#xff0c;或实时监听更新 本文介绍的是在App打开启动的时候调用更新&#xff0c;点击下方链接&#xff0c;查看使用WebSocket实现实时通知在线用户更新。 uniapp&#xff1a;全局消息是推送&#xff0c;实现app在线更新&#xff0c;WebSocket&#xff0c;ap…...

设计循环队列——oj题622

. 个人主页&#xff1a;晓风飞 专栏&#xff1a;LeetCode刷题|数据结构|Linux 路漫漫其修远兮&#xff0c;吾将上下而求索 文章目录 题目要求&#xff1a;应该支持如下操作&#xff1a;示例&#xff1a;提示&#xff1a; 结构体定义队列的创建基本操作判断队列是否为空&#xf…...

阿里后端实习一面面经

阿里后端实习一面面经 项目中使用到了es&#xff0c;es的作用&#xff1f; elasticsearch是一款非常强大的开源搜索引擎&#xff0c;具备非常多强大功能&#xff0c;可以帮助我们从海量数据中快速找到需要的内容 es中的重要概念&#xff1f; 群集&#xff1a;一个或多个节点…...

element-ui组件DatePicker日期选择器移动端兼容

element-ui组件DatePicker日期选择器移动端兼容 css /** 移动端展示 **/ media screen and (max-width: 500px) {.el-picker-panel__sidebar {width: 100%;}.el-picker-panel {width: 400px!important;}.el-picker-panel__content {width: 100%;}.el-picker-panel__body{marg…...

burpsuite 爆破

靶场搭建:phpstudy的安装与靶场搭建 - junlin623 - 博客园 (cnblogs.com) 账号字典:XXTK: 一些弱口令、fuzz字典 (gitee.com) 网盘链接:https://pan.baidu.com/s/1v5pAwaTwoeCnJgkUXf3iLQ?pwd=mllm 提取码:mllm --来自百度网盘超级会员V2的分享 一、暴力破解 - 基于…...

SparkSQL基础解析(三)

1、 Spark SQL概述 1.1什么是Spark SQL Spark SQL是Spark用来处理结构化数据的一个模块&#xff0c;它提供了2个编程抽象&#xff1a;DataFrame和 DataSet&#xff0c;并且作为分布式SQL查询引擎的作用。 我们已经学习了Hive&#xff0c;它是将Hive SQL转换成MapReduce然后提…...

gz-hamonic 安装提示缺少许多依赖无法安装

在软件更新源中增加gz-hamonic的软件源&#xff0c; 点击添加&#xff0c;在输入框中填入如下语句&#xff1a; deb http://packages.osrfoundation.org/ubuntu jammy main 如图所示&#xff1a; 然后执行 sudo apt -get install gz-hamonic即可安装。 如下图 在终端中输入…...

新版Edge卸载

新版Edge卸载&#xff1a;步骤与注意事项 随着Windows 10的发布&#xff0c;微软推出了新版Edge浏览器。虽然新版Edge浏览器具有许多优秀的新功能和改进&#xff0c;但有时您可能希望卸载它并使用其他浏览器。在本文中&#xff0c;我们将向您介绍如何卸载新版Edge浏览器&#…...

Ansibe自动化基础

目录 一.Ansibe自动化概述 1.特点 2.工作特性 3.应用场合 二.ansibe安装即相关文件说明 1.安装 2.相关文件 3.主配置文件内容详解 4.ansibe运行机制 三.ansibe管理节点命令 1.Ansibe 四.主机组配置 1.基本配置 第一种&#xff1a; 第二种&#xff1a; 2.设置SSH…...

2023 年中国高校大数据挑战赛赛题B DNA 存储中的序列聚类与比对-解析与参考代码

题目背景&#xff1a;目前往往需要对测序后的序列进行聚类与比对。其中聚类指的是将测序序列聚类以判断原始序列有多少条&#xff0c;聚类后相同类的序列定义为一个簇。比对则是指在聚类基础上对一个簇内的序列进行比对进而输出一条最有 可能的正确序列。通过聚类与比对将会极大…...

决策树--分类决策树

1、介绍 ① 定义 分类决策树通过树形结构来模拟决策过程&#xff0c;决策树由结点和有向边组成。结点有两种类型&#xff1a;内部结 点和叶结点。内部结点表示一个特征或属性&#xff0c;叶子节点表示一个类。 ② 生成过程 用决策树分类&#xff0c;从根结点开始&#xff…...

【2024/1/5】

2024/1/5周报 本周开展工作下周工作计划 本周开展工作 首先的话就是跟大家汇报一下上一个项目的进度&#xff0c;那因为一些我这边的不可控的因素暂时进行搁置&#xff0c;随后的话还是需要在进行做的。 因此我们最近在做一个web端的项目&#xff0c;这个项目的具体的就不汇报…...

CNN——VGG

1.VGG简介 论文下载地址&#xff1a;https://arxiv.org/pdf/1409.1556.pdf VGGNet 是由牛津大学视觉几何小组&#xff08;Visual Geometry Group, VGG&#xff09;提出的一种深层卷积网络结构&#xff0c;他们以 7.32% 的错误率赢得了 2014 年 ILSVRC 分类任务的亚军&#xff…...

深入理解Java中的多线程编程与并发控制

当谈论到 Java 编程语言时&#xff0c;多线程编程和并发控制是其中最重要的话题之一。Java 在多线程领域有着强大的支持和丰富的工具集&#xff0c;允许开发人员利用并发性来提高程序性能和效率。本文将深入探讨 Java 中的多线程编程和并发控制&#xff0c;包括线程的创建、同步…...

提供10个mysql的实例和思路

学生信息管理系统 学生表&#xff08;id, name, gender, age, class_id&#xff09;班级表&#xff08;id, name&#xff09;思路&#xff1a;通过学生表和班级表进行关联&#xff0c;可以实现学生信息的查询、添加、修改、删除等操作。 订单管理系统 订单表&#xff08;id, us…...

FPGA项目(14)——基于FPGA的数字秒表设计

1.功能设计 设计内容及要求: 1.秒表最大计时范围为99分59. 99秒 2.6位数码管显示&#xff0c;分辨率为0.01秒 3.具有清零、启动计时、暂停及继续计时等功能 4.控制操作按键不超过二个。 2.设计思路 所采用的时钟为50M&#xff0c;先对时钟进行分频&#xff0c;得到100HZ频率…...

浅谈指数移动平均(ema)

经常在各种代码中看到指数移动平均(比如我专注的网络传输领域)&#xff0c;但却不曾想到它就是诠释世界的方法&#xff0c;我们每个人都在被这种方式 “平均”… 今天说说指数移动平均(或移动指数平均&#xff0c;Exponential Moving Average)。 能查到的资料都侧重于其数学形…...

1-并发编程线程基础

什么是线程 在讨论什么是线程前有必要先说下什么是进程&#xff0c;因为线程是进程中的一个实体&#xff0c;线程本身是不会独立存在的。 进程是代码在数据集合上的一次运行活动&#xff0c;是系统进行资源分配和调度的基本单位&#xff0c;线程则是进程的一个执行路径&#…...

vue中动态出来返回的时间秒数,在多少范围显示多少秒,多少范围显示分,小时等等

在Vue中&#xff0c;你可以使用计算属性&#xff08;computed property&#xff09;或过滤器&#xff08;filter&#xff09;来根据动态返回的时间秒数来显示不同的时间单位&#xff0c;比如秒、分、小时等等。 下面是一个使用计算属性的示例&#xff1a; <template>&l…...

English: go through customs

文章目录 常见单词机场指示登机和中转降落以及公共服务签证篇出/入境卡篇入境英语会话篇 常见单词 customs: 海关 (kʌstəmz)cash: 现金 (kʃ)passport: 护照 (pspɔːt)luggage/baggage: 行李 (lʌɡɪdʒ/ˈbɡɪdʒ)Exchange: 换钱 (ɪks’tʃeɪndʒ)airport: 飞机场 (ɛ…...

iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘

美国西海岸的夏天&#xff0c;再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至&#xff0c;这不仅是开发者的盛宴&#xff0c;更是全球数亿苹果用户翘首以盼的科技春晚。今年&#xff0c;苹果依旧为我们带来了全家桶式的系统更新&#xff0c;包括 iOS 26、iPadOS 26…...

Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?

Golang 面试经典题&#xff1a;map 的 key 可以是什么类型&#xff1f;哪些不可以&#xff1f; 在 Golang 的面试中&#xff0c;map 类型的使用是一个常见的考点&#xff0c;其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...

从零实现富文本编辑器#5-编辑器选区模型的状态结构表达

先前我们总结了浏览器选区模型的交互策略&#xff0c;并且实现了基本的选区操作&#xff0c;还调研了自绘选区的实现。那么相对的&#xff0c;我们还需要设计编辑器的选区表达&#xff0c;也可以称为模型选区。编辑器中应用变更时的操作范围&#xff0c;就是以模型选区为基准来…...

遍历 Map 类型集合的方法汇总

1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...

项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)

Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败&#xff0c;具体原因是客户端发送了密码认证请求&#xff0c;但Redis服务器未设置密码 1.为Redis设置密码&#xff08;匹配客户端配置&#xff09; 步骤&#xff1a; 1&#xff09;.修…...

安卓基础(aar)

重新设置java21的环境&#xff0c;临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的&#xff1a; MyApp/ ├── app/ …...

springboot整合VUE之在线教育管理系统简介

可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生&#xff0c;小白用户&#xff0c;想学习知识的 有点基础&#xff0c;想要通过项…...

Java求职者面试指南:计算机基础与源码原理深度解析

Java求职者面试指南&#xff1a;计算机基础与源码原理深度解析 第一轮提问&#xff1a;基础概念问题 1. 请解释什么是进程和线程的区别&#xff1f; 面试官&#xff1a;进程是程序的一次执行过程&#xff0c;是系统进行资源分配和调度的基本单位&#xff1b;而线程是进程中的…...

C#学习第29天:表达式树(Expression Trees)

目录 什么是表达式树&#xff1f; 核心概念 1.表达式树的构建 2. 表达式树与Lambda表达式 3.解析和访问表达式树 4.动态条件查询 表达式树的优势 1.动态构建查询 2.LINQ 提供程序支持&#xff1a; 3.性能优化 4.元数据处理 5.代码转换和重写 适用场景 代码复杂性…...

【Linux系统】Linux环境变量:系统配置的隐形指挥官

。# Linux系列 文章目录 前言一、环境变量的概念二、常见的环境变量三、环境变量特点及其相关指令3.1 环境变量的全局性3.2、环境变量的生命周期 四、环境变量的组织方式五、C语言对环境变量的操作5.1 设置环境变量&#xff1a;setenv5.2 删除环境变量:unsetenv5.3 遍历所有环境…...