EasyHttp框架的使用

news2024/11/26 5:37:17

项目的集成

// JitPack 远程仓库:https://jitpack.io
 maven { url 'https://jitpack.io' }
// 网络请求框架:https://github.com/getActivity/EasyHttp
implementation 'com.github.getActivity:EasyHttp:12.5'
// OkHttp 框架:https://github.com/square/okhttp
// noinspection GradleDependency
implementation 'com.squareup.okhttp3:okhttp:3.12.13'

源码及介绍

网络请求,如斯优雅 - 简书

GitHub - getActivity/EasyHttp: Android 网络请求框架,简单易用,so easy

解决明文流量的网络请求报错

CLEARTEXT communication to 3g.163.com not permitted by network security policy
    java.net.UnknownServiceException: CLEARTEXT communication to 3g.163.com not permitted by network security policy

添加res/xml/network_security_config.xml文件。文件名可以自拟,和后面的配置对应上即可。如果项目中,res文件夹中没有xml文件夹,我们需要在里面创建一个xml文件夹,然后在里面创建一个network_security_config.xml文件,network_security_config.xml文件中的内容如下:

<?xml version="1.0" encoding="utf-8"?>
<!-- 解决 Android P 禁用 http 请求的问题 -->
<network-security-config>
    <!-- 允许明文通信 -->
    <base-config cleartextTrafficPermitted="true" />
</network-security-config>
android:networkSecurityConfig="@xml/network_security_config"

初始化EasyHttp

由于直接复用轮子哥的配置代码,需要引入他调用的包,如果不引用的话修改他的配置代码

// Json 解析框架:https://github.com/google/gson
implementation 'com.google.code.gson:gson:2.10.1'
// Gson 解析容错:https://github.com/getActivity/GsonFactory
implementation 'com.github.getActivity:GsonFactory:8.0'
// 腾讯 MMKV:https://github.com/Tencent/MMKV MMKV初始化,MMKV是基于mmap内存映射的key-value组件
implementation 'com.tencent:mmkv-static:1.2.14'
    /**
     * 初始哈EasyHttp
     */
    private void initEasyHttp() {
        MMKV.initialize(this);//MMKV初始化,MMKV是基于mmap内存映射的key-value组件
        OkHttpClient okHttpClient = new OkHttpClient.Builder().build();
        // 网络请求框架初始化
        IRequestServer server = new ReleaseServer();
        EasyConfig.with(okHttpClient)
                //.setLogEnabled(BuildConfig.DEBUG)// 是否打印日志
                .setServer(server)// 设置服务器配置(必须设置)
                .setHandler(new RequestHandler(this))// 设置请求处理策略(必须设置)
                // 设置请求参数拦截器
                .setInterceptor(new IRequestInterceptor() {
                    @Override
                    public void interceptArguments(@NonNull HttpRequest<?> httpRequest,
                                                   @NonNull HttpParams params,
                                                   @NonNull HttpHeaders headers) {
                        headers.put("timestamp", String.valueOf(System.currentTimeMillis()));
                    }
                })
                .setRetryCount(1) // 设置请求重试次数
                .setRetryTime(2000)// 设置请求重试时间
//                .addParam("token", "6666666")// 添加全局请求参数
//                .addHeader("date", "20191030")// 添加全局请求头
                .into();
    }

RequestHandler 请求处理类 

final public class RequestHandler implements IRequestHandler {
    private final Application mApplication;

    public RequestHandler(Application application) {
        mApplication = application;
    }

