Android入门第56天-在Android里使用OKHttp多线程下载文件并展示其进度

news2024/11/17 2:59:59

简介

OkHttp是一个神器。OkHttp分为异步、同步两种调用。今天我们就会基于OkHttp的异步调用实现一个多线程并行下载文件并以进度条展示总进度的实用例子。当然这不是我们的Android里使用OkHttp的最终目标,我们最终在下一篇中会在今天这一课的基础上加入“断点续传”的功能,从而以这么连续的几篇从易到难的循序渐进的过程,让大家熟悉和掌握Android中使用OkHttp的技巧以便于形成大脑的“肌肉记忆”。

课程目标

 

  1. 熟悉OkHttp的同步、异步调用;
  2. 实现n个线程并行下载文件;
  3. 使用线程中的回调机制实时传输下载时的进度;
  4. 用进度条辅助显示我们的整体下载进度;

OkHttp的同步调用例子

    public int getDownloadFileSize(String downloadUrl) throws Exception {
        int size = -1;
        OkHttpClient client = new OkHttpClient.Builder()
                .connectTimeout(10, TimeUnit.SECONDS)//设置连接超时时间
                .readTimeout(10, TimeUnit.SECONDS).build();//设置读取超时时间
        Request request = new Request.Builder().url(downloadUrl)//请求接口,如果需要传参拼接到接口后面
                .build(); //创建Request对象
        Response response = null;
        try {
            Call call = client.newCall(request);
            response = call.execute();
            if (200 == response.code()) {
                Log.d(TAG, ">>>>>>response.code()==" + response.code());
                Log.d(TAG, ">>>>>>response.message()==" + response.message());
                try {
                    size = (int) response.body().contentLength();
                    Log.d(TAG, ">>>>>>file length->" + size);
                    //fileSizeListener.onHttpResponse((int) size);
                } catch (Exception e) {
                    Log.e(TAG, ">>>>>>get remote file size error: " + e.getMessage(), e);
                }
            }
        } catch (Exception e) {
            Log.e(TAG, ">>>>>>open connection to path->" + downloadUrl + "\nerror: " + e.getMessage(), e);
            throw new Exception(">>>>>>getDownloadFileSize from->" + downloadUrl + "\nerror: " + e.getMessage(), e);
        } finally {
            try {
                response.close();
            } catch (Exception e) {
            }
        }
        return size;
    }

这是一个OkHttp的同步调用例子,访问后根据response.code来作出响应并取response.body()的内容做出相应的业务处理。

OkHttp的异步调用例子

            OkHttpClient client = new OkHttpClient();
            Request request = new Request.Builder()
                    .url(downloadFilePath)//请求接口,如果需要传参拼接到接口后面
                    .build(); //创建Request对象
            Log.d(TAG, ">>>>>>线程" + (threadId + 1) + "开始下载...");
            Call call = client.newCall(request);
            //异步请求
            call.enqueue(new Callback() {
                //失败的请求
                @Override
                public void onFailure(@NonNull Call call, @NonNull IOException e) {
                    Log.e(TAG, ">>>>>>下载进程加载->" + downloadFilePath + " error:" + e.getMessage(), e);
                }

                //结束的回调
                @Override
                public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
                    Log.d(TAG, ">>>>>>连接->" + downloadFilePath + " 己经连接,进入下载...");
                    InputStream is = null;
                    try {
                        if (200 == response.code()) {
                            Log.d(TAG, ">>>>>>response.code()==" + response.code());
                            Log.d(TAG, ">>>>>>response.message()==" + response.message());
                            is = response.body().byteStream();
                            byte[] buffer = new byte[1024];
                            int len = -1;
                            int length = 0;
                            while (length < threadLength && (len = is.read(buffer)) != -1) {
                                threadFile.write(buffer, 0, len);
                                //计算累计下载的长度
                                length += len;
                                downloadListener.onDownload(length,totalSize);
                            }
                            Log.d(TAG, ">>>>>>线程" + (threadId + 1) + "已下载完成");
                        }
                    } catch (Exception e) {
                        Log.e(TAG, ">>>>>>线程:" + threadId + " 下载出错: " + e.getMessage(), e);
                    } finally {
                        try {
                            threadFile.close();
                        } catch (Exception e) {
                        }
                        try {
                            is.close();
                            ;
                        } catch (Exception e) {
                        }
                    }

                }
            });

