Android之APP更新(通过接口更新)

文章目录

  • 前言
  • 一、效果图
  • 二、实现步骤
    • 1.AndroidManifest权限申请
    • 2.activity实现
    • 3.有版本更新弹框UpdateappUtilDialog
    • 4.下载弹框DownloadAppUtils
    • 5.弹框背景图
  • 总结


前言

对于做Android的朋友来说,APP更新功能再常见不过了,因为平台更新审核时间较长,所以现在很多都会通过接口更新,这里就记录一下吧,欢迎各位讨论。


一、效果图

在这里插入图片描述
在这里插入图片描述

二、实现步骤

1.AndroidManifest权限申请

    <!--外置存储卡写入权限--><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /><!--请求安装APK的权限--><uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" /><!--读取删除SD卡权限--><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /><!--修改删除SD卡权限--><uses-permissionandroid:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"tools:ignore="ProtectedPermissions" /><uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />//application标签下加如下代码<!-- 安卓7.0安装时需要,拍照需要的临时权限 --><providerandroid:name="androidx.core.content.FileProvider"android:authorities="这里是applicationId也就是包名.fileprovider"android:exported="false"android:grantUriPermissions="true"><meta-dataandroid:name="android.support.FILE_PROVIDER_PATHS"android:resource="@xml/provider_paths" /></provider>//这里的provider_paths文件放在xml文件下,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<paths><!--拍照需要的路径--><external-pathname="comxf.activity.provider.download"path="." /><external-files-path name="extfiles" path="autoupdatecache/" /><external-cache-path name="extcachs" path="autoupdatecache/" /><cache-path name="intcachs" path="autoupdatecache/" /><paths><external-path path="" name="download"/></paths></paths>

2.activity实现

1.通过接口获取Javabean对象

    public Integer id;public String version_name;//App版本名public Integer version_code;//版本编号public String content;//版本更新内容public String is_must_update;//是否需要强更新(0:否,1:是)public String file_url;//app包的下载地址@Overridepublic String toString() {return "VersionUpgradeBean{" +"id=" + id +", version_name='" + version_name + '\'' +", version_code=" + version_code +", content='" + content + '\'' +", is_must_update='" + is_must_update + '\'' +", file_url='" + file_url + '\'' +'}';}