    @NonNull
    @Override
    public Object requestSuccess(@NonNull HttpRequest<?> httpRequest, @NonNull Response response,
                                 @NonNull Type type) throws Exception {
        if (Response.class.equals(type)) {
            return response;
        }

        if (!response.isSuccessful()) {
            throw new ResponseException(String.format(mApplication.getString(R.string.http_response_error),
                    response.code(), response.message()), response);
        }

        if (Headers.class.equals(type)) {
            return response.headers();
        }

        ResponseBody body = response.body();
        if (body == null) {
            throw new NullBodyException(mApplication.getString(R.string.http_response_null_body));
        }

        if (ResponseBody.class.equals(type)) {
            return body;
        }

        // 如果是用数组接收,判断一下是不是用 byte[] 类型进行接收的
        if(type instanceof GenericArrayType) {
            Type genericComponentType = ((GenericArrayType) type).getGenericComponentType();
            if (byte.class.equals(genericComponentType)) {
                return body.bytes();
            }
        }

        if (InputStream.class.equals(type)) {
            return body.byteStream();
        }

        if (Bitmap.class.equals(type)) {
            return BitmapFactory.decodeStream(body.byteStream());
        }

        String text;
        try {
            text = body.string();
        } catch (IOException e) {
            // 返回结果读取异常
            throw new DataException(mApplication.getString(R.string.http_data_explain_error), e);
        }

        // 打印这个 Json 或者文本
        EasyLog.printJson(httpRequest, text);

        if (String.class.equals(type)) {
            return text;
        }

        final Object result;

        try {
            result = GsonFactory.getSingletonGson().fromJson(text, type);
        } catch (JsonSyntaxException e) {
            // 返回结果读取异常
            throw new DataException(mApplication.getString(R.string.http_data_explain_error), e);
        }

        if (result instanceof HttpData) {
            HttpData<?> model = (HttpData<?>) result;
            model.setResponseHeaders(response.headers());

            if (model.isRequestSuccess()) {
                // 代表执行成功
                return result;
            }

            if (model.isTokenInvalidation()) {
                // 代表登录失效,需要重新登录
                throw new TokenException(mApplication.getString(R.string.http_token_error));
            }

            // 代表执行失败
            throw new ResultException(model.getMessage(), model);
        }
        return result;
    }

    @NonNull
    @Override
    public Exception requestFail(@NonNull HttpRequest<?> httpRequest, @NonNull Exception e) {
        if (e instanceof HttpException) {
            if (e instanceof TokenException) {
                // 登录信息失效,跳转到登录页

            }
            return e;
        }

        if (e instanceof SocketTimeoutException) {
            return new TimeoutException(mApplication.getString(R.string.http_server_out_time), e);
        }

        if (e instanceof UnknownHostException) {
            NetworkInfo info = ((ConnectivityManager) mApplication.getSystemService(Context.CONNECTIVITY_SERVICE)).getActiveNetworkInfo();
            // 判断网络是否连接
            if (info != null && info.isConnected()) {
                // 有连接就是服务器的问题
                return new ServerException(mApplication.getString(R.string.http_server_error), e);
            }
            // 没有连接就是网络异常
            return new NetworkException(mApplication.getString(R.string.http_network_error), e);
        }

        if (e instanceof IOException) {
            // 出现该异常的两种情况
            // 1. 调用 EasyHttp.cancel
            // 2. 网络请求被中断
            return new CancelException(mApplication.getString(R.string.http_request_cancel), e);
        }

        return new HttpException(e.getMessage(), e);
    }

    @NonNull
    @Override
    public Exception downloadFail(@NonNull HttpRequest<?> httpRequest, @NonNull Exception e) {
        if (e instanceof ResponseException) {
            ResponseException responseException = ((ResponseException) e);
            Response response = responseException.getResponse();
            responseException.setMessage(String.format(mApplication.getString(R.string.http_response_error),
                    response.code(), response.message()));
            return responseException;
        } else if (e instanceof NullBodyException) {
            NullBodyException nullBodyException = ((NullBodyException) e);
            nullBodyException.setMessage(mApplication.getString(R.string.http_response_null_body));
            return nullBodyException;
        } else if (e instanceof FileMd5Exception) {
            FileMd5Exception fileMd5Exception = ((FileMd5Exception) e);
            fileMd5Exception.setMessage(mApplication.getString(R.string.http_response_md5_error));
            return fileMd5Exception;
        }
        return requestFail(httpRequest, e);
    }

    @Nullable
    @Override
    public Object readCache(@NonNull HttpRequest<?> httpRequest, @NonNull Type type, long cacheTime) {
        String cacheKey = HttpCacheManager.generateCacheKey(httpRequest);
        String cacheValue = HttpCacheManager.readHttpCache(cacheKey);
        if (cacheValue == null || "".equals(cacheValue) || "{}".equals(cacheValue)) {
            return null;
        }
        EasyLog.printLog(httpRequest, "----- read cache key -----");
        EasyLog.printJson(httpRequest, cacheKey);
        EasyLog.printLog(httpRequest, "----- read cache value -----");
        EasyLog.printJson(httpRequest, cacheValue);
        EasyLog.printLog(httpRequest, "cacheTime = " + cacheTime);
        boolean cacheInvalidate = HttpCacheManager.isCacheInvalidate(cacheKey, cacheTime);
        EasyLog.printLog(httpRequest, "cacheInvalidate = " + cacheInvalidate);
        if (cacheInvalidate) {
            // 表示缓存已经过期了,直接返回 null 给外层,表示缓存不可用
            return null;
        }
        return GsonFactory.getSingletonGson().fromJson(cacheValue, type);
    }