这是一个OkHttp的异步调用例子,我们可以看到它首先以call.enqueue来执行调用,然后至少有2个回调方法:onFailure和onResponse需要自己覆盖来实现业务功能。

各位记得同步、异步的调用还是有很大区别的。比如说有以下调用顺序:

  • OkHttp调用
  • A方法根据OkHttp调用后的结果再执行B

此时你就必须使用同步调用,而不能使用异步。因为如果你用的是异步很有可能B在执行到一半时,第一步OkHttp调用的结果才刚刚到达。

多线程并行下载文件需要解决的几个核心问题

如何从一个远程Http资源得到文件的尺寸

OkHttpClient client = new OkHttpClient.Builder()
                .connectTimeout(10, TimeUnit.SECONDS)//设置连接超时时间
                .readTimeout(10, TimeUnit.SECONDS).build();//设置读取超时时间
Request request = new Request.Builder().url(downloadUrl)
                .build(); //创建Request对象
Response response = null;
Call call = client.newCall(request);
response = call.execute();
if (200 == response.code()) {
   size = (int) response.body().contentLength();
}

我们通过response.body().contentLength()即可以获得远程资源文件的尺寸了。

如何对一个本地文件进行并行的写

在这儿,我们使用RandomAccessFile来进行并行写操作。因为RandomAccessFile里有一个seek属性。

seek即写文件起始、结束位置。因此我们设每个不同的线程处理自己的start-end的位置就可以做到对文件进行并行写操作了。为此我们需要执行以下这么几步:

创建一个长度=远程资源长度的空的文件

先创建一个空的RandomAccessFile,并把远程资源的长度以如下的API set进去;

file.setLength(fileLength)

为每个线程分配写入的start-end区间段

假设我们有n个线程,每个线程写文件的长度可以用以下公式得到:

int threadlength = (int) fileLength % threadCount == 0 
                   ? (int) fileLength / threadCount : (int) fileLength + 1;

当得到了threadLength即每个线程固定写入的长度后我们就可以得到每个线程起始的写文件位置即: startPosition。

int startPosition = threadNo * threadlength;

 每个线程在写入操作时需要先进行:seek(起始位)。

threadFile.seek(startPosition);

然后在写时每个线程不得超过自己被分配的固定长度,到了写入的固定长度后就结束写操作。

is = response.body().byteStream();
byte[] buffer = new byte[1024];
int len = -1;
int length = 0;
while (length < threadLength && (len = is.read(buffer)) != -1) {
        threadFile.write(buffer, 0, len);
        //计算累计下载的长度
        length += len;

}

如何在子线程里把当前正写入的文件的size传给Android界面以作进度展示

答案就是:回调函数。

我们先设一个接口如下

package org.mk.android.demo.http;

public interface DownloadListener {
    public void onDownload(int size,int totalSize);
}

然后我们在线程实例化时需要转入这个接口