2.获取到版本号后判断是否高于当前版本

  if (versionbean.version_code > MyApplication.getVersionCode(this@SetUpActivity)) {val myDialog = UpdateappUtilDialog()myDialog.initDialog(this@SetUpActivity, versionbean)myDialog.buttonClickEvent(object : UpdateappUtilDialog.DialogButtonClick {override fun cilckComfirmButton(view: View?) {myDialog.Dismiss()//6.0才用动态权限if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {if (ContextCompat.checkSelfPermission(this@SetUpActivity,Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {// 申请读写内存卡内容的权限ActivityCompat.requestPermissions(this@SetUpActivity, arrayOf(Manifest.permission.`WRITE_EXTERNAL_STORAGE`), 123)} else {//授权成功if (FileUtils.createOrExistsDir(NetConst.baseFileName)) {val mUpdateManger = DownloadAppUtils(this@SetUpActivity, versionbean.file_url, versionbean)mUpdateManger.checkUpdateInfo()}}} else {//授权成功if (FileUtils.createOrExistsDir(NetConst.baseFileName)) {val mUpdateManger = DownloadAppUtils(this@SetUpActivity, versionbean.file_url, versionbean)mUpdateManger.checkUpdateInfo()}}}override fun cilckCancleButton(view: View?) {//点击取消按钮myDialog.Dismiss()}})}

3.用户权限结果处理

  /*** todo 对用户权限授予结果处理** @param requestCode  权限要求码,即我们申请权限时传入的常量 如: TAKE_PHOTO_PERMISSION_REQUEST_CODE* @param permissions  保存权限名称的 String 数组,可以同时申请一个以上的权限* @param grantResults 每一个申请的权限的用户处理结果数组(是否授权)*/override fun onRequestPermissionsResult(requestCode: Int,permissions: Array<String?>,grantResults: IntArray) {super.onRequestPermissionsResult(requestCode, permissions, grantResults)when (requestCode) {123 -> {//授权成功if (FileUtils.createOrExistsDir(NetConst.baseFileName)) {val mUpdateManger = DownloadAppUtils(this@SetUpActivity, versionbean.file_url, versionbean)mUpdateManger.checkUpdateInfo()}}}}

3.有版本更新弹框UpdateappUtilDialog

1.util代码

/*** Created by :caoliulang* ❤* Creation time :2025/2/24* ❤* Function :更新app的Dialog*/
public class UpdateappUtilDialog {private TextView secondBtn, textvs;private ImageView tv_cancel;private AlertDialog alertDialog;private DialogButtonClick mClick;private TextView tv_desc;public interface DialogButtonClick {void cilckComfirmButton(View view);void cilckCancleButton(View view);}public void buttonClickEvent(DialogButtonClick bc) {if (bc != null) {mClick = bc;cilckEvent();}}public void initDialog(Context context, VersionUpgradeBean upbean) {alertDialog = new AlertDialog.Builder(context).create();alertDialog.show();alertDialog.setCancelable(false);//调用这个方法时,按对话框以外的地方不起作用。返回键 不作用
//        alertDialog.setCanceledOnTouchOutside(false);//调用这个方法时,按对话框以外的地方不起作用。返回键 有作用Window window = alertDialog.getWindow();window.setContentView(R.layout.fragment_update_app);window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));tv_desc = window.findViewById(R.id.tv_desc);tv_desc.setText(upbean.content);secondBtn = window.findViewById(R.id.tv_ok);textvs = window.findViewById(R.id.textvs);textvs.setText(upbean.version_name);tv_cancel = window.findViewById(R.id.tv_cancel);//1不强制更新  2 强制更新
//        if(upbean.getUpdateType().equals("2")){
//            firstBtn.setVisibility(View.GONE);
//        }//        //不关闭弹窗也能点击外部事件
//        alertDialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);String isqg = upbean.is_must_update + "";if (isqg.equals("true")) {tv_cancel.setVisibility(View.GONE);} else {tv_cancel.setVisibility(View.VISIBLE);}}public void cilckEvent() {
//        if (firstBtn != null) {
//            firstBtn.setOnClickListener(new View.OnClickListener() {
//                @Override
//                public void onClick(View v) {
//                    alertDialog.dismiss();
//                    mClick.cilckCancleButton(v);
//                }
//            });secondBtn.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {
//                    alertDialog.dismiss();mClick.cilckComfirmButton(v);}});tv_cancel.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {mClick.cilckCancleButton(view);}});
//        }}public void Dismiss() {if (null != alertDialog) {alertDialog.dismiss();}}}

2.xml布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#01000000"android:gravity="center"android:orientation="vertical"><RelativeLayoutandroid:layout_width="301dp"android:layout_height="400dp"android:orientation="vertical"><ImageViewandroid:id="@+id/iv_top"android:layout_width="match_parent"android:layout_height="match_parent"android:src="@mipmap/lib_update_app_top_bg" /><TextViewandroid:id="@+id/textname"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="16dp"android:layout_marginTop="55dp"android:text="@string/Newversionfound"android:textColor="#2e2e2e"android:textSize="18dp"android:textStyle="bold" /><TextViewandroid:id="@+id/textvs"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_below="@+id/textname"android:layout_marginLeft="16dp"android:layout_marginTop="5dp"android:text="1.0.0"android:textColor="#2e2e2e"android:textSize="16dp"android:textStyle="bold" /><LinearLayoutandroid:layout_width="match_parent"android:layout_height="210dp"android:layout_marginTop="180dp"android:orientation="vertical"><!--这个地方需要设置可以滚动--><ScrollViewandroid:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1"android:paddingTop="18dp"android:paddingBottom="5dp"android:scrollbars="none"><TextViewandroid:id="@+id/tv_desc"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginLeft="20dp"android:layout_marginRight="20dp"android:lineSpacingExtra="5dp"android:text="1.已知bug修复;\n2.新功能增加;\n3.性能优化;"android:textColor="@android:color/black"android:textSize="14dp" /></ScrollView><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginLeft="20dp"android:layout_marginRight="20dp"android:layout_marginBottom="20dp"android:orientation="horizontal"><TextViewandroid:id="@+id/tv_ok"android:layout_width="match_parent"android:layout_height="44dp"android:background="@drawable/jianbianlanse"android:gravity="center"android:text="@string/UPDATENOW"android:textColor="#ffffff"android:textSize="18dp" /></LinearLayout></LinearLayout></RelativeLayout><ImageViewandroid:id="@+id/tv_cancel"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="15dp"android:src="@mipmap/ico_sjgb" /></LinearLayout>

4.下载弹框DownloadAppUtils

1.util代码如下

*** Created by :caoliulang** Creation time :2024/2/24** FunctionAPP更新下载*/
public class DownloadAppUtils {// 应用程序Contextpublic static Activity mContext;private static final String savePath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + "tdzr/Download/";// 保存apk的文件夹///storage/emulated/0/tdzr/Download/aidigital1.0.4.apkprivate static final String saveFileName = savePath + "aidigital" + MyApplication.versionname + ".apk";// 进度条与通知UI刷新的handler和msg常量private ProgressBar mProgress;private TextView text_progress, textvs;private static final int DOWN_UPDATE = 1;private static final int DOWN_OVER = 2;private static final int INT_NOT = 9;private String downlogdurl;//下载地址private int progress = 0;// 当前进度private Thread downLoadThread; // 下载线程private boolean interceptFlag = false;// 用户取消下载private AlertDialog dialog;private VersionUpgradeBean upbean;// 通知处理刷新界面的handler@SuppressLint("HandlerLeak")private Handler mHandler = new Handler() {@SuppressLint("HandlerLeak")@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case DOWN_UPDATE:mProgress.setProgress(progress); //设置第一进度text_progress.setText(progress + "%");break;case DOWN_OVER:
//                    //1不强制更新  2 强制更新
//                    if(upbean.getUpdateType().equals("1")){
//                        dialog.dismiss();
//                    }dialog.dismiss();installApk();break;case INT_NOT:dialog.dismiss();FileUtils.deleteFile(saveFileName);//删除文件ToastUtils.showToast(mContext.getResources().getString(R.string.Networkconnectionfailed));break;}super.handleMessage(msg);}};public DownloadAppUtils(Activity context, String downlogdurl, VersionUpgradeBean upbean) {this.mContext = context;this.downlogdurl = downlogdurl;this.upbean = upbean;System.out.println("过来了--1");}// 显示更新程序对话框,供主程序调用public void checkUpdateInfo() {//判断本地是否存在已下载的apk(有bug)
//        if (FileUtils.isFileExists(saveFileName) == true) {
//            //安装
//            installApk();
//        } else {
//            //下载
//            showDownloadDialog();
//        }//判断本地是否存在已下载的apkif (FileUtils.isFileExists(saveFileName) == true) {//删除DeletFile.delete(savePath);}//下载showDownloadDialog();}//弹出更新进度条private void showDownloadDialog() {dialog = new AlertDialog.Builder(mContext).create();dialog.show();dialog.setCancelable(false);View viewt = View.inflate(mContext, R.layout.xiazai_two_dialog, null);Window window = dialog.getWindow();window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));dialog.setContentView(viewt);mProgress = viewt.findViewById(R.id.upprogressBar);text_progress = viewt.findViewById(R.id.text_progress);text_progress.setText("0%");textvs = viewt.findViewById(R.id.textvs);textvs.setText(upbean.version_name);ImageView tv_cancel = viewt.findViewById(R.id.tv_cancel);String isqg = upbean.is_must_update + "";if (isqg.equals("true")) {tv_cancel.setVisibility(View.GONE);} else {tv_cancel.setVisibility(View.VISIBLE);}tv_cancel.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {FileUtils.deleteFile(saveFileName);//删除文件dialog.dismiss();interceptFlag = true;}});downloadApk();}//安装apkprivate static void installApk() {File apkfile = new File(saveFileName);if (!apkfile.exists()) {return;}Intent intent = new Intent(Intent.ACTION_VIEW);//版本在7.0以上是不能直接通过uri访问的if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {// 由于没有在Activity环境下启动Activity,设置下面的标签intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);//参数1 上下文, 参数2 Provider主机地址 和配置文件中保持一致   参数3  共享的文件Uri apkUri = FileProvider.getUriForFile(mContext, "这里是applicationId也就是包名.fileprovider", apkfile);//添加这一句表示对目标应用临时授权该Uri所代表的文件intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);intent.setDataAndType(apkUri, "application/vnd.android.package-archive");} else {intent.setDataAndType(Uri.fromFile(new File(saveFileName)),"application/vnd.android.package-archive");}mContext.startActivity(intent);}//下载apkprivate void downloadApk() {System.out.println("url打印:" + downlogdurl);downLoadThread = new Thread(mdownApkRunnable);downLoadThread.start();}private Runnable mdownApkRunnable = new Runnable() {@Overridepublic void run() {System.out.println("打印路径:" + saveFileName);URL url;try {url = new URL(downlogdurl);HttpURLConnection conn = (HttpURLConnection) url.openConnection();conn.setConnectTimeout(15000);  //设置连接超时为15sconn.setReadTimeout(15000);     //读取数据超时也是15sint code = conn.getResponseCode();System.out.println("code打印:" + code);if (code == 200) {conn.connect();int length = conn.getContentLength();InputStream ins = conn.getInputStream();if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {Toast.makeText(mContext, "SD卡不可用~", Toast.LENGTH_SHORT).show();return;}File file = new File(savePath);if (!file.exists()) {file.mkdir();}String apkFile = saveFileName;File ApkFile = new File(apkFile);if (!ApkFile.exists()) {ApkFile.createNewFile();}FileOutputStream outStream = new FileOutputStream(ApkFile);int count = 0;byte buf[] = new byte[1024];do {int numread = ins.read(buf);count += numread;int s = (int) (((float) count / length) * 100);if (progress != s) {progress = (int) (((float) count / length) * 100);// 下载进度mHandler.sendEmptyMessage(DOWN_UPDATE);}if (numread <= 0) {// 下载完成通知安装mHandler.sendEmptyMessage(DOWN_OVER);break;}outStream.write(buf, 0, numread);} while (!interceptFlag);// 点击取消停止下载outStream.close();ins.close();} else {mHandler.sendEmptyMessage(INT_NOT);}} catch (Exception e) {//这里报错因为路径不对,正确路径storage/emulated/0/tdzr/Download/aidigital1.0.4.apkSystem.out.println("code打印:---" + e.toString());mHandler.sendEmptyMessage(INT_NOT);}}};

2.下载弹框xml布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#01000000"android:gravity="center"android:orientation="vertical"><RelativeLayoutandroid:layout_width="301dp"android:layout_height="400dp"android:orientation="vertical"><RelativeLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"><ImageViewandroid:id="@+id/topimg"android:layout_width="match_parent"android:layout_height="match_parent"android:src="@mipmap/lib_update_app_top_bg" /><TextViewandroid:id="@+id/textname"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="16dp"android:layout_marginTop="55dp"android:text="@string/Newversionfound"android:textColor="#2E2E2E"android:textSize="18dp"android:textStyle="bold" /><TextViewandroid:id="@+id/textvs"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_below="@+id/textname"android:layout_marginLeft="16dp"android:layout_marginTop="5dp"android:text="1.0.0"android:textColor="#2E2E2E"android:textSize="16dp"android:textStyle="bold" /></RelativeLayout><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="250dp"android:gravity="center_horizontal"android:orientation="vertical"><TextViewandroid:id="@+id/text_progress"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:text="0%"android:textColor="#2e2e2e"android:textSize="16dp"android:textStyle="bold" /><ProgressBarandroid:id="@+id/upprogressBar"style="@style/Base.Widget.AppCompat.ProgressBar.Horizontal"android:layout_width="match_parent"android:layout_height="18dp"android:layout_marginStart="15dp"android:layout_marginTop="15dp"android:layout_marginEnd="15dp"android:layout_marginBottom="15dp"android:max="100"android:progress="0"android:progressDrawable="@drawable/progressbar_h" /></LinearLayout></RelativeLayout><ImageViewandroid:id="@+id/tv_cancel"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="15dp"android:src="@mipmap/ico_sjgb" />
</LinearLayout>

3.progressbar_h(在drawable下新建文件)

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"><itemandroid:id="@android:id/background"android:gravity="center_vertical|fill_horizontal"><shape android:shape="rectangle"><size android:height="20dp" /><solid android:color="#E4E4E4" /><corners android:radius="10dp" /></shape></item><!--第二进度条--><itemandroid:id="@android:id/secondaryProgress"android:gravity="center_vertical|fill_horizontal"><scale android:scaleWidth="100%"><shape android:shape="rectangle"><size android:height="20dp" /><solid android:color="#E4E4E4" /><corners android:radius="10dp" /></shape></scale></item><!--第一进度条--><itemandroid:id="@android:id/progress"android:gravity="center_vertical|fill_horizontal"><scale android:scaleWidth="100%"><shape android:shape="rectangle"><size android:height="20dp" /><solid android:color="#217FFD" /><corners android:radius="10dp" /></shape></scale></item></layer-list>

5.弹框背景图

在这里插入图片描述


总结

其实这功能挺简单的,就是里面细节太多,首先AndroidManifest加权限,还要加provider,provider里面记得更改包名,下载util里面的报名也是一样。其次判断版本是否更新,最后更新下载安装即可,得吃。

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

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

相关文章

AI触手可及 | 基于函数计算玩转AI大模型

AI触手可及 | 基于函数计算玩转AI大模型 基于函数计算部署AI大模型的优势方案架构图像生成 - Stable Diffusion WebUI部署操作 释放资源部署总结体验反馈 在生成式AI技术加速迭代的浪潮下&#xff0c;百亿级参数的行业大模型正推动产业智能化范式转移。面对数字化转型竞赛&…

DDD该怎么去落地实现(4)多对多关系

多对多关系的设计实现 如题&#xff0c;DDD该如何落地呢&#xff1f;前面我通过三期的内容&#xff0c;讲解了DDD落地的关键在于“关系”&#xff0c;也就是通过前面我们对业务的理解先形成领域模型&#xff0c;然后将领域模型的原貌&#xff0c;形成程序代码中的服务、实体、…

【补阙拾遗】排序之冒泡、插入、选择排序

炉烟爇尽寒灰重&#xff0c;剔出真金一寸明 冒泡排序1. 轻量化情境导入 &#x1f30c;2. 边界明确的目标声明 &#x1f3af;3. 模块化知识呈现 &#x1f9e9;&#x1f4ca; 双循环结构对比表★★★⚠️ 代码关键点注释 4. 嵌入式应用示范 &#x1f6e0;️5. 敏捷化巩固反馈 ✅ …

前端面试题---小程序跟vue的声明周期的区别

1. 小程序生命周期 小程序的生命周期主要分为 页面生命周期 和 应用生命周期。每个页面和应用都有自己独立的生命周期函数。 应用生命周期 小程序的应用生命周期函数与全局应用相关&#xff0c;通常包括以下几个钩子&#xff1a; onLaunch(options)&#xff1a;应用初始化时触…

【芯片设计】NPU芯片前端设计工程师面试记录·20250227

应聘公司 某NPU/CPU方向芯片设计公司。 小声吐槽两句,前面我问了hr需不需要带简历,hr不用公司给打好了,然后我就没带空手去的。结果hr小姐姐去开会了,手机静音( Ĭ ^ Ĭ )面试官、我、另外的hr小姐姐都联系不上,结果就变成了两个面试官和我一共三个人在会议室里一人拿出…

让Word插上AI的翅膀:如何把DeepSeek装进Word

在日常办公中&#xff0c;微软的Word无疑是我们最常用的文字处理工具。无论是撰写报告、编辑文档&#xff0c;还是整理笔记&#xff0c;Word都能胜任。然而&#xff0c;随着AI技术的飞速发展&#xff0c;尤其是DeepSeek的出现&#xff0c;我们的文字编辑方式正在发生革命性的变…

点击修改按钮图片显示有问题

问题可能出在表单数据的初始化上。在 ave-form.vue 中&#xff0c;我们需要处理一下从后端返回的图片数据&#xff0c;因为它们可能是 JSON 字符串格式。 vue:src/views/tools/fake-strategy/components/ave-form.vue// ... existing code ...Watch(value)watchValue(v: any) …

vue深拷贝:1、使用JSON.parse()和JSON.stringify();2、使用Lodash库;3、使用深拷贝函数(采用递归的方式)

文章目录 引言三种方法的优缺点在Vue中,实现数组的深拷贝I JSON.stringify和 JSON.parse的小技巧深拷贝步骤缺点:案例1:向后端请求路由数据案例2: 表单数据处理时复制用户输入的数据II 使用Lodash库步骤适用于复杂数据结构和需要处理循环引用的场景III 自定义的深拷贝函数(…

线性模型 - 支持向量机

支持向量机&#xff08;SVM&#xff09;是一种用于分类&#xff08;和回归&#xff09;的监督学习算法&#xff0c;其主要目标是找到一个最佳决策超平面&#xff0c;将数据点分为不同的类别&#xff0c;并且使得分类边界与最近的数据点之间的间隔&#xff08;margin&#xff09…

记录一次解决springboot需要重新启动项目才能在前端界面展示静态资源的问题--------使用热部署解决

问题 使用sprinbootthymeleaf&#xff0c;前后端不分离&#xff0c;一个功能是用户可以上传图片&#xff0c;之后可以在网页展示。用户上传的图片能在对应的静态资源目录中找到&#xff0c;但是在target目录没有&#xff0c;导致无法显示在前端界面 解决 配置热部署 <depe…

【Python pro】函数

1、函数的定义及调用 1.1 为什么需要函数 提高代码复用性——封装将复杂问题分而治之——模块化利于代码的维护和管理 1.1.1 顺序式 n 5 res 1 for i in range(1, n1):res * i print(res) # 输出&#xff1a;1201.1.2 抽象成函数 def factorial(n):res 1for i in range(1…

[Web 信息收集] Web 信息收集 — 手动收集 IP 信息

关注这个专栏的其他相关笔记&#xff1a;[Web 安全] Web 安全攻防 - 学习手册-CSDN博客 0x01&#xff1a;通过 DNS 服务获取域名对应 IP DNS 即域名系统&#xff0c;用于将域名与 IP 地址相互映射&#xff0c;方便用户访问互联网。对于域名到 IP 的转换过程则可以参考下面这篇…

大语言模型的评测

大语言模型评测是评估这些模型在各种任务和场景下的性能和能力的过程。 能力 1. 基准测试&#xff08;Benchmarking&#xff09; GLUE&#xff08;General Language Understanding Evaluation&#xff09;&#xff1a;包含多个自然语言处理任务&#xff0c;如文本分类、情感分…

Node.js与MySQL的深入探讨

Node.js与MySQL的深入探讨 引言 Node.js,一个基于Chrome V8引擎的JavaScript运行时环境,以其非阻塞、事件驱动的方式在服务器端应用中占据了一席之地。MySQL,作为一款广泛使用的开源关系型数据库管理系统,凭借其稳定性和高效性,成为了许多应用的数据库选择。本文将深入探…

STM32--SPI通信讲解

前言 嘿&#xff0c;小伙伴们&#xff01;今天咱们来聊聊STM32的SPI通信。SPI&#xff08;Serial Peripheral Interface&#xff09;是一种超常用的串行通信协议&#xff0c;特别适合微控制器和各种外设&#xff08;比如传感器、存储器、显示屏&#xff09;之间的通信。如果你…

基于定制开发开源AI大模型S2B2C商城小程序的商品选品策略研究

摘要&#xff1a;随着电子商务的蓬勃发展和技术的不断进步&#xff0c;商品选品在电商领域中的重要性日益凸显。特别是在定制开发开源AI大模型S2B2C商城小程序的环境下&#xff0c;如何精准、高效地选择推广商品&#xff0c;成为商家面临的一大挑战。本文首先分析了商品选品的基…

C#异步编程之async与await

一&#xff1a;需求起因 在 C# 中使用异步编程&#xff08;特别是使用 async 和 await 关键字&#xff09;通常是为了提高应用程序的响应性和性能&#xff0c;特别是在需要进行 I/O 操作或执行长时间运行的任务时。 常见应用场景如下&#xff1a; 1. 网络请求 HTTP 请求&…

PMP项目管理—整合管理篇—7.结束项目或阶段

文章目录 基本信息过程4W1HITTO输入工具与技术输出 收尾过程组项目收尾&#xff08;结束项目或阶段&#xff09;行政收尾/管理收尾 合同收尾&#xff08;结束采购&#xff09; 最终报告 基本信息 项目无论何因何时终止&#xff0c;都必须用结束项目或阶段过程来正式关闭。通过…

labview中VISA串口出现异常的解决方案

前两天在做项目时发现&#xff0c;当用VISA串口读取指令时出现了回复异常的情况&#xff0c;不管发什么东西就一直乱回&#xff0c;针对这个情况&#xff0c;后面在VISA串口中加了一个VISA寄存器清零的函数。加了之后果然好多了&#xff0c;不会出现乱回的情况&#xff0c;但是…

【GO】学习笔记

目录 学习链接 开发环境 开发工具 GVM - GO多版本部署 GOPATH 与 go.mod go常用命令 环境初始化 编译与运行 GDB -- GNU 调试器 基本语法与字符类型 关键字与标识符 格式化占位符 基本语法 初始值&零值&默认值 变量声明与赋值 _ 下划线的用法 字…