    @Override
    public boolean writeCache(@NonNull HttpRequest<?> httpRequest, @NonNull Response response, @NonNull Object result) {
        String cacheKey = HttpCacheManager.generateCacheKey(httpRequest);
        String cacheValue = GsonFactory.getSingletonGson().toJson(result);
        if (cacheValue == null || "".equals(cacheValue) || "{}".equals(cacheValue)) {
            return false;
        }
        EasyLog.printLog(httpRequest, "----- write cache key -----");
        EasyLog.printJson(httpRequest, cacheKey);
        EasyLog.printLog(httpRequest, "----- write cache value -----");
        EasyLog.printJson(httpRequest, cacheValue);
        boolean writeHttpCacheResult = HttpCacheManager.writeHttpCache(cacheKey, cacheValue);
        EasyLog.printLog(httpRequest, "writeHttpCacheResult = " + writeHttpCacheResult);
        boolean refreshHttpCacheTimeResult = HttpCacheManager.setHttpCacheTime(cacheKey, System.currentTimeMillis());
        EasyLog.printLog(httpRequest, "refreshHttpCacheTimeResult = " + refreshHttpCacheTimeResult);
        return writeHttpCacheResult && refreshHttpCacheTimeResult;
    }

    @Override
    public void clearCache() {
        HttpCacheManager.clearCache();
    }
}

字符串

<!--EasyHttp-->
<string name="http_response_error">服务器响应异常,请稍后再试,响应码:%d,响应信息:%s</string>
<string name="http_response_null_body">服务器数据返回异常,请稍后再试</string>
<string name="http_data_explain_error">数据解析异常,请稍后</string>
<string name="http_token_error">登录失效,请重新登录</string>
<string name="http_server_out_time">服务器请求超时,请稍后再试</string>
<string name="http_server_error">服务器连接异常,请稍后再试</string>
<string name="http_network_error">请求失败,请检查网络设置</string>
<string name="http_request_cancel">请求被中断,请重试</string>
<string name="http_response_md5_error">下载失败,文件 md5 校验失败</string>

HttpCacheManager Http缓存管理器

public final class HttpCacheManager {

    private static final MMKV HTTP_CACHE_CONTENT = MMKV.mmkvWithID("http_cache_content");

    private static final MMKV HTTP_CACHE_TIME = MMKV.mmkvWithID("http_cache_time");

    /**
     * 生成缓存的 key
     */
    public static String generateCacheKey(@NonNull HttpRequest<?> httpRequest) {
        IRequestApi requestApi = httpRequest.getRequestApi();
        return "请替换成当前的用户 id" + "\n" + requestApi.getApi() + "\n" + GsonFactory.getSingletonGson().toJson(requestApi);
    }

    /**
     * 读取缓存
     */
    public static String readHttpCache(String cacheKey) {
        String cacheValue = HTTP_CACHE_CONTENT.getString(cacheKey, null);
        if ("".equals(cacheValue) || "{}".equals(cacheValue)) {
            return null;
        }
        return cacheValue;
    }

    /**
     * 写入缓存
     */
    public static boolean writeHttpCache(String cacheKey, String cacheValue) {
        return HTTP_CACHE_CONTENT.putString(cacheKey, cacheValue).commit();
    }

    /**
     * 清理缓存
     */
    public static void clearCache() {
        HTTP_CACHE_CONTENT.clearMemoryCache();
        HTTP_CACHE_CONTENT.clearAll();

        HTTP_CACHE_TIME.clearMemoryCache();
        HTTP_CACHE_TIME.clearAll();
    }

    /**
     * 获取 Http 写入缓存的时间
     */
    public static long getHttpCacheTime(String cacheKey) {
        return HTTP_CACHE_TIME.getLong(cacheKey, 0);
    }

    /**
     * 设置 Http 写入缓存的时间
     */
    public static boolean setHttpCacheTime(String cacheKey, long cacheTime) {
        return HTTP_CACHE_TIME.putLong(cacheKey, cacheTime).commit();
    }