public DownLoadThread(int threadId, int startPosition,
                              RandomAccessFile threadFile, int threadLength, String downloadFilePath,DownloadListener downloadListener,

在写文件时我们作如下操作

while (length < threadLength && (len = is.read(buffer)) != -1) {
       threadFile.write(buffer, 0, len);
       //计算累计下载的长度
       length += len;
       downloadListener.onDownload(length,totalSize);
}

而在外层调用时如下实现这个onDownload

multiDownloadHelper.download(new DownloadListener() {
     @Override
      public void onDownload(int size, int totalSize) {
            Log.d(TAG, ">>>>>>download size->" + size);
            float progress = ((float) size / (float) fileSize) * 100;
            int pgValue = (int) progress;
      }
});

就可以在多线程的最外层得到当前写文件的“进度”了。

下面就给出全代码。

全代码

前端

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/buttonDownload"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="download"
        android:textSize="20sp" />


    <ProgressBar
        android:id="@+id/progressBarDownload"
        style="@android:style/Widget.DeviceDefault.Light.ProgressBar.Horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:max="100" />

</LinearLayout>

后端

DownloadListener

package org.mk.android.demo.http;

public interface DownloadListener {
    public void onDownload(int size,int totalSize);
}

MultiDownloadHelper

package org.mk.android.demo.http;

import android.os.Environment;
import android.util.Log;

import androidx.annotation.NonNull;

import org.apache.commons.io.FilenameUtils;

import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.URL;
import java.util.EnumMap;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

public class MultiDownloadHelper {
    private static final String TAG = "DemoMultiDownloadWithProgressBar";
    private int threadCount = 0;
    private String downloadFilePath = "";

    public MultiDownloadHelper(int threadCount, String filePath) {
        this.threadCount = threadCount;
        this.downloadFilePath = filePath;
    }

    private enum DownLoadThreadInfor {
        threadLength, startPosition
    }

    private EnumMap<DownLoadThreadInfor, Object> calcStartPosition(long fileLength, int threadNo) {
        int threadlength = (int) fileLength % threadCount == 0 ? (int) fileLength / threadCount : (int) fileLength + 1;
        int startPosition = threadNo * threadlength;
        EnumMap<DownLoadThreadInfor, Object> downloadThreadInfor = new EnumMap<DownLoadThreadInfor, Object>(DownLoadThreadInfor.class);
        downloadThreadInfor.put(DownLoadThreadInfor.threadLength, threadlength);
        downloadThreadInfor.put(DownLoadThreadInfor.startPosition, startPosition);
        return downloadThreadInfor;
    }

    private String generateTempFile(String filePath, long fileLength) throws Exception {
        String end = filePath.substring(filePath.lastIndexOf("."));
        URL url = new URL(filePath);
        //String downloadFilePath = "Cache_" + System.currentTimeMillis() + end;
        String urlFileName=FilenameUtils.getName(url.getPath());
        RandomAccessFile file = null;
        try {
            if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
                String fileName = Environment.getExternalStorageDirectory().getCanonicalPath() + "/" + urlFileName;
                Log.d(TAG,">>>>>>写入->"+fileName);
                file = new RandomAccessFile(fileName, "rwd");
                file.setLength(fileLength);
                return fileName;
            } else {
                throw new Exception("SD卡不可读写");
            }
        } catch (Exception e) {
            throw new Exception("GenerateTempFile error: " + e.getMessage(), e);
        } finally {
            try {
                file.close();
            } catch (Exception e) {
            }
        }

    }

    private class DownLoadThread extends Thread {
        private int threadId;
        private int startPosition;
        private RandomAccessFile threadFile;
        private int threadLength;
        private String downloadFilePath;
        private DownloadListener downloadListener;
        private int totalSize=0;
        public DownLoadThread(int threadId, int startPosition,
                              RandomAccessFile threadFile, int threadLength, String downloadFilePath,DownloadListener downloadListener,
                int totalSize) {
            this.threadId = threadId;
            this.startPosition = startPosition;
            this.threadFile = threadFile;
            this.threadLength = threadLength;
            this.downloadFilePath = downloadFilePath;
            this.downloadListener=downloadListener;
            this.totalSize=totalSize;
        }

        public void run() {
            OkHttpClient client = new OkHttpClient();
            Request request = new Request.Builder()
                    .url(downloadFilePath)//请求接口,如果需要传参拼接到接口后面
                    .build(); //创建Request对象
            Log.d(TAG, ">>>>>>线程" + (threadId + 1) + "开始下载...");
            Call call = client.newCall(request);
            //异步请求
            call.enqueue(new Callback() {
                //失败的请求
                @Override
                public void onFailure(@NonNull Call call, @NonNull IOException e) {
                    Log.e(TAG, ">>>>>>下载进程加载->" + downloadFilePath + " error:" + e.getMessage(), e);
                }

                //结束的回调
                @Override
                public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
                    Log.d(TAG, ">>>>>>连接->" + downloadFilePath + " 己经连接,进入下载...");
                    InputStream is = null;
                    try {
                        if (200 == response.code()) {
                            Log.d(TAG, ">>>>>>response.code()==" + response.code());
                            Log.d(TAG, ">>>>>>response.message()==" + response.message());
                            is = response.body().byteStream();
                            byte[] buffer = new byte[1024];
                            int len = -1;
                            int length = 0;
                            while (length < threadLength && (len = is.read(buffer)) != -1) {
                                threadFile.write(buffer, 0, len);
                                //计算累计下载的长度
                                length += len;
                                downloadListener.onDownload(length,totalSize);
                            }
                            Log.d(TAG, ">>>>>>线程" + (threadId + 1) + "已下载完成");
                        }
                    } catch (Exception e) {
                        Log.e(TAG, ">>>>>>线程:" + threadId + " 下载出错: " + e.getMessage(), e);
                    } finally {
                        try {
                            threadFile.close();
                        } catch (Exception e) {
                        }
                        try {
                            is.close();
                            ;
                        } catch (Exception e) {
                        }
                    }

                }
            });

        }
    }

    public void download(DownloadListener downloadListener) {
        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder()
                .url(downloadFilePath)//请求接口,如果需要传参拼接到接口后面
                .build(); //创建Request对象
        try {
            Call call = client.newCall(request);
            //异步请求
            call.enqueue(new Callback() {
                //失败的请求
                @Override
                public void onFailure(@NonNull Call call, @NonNull IOException e) {
                    Log.e(TAG, ">>>>>>加载->" + downloadFilePath + " error:" + e.getMessage(), e);
                }

                //结束的回调
                @Override
                public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
                    //响应码可能是404也可能是200都会走这个方法
                    Log.i(TAG, ">>>>>>the response code is: " + response.code());
                    if (200 == response.code()) {
                        Log.d(TAG, ">>>>>>response.code()==" + response.code());
                        Log.d(TAG, ">>>>>>response.message()==" + response.message());
                        try {
                            long size = response.body().contentLength();
                            Log.d(TAG, ">>>>>>file length->" + size);
                            for (int i = 0; i < threadCount; i++) {
                                EnumMap<DownLoadThreadInfor, Object> downLoadThreadInforObjectEnumMap = new EnumMap<DownLoadThreadInfor, Object>(DownLoadThreadInfor.class);
                                downLoadThreadInforObjectEnumMap = calcStartPosition(size, i);
                                String threadFileName = generateTempFile(downloadFilePath, size);
                                int startPosition = (int) downLoadThreadInforObjectEnumMap.get(DownLoadThreadInfor.startPosition);
                                int threadLength = (int) downLoadThreadInforObjectEnumMap.get(DownLoadThreadInfor.threadLength);
                                RandomAccessFile threadFile = new RandomAccessFile(threadFileName, "rwd");
                                threadFile.seek(startPosition);
                                new DownLoadThread(i, startPosition, threadFile, threadLength, downloadFilePath,downloadListener,(int)size).start();
                                Log.d(TAG, ">>>>>>start thread: " + i + 1 + " start position->" + startPosition);
                            }
                        } catch (Exception e) {
                            Log.e(TAG, ">>>>>>get remote file size error: " + e.getMessage(), e);
                        }
                    }
                }
            });
        } catch (Exception e) {
            Log.e(TAG, ">>>>>>open connection to path->" + downloadFilePath + "\nerror: " + e.getMessage(), e);
        }
    }
}

