Android网络请求,全方位优雅解析

news2024/11/28 5:37:08

网络请求的基本流程

网络请求步骤(用户输入一个网址到网页最终展现到用户面前)大致流程总结如下:

  • 在客户端浏览器中输入网址URL。
  • 发送到DNS(域名服务器)获得域名对应的WEB服务器的IP地址。
  • 客户端浏览器与WEB服务器建立TCP(传输控制协议)连接。
  • 客户端浏览器向对应IP地址的WEB服务器发送相应的HTTP或HTTPS请求。
  • WEB服务器响应请求,返回指定的URL数据或错误信息;如果设定重定向,则重定向到新的URL地址。
  • 客户端浏览器下载数据,解析HTML源文件,解析的过程中实现对页面的排版,解析完成后,在浏览器中显示基础的页面。
  • 分析页面中的超链接,显示在当前页面,重复以上过程直至没有超链接需要发送,完成页面的全部显示。

1、输入地址

当我们开始在浏览器中输入网址的时候,浏览器其实就已经在智能的匹配可能得 url 了,他会从历史记录,书签等地方,找到已经输入的字符串可能对应的 url,然后给出智能提示,让你可以补全url地址。对于 google的chrome 的浏览器,他甚至会直接从缓存中把网页展示出来,就是说,你还没有按下 enter,页面就出来了。

2、浏览器查找域名的 IP 地址

  1. 请求一旦发起,浏览器首先要做的事情就是解析这个域名,一般来说,浏览器会首先查看本地硬盘的 hosts 文件,看看其中有没有和这个域名对应的规则,如果有的话就直接使用 hosts 文件里面的 ip 地址。
  2. 如果在本地的 hosts 文件没有能够找到对应的 ip 地址,浏览器会发出一个 DNS请求到本地DNS服务器 。本地DNS服务器一般都是你的网络接入服务器商提供,比如中国电信,中国移动。
  3. 查询你输入的网址的DNS请求到达本地DNS服务器之后,本地DNS服务器会首先查询它的缓存记录,如果缓存中有此条记录,就可以直接返回结果,此过程是递归的方式进行查询。如果没有,本地DNS服务器还要向DNS根服务器进行查询。
  4. 根DNS服务器没有记录具体的域名和IP地址的对应关系,而是告诉本地DNS服务器,你可以到域服务器上去继续查询,并给出域服务器的地址。这种过程是迭代的过程。
  5. 本地DNS服务器继续向域服务器发出请求,在这个例子中,请求的对象是.com域服务器。.com域服务器收到请求之后,也不会直接返回域名和IP地址的对应关系,而是告诉本地DNS服务器,你的域名的解析服务器的地址。
  6. 最后,本地DNS服务器向域名的解析服务器发出请求,这时就能收到一个域名和IP地址对应关系,本地DNS服务器不仅要把IP地址返回给用户电脑,还要把这个对应关系保存在缓存中,以备下次别的用户查询时,可以直接返回结果,加快网络访问。

HttpURLconnection的介绍

在Android开发中网络请求是最常用的操作之一, Android SDK中对HTTP(超文本传输协议)也提供了很好的支持,这里包括两种接口:

  • 标准Java接口(java.NET) —-HttpURLConnection,可以实现简单的基于URL请求、响应功能;
  • Apache接口(org.appache.http)—-HttpClient,使用起来更方面更强大。

但在android API23的SDK中Google将HttpClient移除了。Google建议使用httpURLconnection进行网络访问操作。

HttpURLconnection是基于http协议的,支持get,post,put,delete等各种请求方式,最常用的就是get和post,下面针对这两种请求方式进行讲解。

Get请求的使用方法

HttpURLconnection是同步的请求,所以必须放在子线程中。使用示例如下:

new Thread(new Runnable() {
    @Override
    public void run() {
        try {
            String url = "https://www.baidu.com/";
            URL url = new URL(url);
            //得到connection对象。
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            //设置请求方式
            connection.setRequestMethod("GET");
            //连接
            connection.connect();
            //得到响应码
            int responseCode = connection.getResponseCode();
            if(responseCode == HttpURLConnection.HTTP_OK){
                //得到响应流
                InputStream inputStream = connection.getInputStream();
                //将响应流转换成字符串
                String result = is2String(inputStream);//将流转换为字符串。
                Log.d("kwwl","result============="+result);
            }
​
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}).start();
123456789101112131415161718192021222324252627

get请求的使用方法如上。如果需要传递参数,则直接把参数拼接到url后面,其他完全相同,如下:

String url = "https://www.baidu.com/?userName=zhangsan&password=123456";
1

注意点: 1,url与参数之间用?隔开。 2,键值对中键与值用=连接。 3,两个键值对之间用&连接。

分析: 1, 使用connection.setRequestMethod(“GET”);设置请求方式。 2, 使用connection.connect();连接网络。请求行,请求头的设置必须放在网络连接前。 3, connection.getInputStream()只是得到一个流对象,并不是数据,不过我们可以从流中读出数据,从流中读取数据的操作必须放在子线程。 4, connection.getInputStream()得到一个流对象,从这个流对象中只能读取一次数据,第二次读取时将会得到空数据。

Post请求的使用方法

post的基本用法如下:

使用示例如下:

new Thread(new Runnable() {
    @Override
    public void run() {
        try {
            URL url = new URL(getUrl);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("POST");//设置请求方式为POST
            connection.setDoOutput(true);//允许写出
            connection.setDoInput(true);//允许读入
            connection.setUseCaches(false);//不使用缓存
            connection.connect();//连接
            int responseCode = connection.getResponseCode();
            if(responseCode == HttpURLConnection.HTTP_OK){
                InputStream inputStream = connection.getInputStream();
                String result = is2String(inputStream);//将流转换为字符串。
                Log.d("kwwl","result============="+result);
            }
​
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}).start();
1234567891011121314151617181920212223

注:post请求与get请求有很多相似,只是在连接之前多了一些设置,两者可以对比学习使用。

使用post请求传递键值对参数

new Thread(new Runnable() {
    @Override
    public void run() {
        try {
            URL url = new URL(getUrl);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("POST"); 
            connection.setDoOutput(true);
            connection.setDoInput(true);
            connection.setUseCaches(false);
            connection.connect();
​
            String body = "userName=zhangsan&password=123456";
            BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(connection.getOutputStream(), "UTF-8"));
            writer.write(body);
            writer.close();
​
            int responseCode = connection.getResponseCode();
            if(responseCode == HttpURLConnection.HTTP_OK){
                InputStream inputStream = connection.getInputStream();
                String result = is2String(inputStream);//将流转换为字符串。
                Log.d("kwwl","result============="+result);
            }
​
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}).start();
1234567891011121314151617181920212223242526272829

分析:

  1. post方式传递参数的本质是:从连接中得到一个输出流,通过输出流把数据写到服务器。
  2. 数据的拼接采用键值对格式,键与值之间用=连接。每个键值对之间用&连接。

使用post请求传递json格式参数

post请求也可以传递json格式的参数,使用示例如下:

new Thread(new Runnable() {
    @Override
    public void run() {
        try {
            URL url = new URL(getUrl);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("POST"); 
            connection.setDoOutput(true);
            connection.setDoInput(true);
            connection.setUseCaches(false);
            connection.setRequestProperty("Content-Type", "application/json;charset=utf-8");//设置参数类型是json格式
            connection.connect();
​
            String body = "{userName:zhangsan,password:123456}";
            BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(connection.getOutputStream(), "UTF-8"));
            writer.write(body);
            writer.close();
​
            int responseCode = connection.getResponseCode();
            if(responseCode == HttpURLConnection.HTTP_OK){
                InputStream inputStream = connection.getInputStream();
                String result = is2String(inputStream);//将流转换为字符串。
                Log.d("kwwl","result============="+result);
            }
​
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}).start();
123456789101112131415161718192021222324252627282930

传递json格式的参数与传递键值对参数不同点有两个:

  1. 传递json格式数据时需要在请求头中设置参数类型是json格式。
  2. body是json格式的字符串。

设置请求头

Get请求与post请求都可以设置请求头,设置请求头的方式也是相同的。为了节约篇幅,重复的代码不再列出,核心代码如下:

connection.setRequestMethod("POST");
connection.setRequestProperty("version", "1.2.3");//设置请求头
connection.setRequestProperty("token", token);//设置请求头
connection.connect();
1234

OkHttp的优点

网络优化方面:

  • 内置连接池,支持连接复用;
  • 支持gzip压缩响应体;
  • 通过缓存避免重复的请求;
  • 支持http2,对一台机器的所有请求共享同一个socket。

功能方面:

功能全面,满足了网络请求的大部分需求

扩展性方面:

责任链模式使得很容易添加一个自定义拦截器对请求和返回结果进行处理

原理

工作流程:

  • 通过OkhttpClient创建一个Call,并发起同步或异步请求时;
  • okhttp会通过Dispatcher对我们所有的RealCall(Call的具体实现类)进行统一管理,并通过 execute()及enqueue()方法对同步或异步请求进行处理;
  • execute()及enqueue()这两个方法会最终调用RealCall中的getResponseWithInterceptorChain()(重点)方法,从拦截器链中获取返回结果;
  • 拦截器链中,依次通过RetryAndFollowUpInterceptor(重定向拦截器)、BridgeInterceptor(桥接拦截器)、CacheInterceptor(缓存拦截器)、ConnectInterceptor(连接拦截器)、CallServerInterceptor(网络拦截器)对请求依次处理,与服务的建立连接后,获取返回数据,再经过上述拦截器依次处理后,最后将结果返回给调用方。

原理图:

编辑

添加图片注释,不超过 140 字(可选)

使用

1.okhttp初始化

 Interceptor REWRITE_CACHE_CONTROL_INTERCEPTOR = new Interceptor() {
     @Override public Response intercept(Chain chain) throws IOException {
         Response originalResponse = chain.proceed(chain.request());
         return originalResponse.newBuilder()
                 .removeHeader("Pragma")
                 .header("Cache-Control", String.format("max-age=%d", 60))
                 .build();
     }
 };
​
 mOkHttpClient.setConnectTimeout(15000, TimeUnit.SECONDS);
 mOkHttpClient.setReadTimeout(15000, TimeUnit.SECONDS);
 mOkHttpClient.setWriteTimeout(15000, TimeUnit.SECONDS);
 mOkHttpClient.setRetryOnConnectionFailure(true);
 //-------------------------------设置http缓存,提升用户体验-----------------------------------
 Cache cache;
 File httpCacheDirectory =  StorageUtils.getOwnCacheDirectory(context,HTTP_CACHE_FILENAME);
 cache = new Cache(httpCacheDirectory, 10 * 1024);
 mOkHttpClient.setCache(cache);
 mOkHttpClient.networkInterceptors().add(REWRITE_CACHE_CONTROL_INTERCEPTOR);
 //-------------------------------设置http缓存,提升用户体验-----------------------------------
​
// Handler mDelivery = new Handler(Looper.getMainLooper());
​
 if (false) {
     mOkHttpClient.setHostnameVerifier(new HostnameVerifier() {
         @Override
         public boolean verify(String hostname, SSLSession session) {
             return true;
         }
     });
 }

2.Get申请数据

Request request = new Request.Builder()
        .url("网络地址这里面设置一个传参数如何办")
        .addHeader("Accept", "application/json; q=0.5").build();
上面代码看到url缺么有带post,这就是get,我当时看到这个半天还在想这就是Get,至少带get的字样,缺么有,这就是Get,然后这
里面涉及的一个参数如何传。会有两个问题参数如何传、如果是apache的,如何转到okhttps,后面都会在我的github会封装一个库。
<span style="white-space:pre">  </span>FormEncodingBuilder body = new FormEncodingBuilder();
        for (ConcurrentHashMap.Entry<String, String> entry : params.urlParams.entrySet()) {
            body.addEncoded(entry.getKey(), entry.getValue());
        }
        Request request = new Request.Builder()
                .url(getUrlWithQueryString(true, params.url, params))
                .build();
        try {
            BaseOkHandler handler = new BaseOkHandler(callback, params);
            client.newCall(request).enqueue(handler);
        } catch (Exception e) {
            e.printStackTrace();
        }
上面看到的params就相当于apache中的RequestParams类,里面传入参数就可以,借鉴的并做修改后得到。
 getUrlWithQueryString(boolean shouldEncodeUrl, String url, BaseParams params) {
        if (url == null)
            return null;
​
        if (shouldEncodeUrl) {
            try {
                String decodedURL = URLDecoder.decode(url, "UTF-8");
                URL _url = new URL(decodedURL);
                URI _uri = new URI(_url.getProtocol(), _url.getUserInfo(), _url.getHost(), _url.getPort(), _url.getPath(),
 _url.getQuery(), _url.getRef());
                url = _uri.toASCIIString();
            } catch (Exception ex) {
                // Should not really happen, added just for sake of validity
            }
        }
​
        if (params != null) {
            // Construct the query string and trim it, in case it
            // includes any excessive white spaces.
            String paramString = params.getParamString().trim();
            // Only add the query string if it isn't empty and it
            // isn't equal to '?'.
            if (!paramString.equals("") && !paramString.equals("?")) {
                url += url.contains("?") ? "&" : "?";
                url += paramString;
            }
        }
     
        return url;
    }

而这个函数就是把所有参数格式化拼接成一个字符串。

3.Post申请数据

 Request request = new Request.Builder()
      .url(url)
      .post(body)//post是关键,提交表单数据、这里面有封装好多库。
      .build();
      Response response = client.newCall(request).execute();

4.Post提交文件

 MultipartBuilder builder = new MultipartBuilder().type(MultipartBuilder.FORM);
        if (params.fileParams.size() > 0) {
            RequestBody fileBody = null;
       for (ConcurrentHashMap.Entry<String, BaseParams.FileWrapper> entry1 : params.fileParams.entrySet()) {
                {
                    File file = entry1.getValue().file;
                    String fileName = file.getName();
                    fileBody = RequestBody.create(MediaType.parse(guessMimeType(fileName)), file);
                    //TODO 根据文件名设置contentType
                    builder.addPart(Headers.of("Content-Disposition",
                                "form-data; name="" + entry1.getKey() + ""; filename="" + fileName + """),
                            fileBody);
                }
            }
        }
        Request request = new Request.Builder()
                .url(params.url)
                .post(builder.build())
                .build();

大家看到上传文件只要使用MultipartBuilder类表单传入就可以。

5.消息回来处理

public class BaseOkHandler implements Callback {
​
    private HttpCallback callBack;
    BaseParams param;
     
    public BaseOkHandler(HttpCallback response, BaseParams cacheParams) {
        this.callBack = response;
        param = cacheParams;
    }
     
    @Override
    public void onFailure(Request request, IOException e) {
    }
     
    @Override
    public void onResponse(Response response) throws IOException {
        try {
            if (response.isSuccessful()) {
                //成功得到文本信息
                String content = response.body().string();
                //通过Handler来传给UI线程。
                Message msg =new Message();
                msg.obj = content;
                msg.what=0;
                mHandler.sendMessage(msg);
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
     
    Handler mHandler = new Handler() {
     
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case 0:
                    //得到数据并去做解析类。
     BaseEntity entity = JsonPaserFactory.paserObj(msg.obj.toString(), param.paserType);
                    //通知UI界面
    callBack.onSuccess(msg.obj.toString(), entity, param.paserType);
                    break;
                default:
                    break;
            }
        }
     
    };
}

本文主要讲解了在Android开发开发中的网络请求技术,更多有关Android网络技术可以参考《Android核心技术手册》里面记录了大部分的Android技巧上1000个技术点.点击查看详细类目。

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

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

相关文章

【MySQL】引擎类型

与其他DBMS一样&#xff0c;MySQL有一个 具体管理和处理数据的内部引擎 。在使用create table语句时&#xff0c;该引擎具体创建表&#xff0c;而在使用select或进行其他数据库处理时&#xff0c;该引擎在内部处理你的请求。多数时候&#xff0c;引擎都隐藏在DBMS内&#xff0…

苹果为 Vision Pro 头显申请游戏手柄专利

苹果Vision Pro 推出后&#xff0c;美国专利局公布了两项苹果公司申请的游戏手柄专利&#xff0c;其中一项的专利图如下图所示。据 PatentlyApple 报道&#xff0c;虽然申请专利并不能保证苹果公司会推出游戏手柄&#xff0c;但是苹果公司同时也为游戏手柄申请了商标&#xff0…

【Python-torch】torchio torchvision:transforms API 对比与解析

【Python-torch】torchio & torchvision&#xff1a;transforms API 对比与解析 文章目录 【Python-torch】torchio & torchvision&#xff1a;transforms API 对比与解析0. 前言与对比1. 预处理部分2. 数据增强部分3. 代码示例参考 0. 前言与对比 当处理3d图像数据时…

HDLBits-Verilog学习记录 | Verilog Language-Modules(2)

文章目录 25.Adder 1 | Module add26.Adder 2 | Module fadd27.Carry-select adder28.Adder-subtractor 25.Adder 1 | Module add practice&#xff1a; You are given a module add16 that performs a 16-bit addition. Instantiate two of them to create a 32-bit adder. O…

振动国标2009GB/T 19873.2-2009/ISO 13373-2:2005笔记

国标原文 1.时域&#xff0c;要求&#xff0c;采样率大于最高频率10倍&#xff08;最低频率&#xff1f;&#xff09; 2.频域&#xff0c;要求采样率大于最高频率2倍。 3.3.2 积分和微分&#xff0c;二次积分。 3.3.3 均方根。 3.4 滤波 4.1 奈奎斯特图、极坐标图、坎贝尔…

代码随想录算法训练营第四十九天|LeetCode 647,516,动态规划总结篇

目录 LeetCode 647.回文子串 动态规划五步曲&#xff1a; 1.确定dp[i][j]的含义 2.找出递推公式 3.初始化dp数组 4.确定遍历方向 5.打印dp数组 LeetCode 516.最长回文子序列 动态规划五步曲&#xff1a; 1.确定dp[i][j]的含义 2.找出递推公式 3.初始化dp数组 4.确定遍历方向 …

openssh---Windows下git安装配置gitlab

安装openssh 1. 专业版Win10/11默认自带&#xff0c;可以查看是否开启 1. Get-WindowsCapability -Online | Where-Object Name -like OpenSSH* 2. Add-WindowsCapability -Online -Name OpenSSH.Client~~~~0.0.1.0 3. Add-WindowsCapability -Online -Name OpenSSH.Serve…

智慧工地云平台源码:工地管理专家

智慧工地是目前建筑行业的热门话题之一&#xff0c;它代表了未来建筑施工的发展趋势。那么&#xff0c;智慧工地的未来&#xff0c;你看好吗&#xff1f; 从技术角度来看&#xff0c;智慧工地无疑是未来发展的趋势。随着人工智能、大数据、云计算等技术的飞速发展&#xff0c;智…

python+Appium自动化:python多线程多并发启动appium服务

Python启动Appium 服务 使用Dos命令或者bat批处理来手动启动appium服务&#xff0c;启动效率低下。如何将启动Appium服务也实现自动化呢&#xff1f; 这里需要使用subprocess模块&#xff0c;该模块可以创建新的进程&#xff0c;并且连接到进程的输入、输出、错误等管道信息&…

Ubuntu22.04安装使用Docker (参考:完成Dock中的企业微信安装)

Ubuntu22.04安装使用Docker 概述什么是Docker ?Docker 的优点 Docker的安装(1) 安装 Docker 依赖项(2) 启用 Docker 官方存储库(3) 使用 Apt 命令安装 Docker(4) 验证和测试 Docker 安装 Docker Compose1. 使用二进制文件安装 Docker Compose2. 使用 Pip 安装 Docker Compose …

2023年高教社杯 国赛数学建模思路 - 案例:ID3-决策树分类算法

文章目录 0 赛题思路1 算法介绍2 FP树表示法3 构建FP树4 实现代码 建模资料 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 算法介绍 FP-Tree算法全称是FrequentPattern Tree算法&#xff0c;就是频繁模…

LC34. 在排序数组中查找元素的第一个和最后一个位置(JAVA)

二分查找 在排序数组中查找元素的第一个和最后一个位置二分查找 上期经典算法 在排序数组中查找元素的第一个和最后一个位置 难度 - 中等 在排序数组中查找元素的第一个和最后一个位置 给你一个按照非递减顺序排列的整数数组 nums&#xff0c;和一个目标值 target。请你找出给定…

unity中Game视图绘制XYZ坐标轴

Game视图显示XYZ坐标轴 功能一&#xff1a;仅显示XYZ坐标轴前期准备设置箭头模型的材质1、在“Assets”中&#xff0c;新建一个名为“Materials”文件夹&#xff0c;专门用于放置材质。选中“Materials”文件夹&#xff0c;鼠标右键->“创建”->“材质”2、重命名为“Red…

# 【三维重建】【深度学习】NeRF代码Pytorch实现--数据加载(中)

【三维重建】【深度学习】NeRF代码Pytorch实现–数据加载(中) 论文提出了一种5D的神经辐射场来作为复杂场景的隐式表示&#xff0c;称为NeRF&#xff0c;其输⼊稀疏的多⻆度带pose的图像训练得到⼀个神经辐射场模型。简单来说就是通过输入同一场景不同视角下的二维图片和相机位…

GCNet论文总结和代码实现

GCNet: Non-local Networks Meet Squeeze-Excitation Networks and Beyond&#xff08;当非局部网络遇到挤压激励网络&#xff09; 论文&#xff1a;GCNet: Non-local Networks Meet Squeeze-Excitation Networks and Beyond 源码&#xff1a;https://gitcode.net/mirrors/xvji…

【LeetCode】227. 基本计算器 II

227. 基本计算器 II&#xff08;中等&#xff09; 方法&#xff1a;双栈解法 思路 我们可以使用两个栈 nums 和 ops 。 nums &#xff1a; 存放所有的数字ops &#xff1a;存放所有的数字以外的操作 然后从前往后做&#xff0c;对遍历到的字符做分情况讨论&#xff1a; 空格 …

爬虫逆向实战(二十三)--某准网数据

一、数据接口分析 主页地址&#xff1a;某准网 1、抓包 通过抓包可以发现数据接口是api_to/search/company_v2.json 2、判断是否有加密参数 请求参数是否加密&#xff1f; 通过查看“载荷”模块可以发现b参数和kiv参数是加密参数 请求头是否加密&#xff1f; 无响应是否加…

torch.mul()函数使用说明,含高维张量实例及运行结果

torch.mul函数使用说明&#xff0c;含实例及运行结果 torch.mul() 函数torch.mul() 函数定义参数及功能高维数据实例解释 参考博文及感谢 torch.mul() 函数 对输入的张量或数做点积运算&#xff0c;如果维度不统一会想进行维度统一&#xff08;广播机制&#xff09;&#xff0…

PConv : Run, Don’t Walk: Chasing Higher FLOPS for Faster Neural Networks

摘要 为了设计快速的神经网络,**许多研究都集中在减少浮点运算(FLOPs)**的数量。然而,我们观察到这种FLOPs的减少并不一定会导致相同程度的延迟减少。这主要是由于浮点运算每秒效率较低的问题所致。为了实现更快的网络,我们重新审视了流行的操作算子,并证明这种低FLOPS主…

docker高级(DockerFile解析)

1、构建三步骤 编写Dockerfile文件 docker build命令构建镜像 docker run依镜像运行容器实例 2、DockerFile构建过程解析 Dockerfile内容基础知识 1&#xff1a;每条保留字指令都必须为大写字母且后面要跟随至少一个参数 2&#xff1a;指令按照从上到下&#xff0c;顺序执行…