    /**
     * 判断缓存是否过期
     */
    public static boolean isCacheInvalidate(String cacheKey, long maxCacheTime) {
        if (maxCacheTime == Long.MAX_VALUE) {
            // 表示缓存长期有效,永远不会过期
            return false;
        }
        long httpCacheTime = getHttpCacheTime(cacheKey);
        if (httpCacheTime == 0) {
            // 表示不知道缓存的时间,这里默认当做已经过期了
            return true;
        }
        return httpCacheTime + maxCacheTime < System.currentTimeMillis();
    }
}

HttpData 统一接口数据结构

public class HttpData<T>  {
    /** 响应头 */
    @Nullable
    private Headers responseHeaders;

    /** 返回码 */
    private int errorCode;
    /** 提示语 */
    private String errorMsg;
    /** 数据 */
    @Nullable
    private T data;

    public void setResponseHeaders(@Nullable Headers responseHeaders) {
        this.responseHeaders = responseHeaders;
    }

    @Nullable
    public Headers getResponseHeaders() {
        return responseHeaders;
    }

    public int getCode() {
        return errorCode;
    }

    public String getMessage() {
        return errorMsg;
    }

    @Nullable
    public T getData() {
        return data;
    }

    /**
     * 是否请求成功
     */
    public boolean isRequestSuccess() {
        // 这里为了兼容 WanAndroid 接口才这样写,但是一般情况下不建议这么设计
        // 因为 int 的默认值就是 0,这样就会导致,后台返回结果码为 0 和没有返回的效果是一样的
        // 本质上其实不一样,没有返回结果码本身就是一种错误数据结构,理论上应该走失败的回调
        // 因为这里会判断是否等于 0,所以就会导致原本走失败的回调,结果走了成功的回调
        // 所以在定义错误码协议的时候,请不要将后台返回的某个成功码或者失败码的值设计成 0
        // 如果你的项目已经出现了这种情况,可以尝试将结果码的数据类型从 int 修改成 Integer
        // 这样就可以通过结果码是否等于 null 来判断后台是否返回了,当然这样也有一些弊端
        // 后面外层在使用这个结果码的时候,要先对 Integer 对象进行一次判空,否则会出现空指针异常
        return errorCode == 0;
    }

    /**
     * 是否 Token 失效
     */
    public boolean isTokenInvalidation() {
        return errorCode == 1001;
    }
}

ReleaseServer 请求服务配置

public class ReleaseServer implements IRequestServer {
    @NonNull
    @Override
    public String getHost() {
        return "https://www.wanandroid.com/";
    }
}

ResultException 返回结果异常

public final class ResultException extends HttpException {

    private final HttpData<?> mData;

    public ResultException(String message, HttpData<?> data) {
        super(message);
        mData = data;
    }

    public ResultException(String message, Throwable cause, HttpData<?> data) {
        super(message, cause);
        mData = data;
    }

    @NonNull
    public HttpData<?> getHttpData() {
        return mData;
    }
}

TokenException Token失效异常

public final class TokenException extends HttpException {

    public TokenException(String message) {
        super(message);
    }

    public TokenException(String message, Throwable cause) {
        super(message, cause);
    }
}

模拟下载文件

// 如果是放到外部存储的应用专属目录则不需要适配分区存储特性
        File file = new File(getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "微信 8.0.15.apk");
        String url = "http://3g.163.com/links/4636";
        EasyHttp.download(this)
                .method(HttpMethod.GET)
                .file(file)
                //.url("https://qd.myapp.com/myapp/qqteam/AndroidQQ/mobileqq_android.apk")
//                .url("https://dldir1.qq.com/weixin/android/weixin8015android2020_arm64.apk")
//                .md5("b05b25d4738ea31091dd9f80f4416469")
                .url(url)
                .listener(new OnDownloadListener() {

                    @Override
                    public void onDownloadStart(File file) {
                        dialoading.setTv("开始下载");
                    }

                    @Override
                    public void onDownloadProgressChange(File file, int progress) {
                        dialoading.setTv("下载进度:" + progress);
                    }

                    @Override
                    public void onDownloadSuccess(File file) {
                        ToastUtils.success("下载成功:" + file.getPath());
                        AppUtils.installApp(file);
                    }

                    @Override
                    public void onDownloadFail(File file, Exception e) {
                        ToastUtils.error("下载失败:" + e.getMessage());
                        new WarningDialog.Builder(DownLoadActivity.this).
setTitle("下载失败").setText(e.toString()).show();
                        if (e instanceof FileMd5Exception) {
                            // 如果是文件 md5 校验失败,则删除文件
                            if (file.exists()) {
                                file.delete();
                            }
                        }
                    }

                    @Override
                    public void onDownloadEnd(File file) {
                        dialoading.close();
                    }
                })
                .start();

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

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