MainActivity

package org.mk.android.demo.http;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.provider.Settings;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "DemoMultiDownloadWithProgressBar";
    private static final String picUrl = "https://tqjimg.tianqistatic.com/toutiao/images/202106/08/3721f7ae444ddfc4.jpg";
    private Button buttonDownload;
    private ProgressBar progressBarDownload;
    private Context ctx=null;

    private Handler downloadHandler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(@NonNull Message msg) {
            Log.i(TAG, ">>>>>>receive handler Message msg.what is: " + msg.what);
            switch (msg.what) {
                case 101:
                    //Toast.makeText(ctx, "下载图片完成", Toast.LENGTH_LONG).show();
                    progressBarDownload.setVisibility(View.VISIBLE);
                    //progressBarDownload.setProgress();
                    int inputNum = msg.getData().getInt("pgValue");
                    progressBarDownload.setProgress(inputNum);
                    if (inputNum >= 100) {
                        Toast.makeText(ctx, "下载图片完成", Toast.LENGTH_LONG).show();
                    }
                    break;
            }
            return false;
        }
    });

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ctx=getApplicationContext();
        buttonDownload = (Button) findViewById(R.id.buttonDownload);
        progressBarDownload = (ProgressBar) findViewById(R.id.progressBarDownload);
        progressBarDownload.setVisibility(View.GONE);
        progressBarDownload.setMax(100);
        buttonDownload.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
                    Log.i(TAG, ">>>>>>version.SDK->" + Build.VERSION.SDK_INT);
                    if (!Environment.isExternalStorageManager()) {
                        Intent intent = new Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);
                        startActivity(intent);
                        return;
                    }
                }
                MultiDownloadHelper multiDownloadHelper = new MultiDownloadHelper(3, picUrl);
                multiDownloadHelper.download(new DownloadListener() {
                    @Override
                    public void onDownload(int size, int totalSize) {
                        Log.d(TAG, ">>>>>>download size->" + size);
                        float progress = ((float) size / (float) totalSize) * 100;
                        int pgValue = (int) progress;
                        Message msg = new Message();
                        msg.what = 101;
                        Bundle bundle = new Bundle();
                        bundle.putInt("pgValue", pgValue);
                        msg.setData(bundle);
                        downloadHandler.sendMessage(msg);
                        Log.d(TAG, ">>>>>>current pgValue->" + progress);
                    }
                });
            }
        });
    }
}

 此处需要注意的点是

  1. 需要使用异步线程去驱动我们的多线程;
  2. 使用handler技术来处理进度条的界面变化;

