Heard ’Em Say something about a REST API?

听过'Em对REST API说些什么?

That’s right! There’s a REST API called “kanye.rest” (https://kanye.rest/) that provides randomised quotes from Kanye West. In this story, you will learn how to interact with the API and how to integrate it into your Android apps!

那就对了! 有一个称为“ kanye.rest”( https://kanye.rest/ )的REST API,它提供来自Kanye West的随机报价。 在这个故事中,您将学习如何与API交互以及如何将其集成到Android应用中!

(Left photo) Photo by Chase Fade on Unsplash (Right photo) Photo by Shahadat Rahman on Unsplash
(左图) Chase Fade在 Unsplash上的 照片 (右图) Shahadat Rahman在 Unsplash上的 照片

什么是REST API? (What is a REST API?)

A REST API is a Representational State Transfer Application Programming Interface… are you still reading this story? Awesome… I appreciate that was a lengthy abbreviation!

REST API是代表性状态转移应用程序编程接口……您还在阅读这个故事吗? 太棒了...我很欣赏那是一个冗长的缩写!

REST is a software architecture that defines rules in order for systems to communicate over the web.

REST是一种软件体系结构,它定义规则以使系统通过Web进行通信。

An API is a way for multiple software systems to communicate. For example, if you wanted to integrate Facebook features into your app (e.g. allowing the user of your app to login with their Facebook account), you would interact with the Facebook API to send/receive data to/from your app.

API是多个软件系统进行通信的一种方式。 例如,如果您想将Facebook功能集成到您的应用程序中(例如,允许应用程序的用户使用其Facebook帐户登录),则可以与Facebook API进行交互以向/从您的应用程序发送/接收数据。

使用Kanye REST API (Using the Kanye REST API)

The Kanye REST API that I used was https://api.kayne.rest

我使用的Kanye REST API是https://api.kayne.rest

More info can be found at: https://kanye.rest/

可以在以下位置找到更多信息: https : //kanye.rest/

设置项目(作为Android应用) (Setting up the Project (as an Android app))

This project walk through will be using the Android platform. You can communicate with this API via other technologies as well.

该项目的演练将使用Android平台 。 您也可以通过其他技术与此API通信。

I called my project “KayneRestAPI”, set the language to “Java” and the Minimum SDK to “API 23”.

我将我的项目命名为“ KayneRestAPI”,将语言设置为“ Java”,将最低SDK设置为“ API 23”。

We also need to allow INTERNET permissions in the AndroidManifest.xml file. If you do not include this, your app will simply crash when it runs.

我们还需要在AndroidManifest.xml文件中允许INTERNET权限。 如果不包括此选项,则您的应用程序将在运行时崩溃。

Include the following code in AndroidManifest.xml to enable this (within the “manifest ” tags/angled brackets but before the “application” opening tag/angled bracket:

在AndroidManifest.xml中包含以下代码以启用此功能(在“ 清单 ”标签/尖括号中,但在“ 应用程序 ”打开标签/尖括号之前:

<uses-permission android:name="android.permission.INTERNET"></uses-permission>

实施应用程序的UI /前端 (Implementing the UI/front-end of the app)

Go to “activity_main.xml” which is located at res → layout. If you cannot find this on the left side on Android Studio, select “Android” if you have “Project” or something else selected at the top left of Android Studio.

转到位于res→布局的“ activity_main.xml”。 如果您无法在Android Studio的左侧找到此图标,请选择“ Android”(如果您在Android Studio的左上方选择了“项目”或其他选项)。

Add the following code to “activity_main.xml” to setup the UI:

将以下代码添加到“ activity_main.xml”以设置UI:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"tools:context=".MainActivity"><TextViewandroid:id="@+id/kayneSaysTextView"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Kayne Says:"android:textSize="24dp"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /><TextViewandroid:id="@+id/kayneQuoteTextView"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="..."android:textSize="25dp"android:textAlignment="center"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toBottomOf="@+id/kayneSaysTextView" />
</androidx.constraintlayout.widget.ConstraintLayout>

I will explain this code and not let this story be a copy & paste exercise…

我将解释此代码,而不是让这个故事成为复制粘贴活动……

the “<androidx.constraintlayout…>” uses the constraint layout (more info here: https://developer.android.com/reference/androidx/constraintlayout/widget/ConstraintLayout).

“ <androidx.constraintlayout…>”使用约束布局 (更多信息在这里: https : //developer.android.com/reference/androidx/constraintlayout/widget/ConstraintLayout )。

This layout achieves the same and more than the Relative Layout so is a good choice… especially for a very simplified UI in this app.

此布局与“相对布局”实现的效果相同且更多,因此是一个不错的选择……尤其是对于此应用程序中非常简化的UI。

The Constraint Layout adds constraints to different views based upon other views, e.g. including constraints of the first TextView with the ID: “kayneSaysTextView” relative to it’s parent, i.e. the entire screen/view. This is shown below by the directions of the 3 arrows/constraints:

约束布局基于其他视图向不同的视图添加约束,例如,包括相对于其父对象(即整个屏幕/视图)的ID为“ kayneSaysTextView ”的第一个TextView的约束。 这通过3个箭头/约束的方向在下面显示:

The “kayneSaysTextView” simply shows the text “Kayne Says:”. The content of this view is static so won’t change at any point when the app runs.

“ kayneSaysTextView”仅显示文本“ Kayne Says:”。 该视图的内容是静态的,因此在应用运行时不会随时更改。

The last TextView: “kayneQuoteTextView” shows the dynamic (changes each time the app is restarted) displays the JSON data fetched from the API.

最后一个TextView :“ kayneQuoteTextView”显示动态(每次重新启动应用程序时都会更改)显示从API提取的JSON数据。

Just to clarify how the API works, the data or Kayne quote provided by the API returns a randomised value/quote each time an API call is made.

只是为了阐明API的工作原理 ,每次进行API调用时,API提供的数据或Kayne引用都会返回随机值/引用。

实施应用程序的逻辑/后端 (Implementing the logic/backend of the app)

Navigate to the “MainActivity” java file which is located at java → com → {your_name} → {project_name} folder

导航到位于Java→com→{您的名字}→{project_name}文件夹中的“ MainActivity” java文件。

Include the following code in the MainActivity class:

在MainActivity类中包含以下代码:

import androidx.appcompat.app.AppCompatActivity;import android.os.Bundle;
import android.widget.TextView;public class MainActivity extends AppCompatActivity implements FetchDataTask.OnTaskFinishedListener {final String URL = "https://api.kanye.rest";String kayneQuote;TextView kayneSaysTextView, kayneQuoteTextView;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);kayneSaysTextView = findViewById(R.id.kayneSaysTextView);kayneQuoteTextView = findViewById(R.id.kayneQuoteTextView);FetchDataTask fetchDataTask = new FetchDataTask(this);fetchDataTask.execute(URL);}@Overridepublic void OnTaskFinished(String kayneQuote) {if (kayneQuote != null) {this.kayneQuote = kayneQuote;kayneQuoteTextView.setText(this.kayneQuote);}}
}

Code explanation

代码说明

The imports will make sure there are no errors with the TextView, etc code.

导入将确保TextView等代码没有错误。

“implements” is included as we want to implement the interface defined in the FetchDataTask class in order to define the behaviour/code to run when the fetching API data task finishes. Don’t worry, we will create this class in a minute.

包含“实现”是因为我们要实现FetchDataTask类中定义的接口 ,以便定义在获取API数据任务完成时要运行的行为/代码。 不用担心,我们将在一分钟内创建此类。

The “URL” variable simply holds the URL/API endpoint that we want to send a GET request to. The “kayneQuote” variable stores the kayne quote value returned from the API. The 2 TextViews represent the TextViews defined in the UI section. These variables are then linked to the backend of the app in the “onCreate()” method on the findViewById line.

“ URL”变量仅包含我们要向其发送GET请求的URL / API端点。 “ kayneQuote”变量存储从API返回的kayne报价值。 2个TextView表示在UI部分中定义的TextView。 然后,这些变量在findViewById行的“ onCreate()”方法中链接到应用程序的后端。

On line 21, an instance of the FetchDataTask class is created, passing in the MainActivity as the reference for the onTaskFinishedListener variable in the FetchDataTask. Line 22 then starts the asynchronous request via calling execute(URL), where “URL” is the variable, therefore passes the API endpoint URL to the method.

在第21行,创建了FetchDataTask类的实例,并传入MainActivity作为FetchDataTask中onTaskFinishedListener变量的引用 。 然后,第22行通过调用execute(URL)来启动异步请求,其中“ URL”是变量,因此将API端点URL传递给该方法。

The code explanation for this class is nearly done… :)

该类的代码解释几乎完成了... :)

“onTaskFinished” implements the method defined in the interface (which is located in the FetchDataTask class). This method checks if the value of the “kayneQuote” parameter is null. If not, the parameter is assigned to the local “kayneQuote” variable (hence this.kayneQuote). The TextView is then updated to show this quote provided from the API.

“ onTaskFinished” 实现接口 (位于FetchDataTask类中)中定义的方法。 此方法检查“ kayneQuote”参数的值是否为空。 如果不是,则将参数分配给本地“ kayneQuote”变量(因此this.kayneQuote)。 然后更新TextView以显示API提供的报价。

Now, this is where we create the FetchDataTask class. Create a new class called… yep FetchDataTask. You can do this by right clicking on the {project_name} on the left → New → Java Class.

现在,我们在这里创建FetchDataTask类。 创建一个名为… yep FetchDataTask的新类。 您可以通过右键单击左侧的{project_name}→新建→Java类来实现。

Once you have created the class, the boilerplate code (basic necessary code to represent the newly created class) will be shown.

创建类后,将显示样板代码(代表新创建的类的基本必要代码)。

Below is the code I have written for the FetchDataTask:

以下是我为FetchDataTask编写的代码:

import android.os.AsyncTask;import org.json.JSONObject;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;/*** This class handles asynchronous calls to the API endpoint, in addition for parsing and deserialising* the returned JSON data into a Java Object, in this specific case into a String called kayneQuote.** @author Andreas Ward*/
public class FetchDataTask extends AsyncTask<String, String, String> {private String jsonData;private OnTaskFinishedListener onTaskFinishedListener;private String kayneQuote;public FetchDataTask(OnTaskFinishedListener onTaskFinishedListener){this.onTaskFinishedListener = onTaskFinishedListener;}@Overrideprotected void onPreExecute() { //method for preparing any data before call to API endpoint.super.onPreExecute();}@Overrideprotected String doInBackground(String... strings) { //method executed on background thread.getJsonData(strings); //gets the returned JSON dataparseJsonToKayneQuote(); //parses the returned JSON data and stored in a String called kayneQuotereturn jsonData;}@Overrideprotected void onPostExecute(String returnedData) {super.onPostExecute(returnedData);if (returnedData != null){onTaskFinishedListener.OnTaskFinished(kayneQuote);}}public void getJsonData(String... strings){try {URL url = new URL(strings[0]);HttpURLConnection conn = (HttpURLConnection) url.openConnection();conn.setDoInput(true); //signifies to read data from the URL connectionconn.setRequestMethod("GET");conn.connect();int responseCode = conn.getResponseCode();if (responseCode != 200) { //if the responseCode is not 'OK'throw new RuntimeException("HttpResponseCode: " + responseCode);} else {InputStream inputStream = conn.getInputStream();//reader defined to read text from the inputStreamBufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));StringBuffer buffer = new StringBuffer(); //thread safe and mutable (compared with StringBuilder)String line = "";while ((line = reader.readLine()) != null) { //reads each line whilst there's a valuebuffer.append(line + "\n"); //appends the data from the current line to the buffer, adding a new line}jsonData = buffer.toString();}} catch(IOException e){e.printStackTrace();jsonData = e.getMessage();}}public void parseJsonToKayneQuote(){if (jsonData != null) {try {JSONObject jsonQuotesObject = new JSONObject(jsonData);String quote = jsonQuotesObject.getString("quote");if (quote != null) {kayneQuote = quote;}} catch (Exception e) {e.printStackTrace();}}}public interface OnTaskFinishedListener{void OnTaskFinished(String kayneQuote);}
}

A bit more code than the MainActivity… but don’t worry, again I will explain it!

MainActivity多一点的代码…但是不用担心,我会再次解释它!

I think it’s easier to reference the above code by a line numbers.. ok so here we go:

我认为通过行号引用上面的代码会更容易..好的,所以我们开始:

Lines 1–10: Imports necessary for Async-related functions (Stream, HTTP and URL libraries).

第1-10行:与异步相关的功能(流,HTTP和URL库)必需的导入。

Line 18: Class declaration which inherits from AsyncTask which includes <Params, Progress, Result> which are included/used as parameters in the methods: doInBackground, onProgressUpdate, onPostExecute. *Note: the method onProgressUpdate isn’t actually implemented in this project, however I mention it just for understanding of the AsyncTask parameters*

第18行:从AsyncTask 继承的类声明,该类声明包括< Params,Progress,Result> ,这些参数包含/用作方法中的参数: doInBackground,onProgressUpdateonPostExecute*注意: onProgressUpdate 方法 实际上并未在该项目中实现,但是我提到它只是为了理解AsyncTask参数*

Lines 19–21: Variables for storing the JSON data returned, reference to the onTaskFinishedListener and “kayneQuote” for storing the result returned from the API request.

第19至21行:用于存储返回的JSON数据的变量,请参考onTaskFinishedListener和用于存储从API请求返回的结果的“ kayneQuote”。

Lines 23–26: Defines constructor for the class, where onTaskFinishedListener is included as a parameter so that the MainActivity onTaskFinishedListener can be assigned to the FetchDataTask class instance.

第23–26行:为该类定义构造函数,其中将onTaskFinishedListener作为参数包含在内,以便可以将MainActivity onTaskFinishedListener分配给FetchDataTask类实例。

Lines 29–31: An overridden class from the AsyncTask class which includes preparing data before the API request. The method from the parent class is also included in this overridden method.

第29至31行:AsyncTask类中的重写类,包括在API请求之前准备数据。 父类的方法也包含在此重写的方法中。

Lines 33–39: Another overridden method that is the actual code run during the asynchronous request. This uses a different thread to the UI thread to send the request. The getJsonData method call is called to achieve the sending of the request. The parseJsonToKayneQuote method parses the returned JSON data into the required format for storage in the kayneQuote variable. The JSON data is returned from the doInBackground method (which is stored in the variable jsonData).

第33–39行:另一个重写的方法,是在异步请求期间运行的实际代码。 这使用与UI线程不同的线程来发送请求。 调用getJsonData方法调用以实现请求的发送。 parseJsonToKayneQuote方法将返回的JSON数据解析为所需格式,以存储在kayneQuote变量中。 JSON数据从doInBackground方法返回(存储在变量jsonData中)。

Lines 41–48: onPostExecute is the 3rd overridden method from the AsyncTask class. This runs code after the asynchronous call is completed for processing data accordingly. There is a null check in this method, however a check already takes place in the parseJsonToKayneQuote method so probably isn’t necessarily required. The onTaskFinished method is then called to signify the task… well has finished :).

第41–48行:onPostExecute是AsyncTask类中的第三个重写方法。 这将在异步调用完成之后运行代码以相应地处理数据。 此方法中有一个空检查,但是在parseJsonToKayneQuote方法中已经进行了检查,因此可能不一定必需。 然后调用onTaskFinished方法来表示任务……已经完成了:)。

Lines 50–82: [This is the largest method of the class]

第50-82行:[这是该课程中最大的方法]

  • Lines 53–57 create the necessary variables, then are used to setup a connection in order to make the API call. The requestMethod is set to a GET request (we only want to read data from the API and not POST anything to the API). A connection attempt is then invoked to connect to the API.第53–57行创建必要的变量,然后用于建立连接以进行API调用。 requestMethod设置为GET请求(我们只想从API读取数据,而不想向API发送任何内容)。 然后调用连接尝试以连接到API。
  • Lines 59–64 The response code sent back from the server is then stored, checked and if the request was successful (a 200 response which is ok) then an InputStream object is created in order to start reading the data stream using the established connection.第59–64行然后存储,检查从服务器发送回的响应代码,如果请求成功(200响应正常),则创建InputStream对象,以便开始使用已建立的连接读取数据流。
  • Lines 67–76 Involve using a BufferedReader and StringBuffer to read the InputStream data from the established connection. A StringBuffer is used as it’s Thread-Safe and Mutable (compared to a StringBuilder). The data is read until the line using the BufferedReader is null. The data stored using the StringBuffer object is then added to as long as the current ‘reader’ (BufferedReader) line has some data to append to it. The ‘buffer’ variable data is then assigned to the jsonData variable. The code after line 76 is just to catch an exceptions thrown when trying to establish a connection, (providing debug and exception messages when/if required).第67-76行涉及使用BufferedReader和StringBuffer从已建立的连接读取InputStream数据。 使用StringBuffer是因为它是线程安全的和可变的(与StringBuilder相比)。 读取数据,直到使用BufferedReader的行为空为止。 只要当前的“ reader”(BufferedReader)行中要追加一些数据,就会添加使用StringBuffer对象存储的数据。 然后将“ buffer”变量数据分配给jsonData变量。 第76行之后的代码仅用于捕获尝试建立连接时引发的异常((在需要时提供调试和异常消息)。
  • Lines 84–100 Initially checks that the returned jsonData contains data. If so, using the imported org.json… code library, a JSONObject is created to represent the raw json data (which is just a long String value) in a JSON structure. The “quote ”String variable accesses and the JSON key-value pair attribute called quote. If this attribute exists and contains data, then this data stored in the “quote” variable is assigned to the “kayneQuote” variable.第84–100行最初检查返回的jsonData是否包含数据。 如果是这样,则使用导入的org.json…代码库,创建一个JSONObject来表示JSON结构中的原始json数据(只是一个很长的String值)。 “ quote”字符串变量访问和名为quote的JSON键值对属性。 如果该属性存在并包含数据,则将存储在“ quote”变量中的该数据分配给“ kayneQuote”变量。
  • Lines 103–107 Include the declaration of the Interface that I mentioned about in the MainActivity class. This defines what method should be implemented when this Interface is used.

    第103–107行包含MainActivity类中提到的Interface的声明。 这定义了使用此接口时应实现的方法。

And that’s it for the code explanation!!

这就是代码说明!!

Once you have either an Android Virtual Device or actual Android device setup, you should see one of the Kayne quotes from the API:

有了Android虚拟设备或实际的Android设备设置后,您应该会从API中看到Kayne引号之一:

I Heard ’Em Say that was the end of the story, now you are a Stronger developer.. ok I will stop with the Kayne song references!

听到'Em说到故事的结尾,现在您是一名Stronger开发人员。.好的,我将停止与Kayne的歌曲引用!

If you have any questions, feel free to comment on this story and I will do my best to help you out.

如果您有任何疑问,请随时评论这个故事,我们将尽力为您提供帮助。

与往常一样,如果您发现这很有用,有趣或两者兼而有之,请在Medium上关注我,以获得更多这些故事! (As always, if you found this useful, entertaining or both, follow me on Medium for more of these stories!)

Thanks and have a good day!

谢谢,祝你有美好的一天!

(…Just published this story and realised that I’ve spelt Kanye wrong…

Kanye West和软件开发有什么共同点……相关推荐

  1. 大话软件开发与开车的共同点

    昨天路上开车,突然有了这个想法,做软件开发与开车,竟然有这么多的相似之处,大致整理了一下思路,和大家分享一下. 一.目的 开车的目的有3个,第一是为了让自己到底目的地(上班族),第二是为了兜风,爱好( ...

  2. 架构设计--仅是软件开发之第二大影响力?!

    SDWest2006(译注1)对我来说是个有趣的大会.我除了星期三之外(当时我正飞往费城参加一个客户会议 == 因此错过了Jolt颁奖部分)每天都在演讲.我也参加了一些谈话和会议:其中最引人关注的是M ...

  3. 软件测试之软件开发模型

    一. 软件开发模型 1 为什么学习软件开发模型 了解开发能够更好的有针对性的做好测试. 2 什么是软件开发模型 软件开发生命周期模型是软件产品从最初构思到退役的过程. 3 常见的开发模型  大爆炸模 ...

  4. 软件开发中的王者荣耀理论

    最近在玩王者荣耀,而且再向荣耀王者的段位发起冲击,打完游戏,回顾的时候,发现万物都有其相关性,王者荣耀晋级理论,同样也适用于软件开发当中,所以写一篇博文来记录这些共同点. 一.每个位置都不能少 王者荣 ...

  5. 软件工程导论08-基于构件的软件开发

    基于构件的软件开发 长期以来的软件开发状况 多数软件都是针对某个具体的应用系统从头进行开发的. 导致:出现了大量的同类软件重复开发,造成大量人力.财力的浪费,而且软件的质量也不高. 对比:汽车工业的生 ...

  6. java 软件开发面试宝典

    一. Java 基础部分........................................................................................ ...

  7. 【20220504】软件开发模式

    时间:2022年05月04日 作者:小蒋聊技术 邮箱:wei_wei10@163.com 大家好,欢迎来到小蒋聊技术.小蒋准备和大家一起聊聊技术的那些事. 现在是五一假期,因为最近疫情的原因大家也只能 ...

  8. 软件测试和软件开发哪个发展更好

    经常有想转IT行业的同学,在了解软件测试和软件开发之后不知道转那个岗位好,今天就系统的,从多个维度来比较软件测试与软件开发,具体包括从基本素质要求.性格要求.入职门槛.知识结构.竞争压力.职业发展.职 ...

  9. 做一名优秀的软件开发qa_如何成为一名优秀的开发人员

    做一名优秀的软件开发qa As a PHP developer, or any kind of developer as a matter of fact, you need to constantl ...

最新文章

  1. jupyter代码字体大小_你可能并不知道这样定制炫酷的jupyter主题
  2. Sliverlight好教程
  3. eclipse中检查项目生成.class文件的地址
  4. Python 100例(上)
  5. 【UOJ168】元旦老人与丛林【图论证明】【最大权闭合子图】【dinic动态推流】
  6. win7下oracle10g安装,专门针对win7下oracle10g安装的详解
  7. java容器类继承_JAVA容器 - weslie - OSCHINA - 中文开源技术交流社区
  8. 曲曲直直线条图计算机教案,【曲曲直直的美术画】_美术教案第三课:曲曲直直(三年级美术下册教案)——小学美术...
  9. LeetCode:226. 翻转二叉树
  10. CSDN博客排名20000以后的用户怎么查询具体排名
  11. 7-11 求链式线性表的倒数第K项 (20 分)
  12. Terminal终端命令(全)
  13. 梅科尔工作室-李庆浩-网页前端7
  14. 刘毅5000词汇_不熟词汇整理_lesson_15 and part_1
  15. 计算机只能在安全模式下起动,电脑不能进入正常的XP系统,只能在安全模式下启动?...
  16. HSI、HSV、RGB、CMY、CMYK、HSL、HSB、Ycc、XYZ、Lab、YUV颜色模型
  17. 瑞士轮赛制模拟器_【入门必读】VGC综合介绍(下篇)【翻译】
  18. 下拉菜单Spinner用法
  19. java生成随机的26位英文字母
  20. android10开发io接口,Android Things:外设I/O接口-GPIO

热门文章

  1. 「编程语言」编程语言InfoQ趋势报告—2019年10月
  2. GBase 8a技术特性-集群架构
  3. 解决selenium打开浏览器不是全屏的问题
  4. linux图形界面切换账户,linux图形界面和字符界面相互转换
  5. 使用c语言easy—x库实现实时钟表
  6. 用Word2Vec建立你的私人购物助手
  7. 2023-01-28 旧的一年的思考与新一年的野望
  8. 流程控制——分支语句
  9. Sophus编译出错:lvalue required as left operand of assignment
  10. 【悟空云课堂】第三十三期:表达式永假/永真(CWE-570:Expression is Always False)