相关文章

ssm+vue的疫情高校师生外出请假管理系统(有报告)。Javaee项目,ssm vue前后端分离项目。

演示视频&#xff1a; ssmvue的疫情高校师生外出请假管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;ssm vue前后端分离项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三…

2023版Pycharm关闭一直显示closing project,正在关闭项目

点击 帮助 下的 查找操作 英文版为 Help 下的 Find Action 输入 Registry 禁用 ide.await.scope.completion 即可 PS&#xff1a;按 Ctrl F 输入可以快速检索

快速实现一个企业级域名 SSL 证书有效期监控巡检系统

Why 现在对于企业来说&#xff0c;HTTPS 已经不是可选项&#xff0c;已经成为一个必选项。HTTPS 协议采用 SSL 协议&#xff0c;采用公开密钥的技术&#xff0c;提供了一套 TCP/IP 传输层数据加密的机制。SSL 证书是一种遵守 SSL 协议的服务器数字证书&#xff0c;一般是由权威…

《golang设计模式》第三部分·行为型模式-04-迭代器模式(Iterator)

文章目录 1. 概念1.1 角色1.2 类图 2. 代码示例2.1 需求2.2 代码2.3 类图 1. 概念 迭代器&#xff08;Iterator&#xff09;能够在不暴露聚合体内部表示的情况下&#xff0c;向客户端提供遍历聚合元素的方法。 1.1 角色 InterfaceAggregate&#xff08;抽象聚合&#xff09;…

图像切分:将一张长图片切分为指定长宽的多张图片

1.需求 比如有一张很长的图片其大小为宽度779&#xff0c;高度为122552&#xff0c;那我想把图片切分为779乘以1280的格式。 步骤如下&#xff1a; 使用图像处理库&#xff08;如PIL或OpenCV&#xff09;加载原始图片。确定子图片的宽度和高度。计算原始图片的宽度和高度&am…

6、Python控制流:if语句、for循环、while循环、循环控制语句

文章目录 Python控制流:if语句、for循环、while循环、循环控制语句if语句示例:for循环示例:while循环示例:循环控制语句示例:最佳实践Python控制流:if语句、for循环、while循环、循环控制语句 控制流是编程中的基础概念,它允许我们根据不同的条件执行不同的代码块,或者…

day3作业

自己封装一个矩形类(Rect)&#xff0c;拥有私有属性:宽度(width)、高度(height)&#xff0c; 定义公有成员函数: 初始化函数:void init(int w, int h) 更改宽度的函数:set_w(int w) 更改高度的函数:set_h(int h) 输出该矩形的周长和面积函数:void show() #include <io…

哈尔滨招聘网站哪个好

哈尔滨招聘网站 吉鹿力招聘网 比较好&#xff0c;我们都知道在现代社会中&#xff0c;互联网已经成为了人们获取信息的主要渠道之一。在寻找工作方面也不例外。吉鹿力招聘网是哈尔滨找工作的求职招聘网站。 登录 吉鹿力招聘网 “注册账号”&#xff0c;然后输入个人基本信息&a…

Android-JobService

JobService 这里写目录标题 JobService一、API详解1 onStartJob2 onStopJob 二、onStartJob | onStopJob 返回值case 1case 2case 3 ref: 深入理解JobScheduler与JobService的使用 - 掘金 (juejin.cn) (28条消息) JobService的使用介绍_TechMerger的博客-CSDN博客 (28条消息) J…

使用 Threejs 从基础开始构建 3D 地球

需求 threejs学习-3D 地球 实现&#xff1a; 1、使用粒子效果模拟宇宙星空 2、贴图、模型等资源的加载 3、加载资源的监听 4、效果合成器 EffectComposer 的初级使用 5、在地球上设置坐标以及坐标涟漪动画 6、标点间建立飞线 7、简单动画建议先浏览一遍git地址上代码&#xff…