结束今天的课程。

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

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

相关文章

【我在异世界学Linux】认识冯诺依曼体系结构

文章目录一、冯诺依曼体系结构是什么二、冯诺依曼为什么要这么设计&#xff1f;三、内存是怎么提高效率的呢&#xff1f;解释&#xff1a;程序要运行&#xff0c;必须加载到内存四、和QQ好友聊天的时候&#xff0c;数据是怎么流向的&#xff1f;一、冯诺依曼体系结构是什么 冯诺…

教你使用Java开发一款简单的扫雷小游戏 附实例代码

相信很多小伙伴都知道也玩过扫雷游戏,本篇文章将和大家分享一篇关于如何使用Java来实现一款简单的扫雷小游戏,这有助于大家对于Java相关知识的学习有一定的参考价值,下面是详情内容。 简介 学了几周的Java,闲来无事,写个乞丐版的扫雷,加强一下Java基础知识。 编写过程…

树莓派4b串口配置

从树莓派的相关资料我们可以看到&#xff0c;树莓派有两个串口可以使用&#xff0c;一个是硬件串口&#xff08;/dev/ttyAMA0&#xff09;,另一个是mini串口&#xff08;/dev/ttyS0&#xff09;。硬件串口有单独的波特率时钟源&#xff0c;性能好&#xff0c;稳定性强&#xff…

【Java寒假打卡】Java基础-接口

【Java寒假打卡】Java基础-接口接口的介绍接口的定义和特点接口中的成员特点JDK8 接口中的成员特点JDK9 接口中的成员特点类和接口的关系接口的介绍 &emsp&#xff1b;当一个类中的所有方法都是抽象方法的时候&#xff0c;我们就可以将其定义为接口&#xff0c;接口也是一…

Redis 缓存数据库

目录Redis 高可用方案高可用概念Redis 高可用的实现方案1、主从模式2、哨兵模式3、集群模式Redis 高可用方案 高可用概念 高可用&#xff08;High Availability&#xff0c;既HA&#xff09;&#xff0c;指的是通过尽量缩短日常维护操作和减少突发系统奔溃锁导致的停机时间来提…

Vagrant 安装 Centos7

首先准备VirtualBox, 当前实验版本为&#xff1a;7.0.4-154605-Win&#xff1b; 再次下载Vagrant windows版本&#xff1a;当前实验版本&#xff1a;vagrant_2.3.4_windows_i686 如果安装VirtualBox过程中提示缺少&#xff1a;Microsoft Visual C 2019 Redistributable &…

MySQL索引概述

索引的英文名叫 index 在数据库是一种帮助MySQL高效获取数据的数据结构 而且是一种有序的数据结构 在数据之外&#xff0c;数据库系统还维护着满足特定查找算法的数据结构&#xff0c;这些数据结构以某种方式引用(指向)数据&#xff0c; 这样就可以在这些数据结构上实现高级找…

公钥基础设施 时间戳规范测评

声明 本文是学习信息安全技术 公钥基础设施 标准符合性测评. 下载地址而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 公钥基础设施 时间戳规范测评 时间戳的产生和颁发 申请和颁发方式 测评依据见GB/T 20520—2006中6.1的内容。 开发者应提供文档…

2.0、Linux-基础了解

2.0、开机关机和基本目录介绍 开机登录&#xff1a; 开会机会启动许多程序&#xff1b;他们在Windows叫做 "服务" &#xff0c;在 Linux 中叫做 "守护进程"&#xff08;daemon&#xff09;&#xff1b; 开机成功后&#xff0c;他会显示一个文本登录…

Tailoring Self-Supervision for Supervised Learning-读后总结

Tailoring Self-Supervision for Supervised Learning摘要文章思路一些值得读的参考文献和技术&#xff1a;值得一读的高引文献可视化技术摘要 近期&#xff0c;在监督学习中部署一个合适的自监督学习来提高监督学习的性能是一个具有前景的方式。然而&#xff0c;因为之前的前…

MP中定义全局常量用于xml的判断,List<String> list = new ArrayList<>(Arrays.asList(“test“));

,1.普通方式 mybatis-plus.configuration.variables.secretFilterSwitch0 yml的方式 mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.demo configuration: variables: userId: 456132465 userName: 李四 配置完成后在代…

人文社科类文献去哪些数据库检索下载

查找下载人文社科类文献的数据库大盘点&#xff1a; 1、文献党下载器&#xff08;wxdown.org&#xff09; 大型文献馆&#xff0c;几乎整合汇集了所有中外文献数据库资源&#xff0c;可附带权限进入文献数据库查找下载文献&#xff0c;覆盖全科包括查找下载人文社科类文献的众…

【C和数据结构-5+1】习题第一天

文章目录一.选择题1.整型在内存中的存储2.大小端字节序3.指针的大小4.形参一级指针或二级指针的区别5.二维数组传参降维成数组指针二.编程题1.自守数2.质数判断一.选择题 1.整型在内存中的存储 猜一猜打印的结果? int main() {char a 101;int sum 200;a 27; sum a;printf…

2023年,无所谓,我会出手整合SSM

目录 一、简介&#xff1a; 二、Maven构建框架&#xff1a; 三、依赖配置 四、web.xml配置文件 五、创建SpringMVC的配置文件 六、Spring.xml配置环境 七、其他配置 八、Mapper.xml配置文件&#xff1a; 九、mybatis-config.xml配置文件&#xff1a; 十、log4j.xml日志…

Dropout详解:Dropout解决过拟合问题

Dropout 是一种能够有效缓解过拟合的正则化技术&#xff0c;被广泛应用于深度神经网络当中。但是被 dropout 所丢掉的位置都有助于缓解过拟合的吗&#xff1f; 中山大学 和 Meta AI 在 NeurIPS 2022 接收的论文在研究了注意力中的 dropout 后发现&#xff1a;不同注意力位置对过…

C++string类介绍

目录 一、介绍 二、string类对象的构造 string类有如下构造方法&#xff1a; 类对象的容量操作 类对象访问及遍历 string对象的修改操作&#xff1a; std::string::insert std::string::erase std::string::c_str std::string::find std::string::substr 一、介绍…

Linux学习笔记 超详细 0基础学习(上)

定义 linux是一个操作系统&#xff0c;与Windows、macos一样&#xff0c;是常见的操作系统之一&#xff0c;一般服务器会部署在Linux操作系统中&#xff0c;稳定应用广泛&#xff0c;Android系统是基于Linux内核的&#xff0c;所以了解Linux相关知识对于程序员来说是很重要的。…

人脸AI识别实战:用AI生成了这些人一生的样貌变化 | 附源码

这是来自斯坦福和华盛顿大学研究员发表的论文&#xff0c;提出了基于GAN的新方法&#xff0c;仅需要一张照片即可生成一个人从小时候到老了的样子。 论文&#xff1a;https://arxiv.org/abs/2003.09764 项目地址&#xff1a; https://github.com/royorel/Lifespan_Age_Trans…

windows上安装并使用exiftool修改图像exif信息

使用exiftool可以对图像的exif信息进行读取、修改、写入等操作。在linux系统上可以直接通过命令行安装&#xff0c;非常方便。但是在windows上&#xff0c;一开始我看了这篇博客&#xff0c;感觉里面写的还挺详细的&#xff0c;就跟着做了一下。里面是说要先安装perl&#xff0…

特斯拉突飞猛进背后带给自己的深思

引言 2013年开始接触汽车行业&#xff0c;那时候所做的事情也是跟着导师去解析整车CAN信号&#xff08;Message&#xff09;&#xff0c;也是从那时才知道车身现场总线——CAN总线。在那时候汽车给自己的感觉还是整车成本所占比重是机械高&#xff0c;软件比重低的可怜&#x…