Java2 - 数据结构

5 数据类型 5.1 整数类型 在Java中&#xff0c;数据类型用于定义变量或表达式可以存储的数据的类型。Java的数据类型可分为两大类&#xff1a;基本数据类型和引用数据类型。 byte&#xff0c;字节 【1字节】表示范围&#xff1a;-128 ~ 127 即&#xff1a;-2^7 ~ 2^7 -1 sho…

LeetCode | 面试题 02.02. 返回倒数第 k 个节点

LeetCode | 面试题 02.02. 返回倒数第 k 个节点 OJ链接 思路&#xff1a;定义两个快慢指针&#xff0c;让快指针先提前走k个节点&#xff0c;然后再让慢结点和快结点一起走&#xff0c;当快指针 NULL时&#xff0c;慢指针就是倒数第k个节点 代码如下&#xff1a; int kthT…

ARMday02(汇编语法、汇编指令)

汇编语法 汇编文件中的内容 1.伪操作&#xff1a;在汇编程序中不占用存储空间&#xff0c;但是可以在程序编译时起到引导和标识作用 .text .global .glbal .if .else .endif .data .word.... 2.汇编指令&#xff1a;每一条汇编指令都用来标识一个机器码&#xff0c;让计算机做…

windows 下 QT Android 环境搭建(QGC 4.2.x + Qt 5.15.2)

文章目录 1. QT Creator 环境搭建2. JDK1&#xff09;官网途径&#xff1a;2) 360 安装&#xff1a;配置 3. SDK1) 通过 Android Studio2&#xff09;QT 配置中安装 姊妹篇&#xff1a; win10下新版QGC地面站环境搭建全面攻略&#xff08;v4.x.x QGroundControl地面站搭建&…

c-CoSe2-CoN助力Zn-空气电池

硒化钴&#xff08;CoSe2&#xff09;的相变可有效调节其固有的电催化活性&#xff0c;但提高CoSe2的电导率和催化活性/稳定性还是一个挑战。异质结构工程可优化界面性能&#xff0c;促进CoSe2基催化剂上氧电催化的动力学。 基于此&#xff0c;黑龙江大学邹金龙教授等人报道了…

蓝桥云课--1014 第 1 场算法双周赛

2-数树数【算法赛】&#xff08;找规律&#xff09; 一、题目要求 二、思路 由此可以推导出来&#xff0c;当s[i]L时&#xff0c;下一个编号当前编号*2-1&#xff1b;当s[i]R时&#xff0c;下一个编号当前编号*2&#xff1b; 三、代码 #include<bits/stdc.h> #define…

Matlab论文插图绘制模板第124期—三维气泡图

在之前的文章中&#xff0c;分享了很多Matlab气泡图的绘制模板&#xff1a; 进一步&#xff0c;再来分享一下三维气泡图。 先来看一下成品效果&#xff1a; 特别提示&#xff1a;本期内容『数据代码』已上传资源群中&#xff0c;加群的朋友请自行下载。有需要的朋友可以关注同…

C语言之认识柔性数组(flexible array)

在学习之前&#xff0c;我们首先要了解柔性数组是放在结构体当中的&#xff0c;知道这一点&#xff0c;我们就开始今天的学习吧&#xff01; 1.柔性数组的声明 在C99中&#xff0c;结构中的最后一个元素允许是未知大小的数组&#xff0c;这就叫做柔性数组成员 这里的结构是结构…

【C语言初学者周冲刺计划】4.3根据输入的行数输出一下图案

目录 1题目&#xff1a; 2解题思路&#xff1a; 3代码&#xff1a; 4运行代码&#xff1a; 5总结&#xff1a; 1题目&#xff1a; 2解题思路&#xff1a; 首先分析题干要求&#xff0c;然后找规律&#xff0c;发现前面空格与行数的规律和A,B递推的规律&#xff0c;然后写代…

如何在macbook上删除文件?Mac删除文件的多种方法

在使用MacBook电脑时&#xff0c;桌面上经常会积累大量的文件&#xff0c;而这些文件可能已经不再需要或已经过时。为了保持桌面的整洁和提高电脑性能&#xff0c;我们需要及时删除这些文件。本文将介绍MacBook怎么删除桌面文件&#xff0c;以及macbook删除桌面文件快捷键。 一…