【Android】 网络技术

前言

本文用于记录Android网络技术的使用, 包括我们如何发起一条HTTP请求、解析XML、JOSN格式的数据以及最好用的网络库Retrofit。

使用HTTP协议访问网络

关于HTTP协议的工作原理,我们只需要知道客户端向服务器发起一条HTTP请求,服务器接收到请求之后会返回一些数据给客户端,然后客户端再对这些数据进行解析和处理。接下来我们将实现一下手动发送HTTP请求。

使用HttpURLConnection

我们先来大致了解一下使用HttpURLConnection发送HTTP请求的步骤:

  • 获取HttpURLConnection实例,一般都是先new出一个URL对象并将目标网站传入,然后调用openConnection()方法即可获得实例
Url url=new URL("http://www.baidu.com");
HttpURLConnection connection=(HttpURLConnection) url.openConnection();
  • 设置HTTP请求所使用的方法。常用方法有GET和POST。GET表示希望从服务器那获取数据,POST表示希望提交数据给服务器。
connection.setRequestMethod("GET");
  • 一些其他设置,如设置连接超时、读取超时毫秒数、服务器希望得到的消息头等
  • 调用getInputStream()方法获取服务器返回的输入流,接下来我们就可以对输入流进行读取
InputStream in=connection.getInputStream();
  • 最后调用disconnect()方法将这个HTTP连接关闭
connection.disconnect();

实例

向百度首页发起一条HTTP请求,返回它的HTML代码

  • activity_main,包括发送请求的按钮和展示返回数据的文本框
<?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:orientation="vertical"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><Buttonandroid:id="@+id/send_request"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="Send Request"/><ScrollViewandroid:layout_width="match_parent"android:layout_height="match_parent"><TextViewandroid:id="@+id/response_text"android:layout_width="match_parent"android:layout_height="wrap_content"/></ScrollView>
</LinearLayout>
  • MainActivity
public class MainActivity extends AppCompatActivity implements View.OnClickListener {TextView responseText;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Button sendRequest=(Button) findViewById(R.id.send_request);responseText=(TextView) findViewById(R.id.response_text);sendRequest.setOnClickListener(this);}@Overridepublic void onClick(View view) {if(view.getId()==R.id.send_request){sendRequestWithHttpURLConnection();}}private void sendRequestWithHttpURLConnection(){//开启线程发送网络请求new Thread(new Runnable() {@Overridepublic void run() {HttpURLConnection connection=null;BufferedReader reader=null;try {//向百度的首页发起一条HTTP请求URL url=new URL("https://www.baidu.com");connection=(HttpURLConnection) url.openConnection();connection.setRequestMethod("GET");connection.setConnectTimeout(8000);connection.setReadTimeout(8000);InputStream in=connection.getInputStream();//下面对获取到的输入流(服务器返回的流)进行读取reader=new BufferedReader(new InputStreamReader(in));StringBuilder response=new StringBuilder();String line;while((line=reader.readLine())!=null){response.append(line);}showResponse(response.toString());}catch (Exception e){e.printStackTrace();}finally {if(reader!=null){try {reader.close();}catch (IOException e){e.printStackTrace();}}if(connection!=null){//关闭HTTP连接connection.disconnect();}}}}).start();}private void showResponse(final String response){//因为Android不允许在子线程更新UI,所有我们使用runOnUiThread方法将线程切换为主线程runOnUiThread(new Runnable() {@Overridepublic void run() {//将结果显示到界面上responseText.setText(response);}});}
}

这里主要注意一下showResponse()方法中的runOnUiThread()方法,它可以将线程由子线程切换为主线程。

  • 最后别忘了申请一下网络权限
<uses-permission android:name="android.permission.INTERNET"/>

POST请求

想要发送POST请求,只需将setRequestMethod的参数改为POST,并在获取输入流之前把要提交的数据写出即可。例如我们要向服务器提交用户名和密码,可以写成:

connection.setRequestMethod("POST");
DataOutputStream out=new DataOutputStream(connection.getOutputStream());
//数据与数据之间用&分隔开
out.writeBytes("username=admin&password=123456);

使用OkHttp

OkHttp是一个可以代替原生HttpURLConnection发送HTTP请求的网络通信库。可用于简化我们发送HTTP的步骤。下面我们来简单梳理一下其基本步骤:

  • 首先使用前必须添加其依赖
implementation ("com.squareup.okhttp3:okhttp:4.9.0")
  • 创建一个OkHttpClient实例
OkHttpClient client=new OkHttpClient();
  • 要想发送HTTP请求,我们需要创建一个Request对象(目标网站为百度首页),注意此时请求并未发送
Request request=new Request.Builder().url("http://www.baidu.com").build();
  • 调用OkHttpClient的newCall()方法来创建一个Call对象并调用它的execute()方法来发送请求并获取服务器返回的数据
Response response=client.newCall(request).execute();
  • 获取具体的返回内容
String responseData=response.body().string();

POST请求

  • 我们需要先构建出一个RequestBody对象来存放待提交的数据
RequestBody requestBody=new FormBody.Builder().add("username","admin").add("password","123456").build();
  • 接着在构建出一个Request对象并将待提交的数据传入
Request request=new Request.Builder().url("http://www.baidu.com").post(requestBody).build();

最后就和GET请求一样调用execute()方法来发送请求并获取服务器返回的数据即可。

实例

这里我们就直接在上面的代码进行改造,将上面点击按钮的sendRequestWithHttpURLConnection()方法换为:

sendRequestWithOkHttp();

接着实现这个方法:

	//使用OkHttp发送请求private void sendRequestWithOkHttp(){new Thread(new Runnable() {@Overridepublic void run() {try {//构建OkHttpClient实例OkHttpClient client=new OkHttpClient();//构建Request对象并将目标网址传入Request request=new Request.Builder().url("https://www.baidu.com").build();//调用execute()方法发送请求并接收返回的数据Response response=client.newCall(request).execute();//将返回的数据解析成字符串String responseData=response.body().string();//将返回的信息显示在屏幕上showResponse(responseData);}catch (Exception e){e.printStackTrace();}}}).start();}

解析XML格式数据

数据在网络上传输通常有两种方式XML和JSON,这两种数据分别具有其自己的结构规格和语义,当我们获取到这种数据时,我们要对其进行解析从而取出我们想要的那部分内容,接下来我们先来介绍Pull解析XML格式数据。

Pull解析方式

首先你要确保你已经下载好Tomcat并做好相关配置并将Tomcat启动起来,接下来我们来自定义一段XML格式的数据(这段数据我是定义在D:\Apache\apache-tomcat-8.5.95\webapps\AndroidStudy这个路径下的):

<apps><app><id>1</id><name>Google Maps</name><version>1.0</version></app><app><id>2</id><name>Chrome</name><version>2.1</version></app><app><id>3</id><name>Google Play</name><version>2.3</version></app>
</apps>

接下来我们可以打开浏览器看看是不是没问题,我的Tomcat端口是9000(可以在conf\server.xml文件中修改端口号),所以我输入localhost:9000/AndroidStudy/get_data.xml会显示出以下内容:

在这里插入图片描述

接下来我们就可以去对这些定义好的数据进行解析,我们接着在上面写好的代码上进行修改

  • 首先构造Request对象时url我们要指定为访问本机IP地址,你打开控制台查一下本机电脑IP,如下所示
.url("http://192.168.??.???:9000/AndroidStudy/get_data.xml")
  • 接着实现一个parseXMLWithPull对返回的数据进行解析
	//Pull解析数据private void parseXMLWithPull(String xmlData){try {XmlPullParserFactory factory=XmlPullParserFactory.newInstance();//使用XmlPullParserFactory来创建出XmlPullParser实例XmlPullParser xmlPullParser=factory.newPullParser();//将服务器返回的数据设置进XmlPullParser就可以开始进行解析了xmlPullParser.setInput(new StringReader(xmlData));//通过getEventType()就可以得到当前的解析事件int eventType=xmlPullParser.getEventType();String id="";String name="";String version="";//!=XmlPullParser.END_DOCUMENT表示解析工作还未完成while(eventType!=XmlPullParser.END_DOCUMENT){String nodeName=xmlPullParser.getName();switch (eventType){//开始解析某个节点,将数据取出case XmlPullParser.START_TAG:{if("id".equals(nodeName)){id=xmlPullParser.nextText();} else if ("name".equals(nodeName)) {name=xmlPullParser.nextText();} else if ("version".equals(nodeName)) {version=xmlPullParser.nextText();}break;}//完成解析某个节点,将数据打印case XmlPullParser.END_TAG:{if("app".equals(nodeName)){Log.d("MainActivity","id is "+id);Log.d("MainActivity","name is "+name);Log.d("MainActivity","version is "+version);}break;}default:break;}eventType=xmlPullParser.next();}}catch (Exception e){e.printStackTrace();}}

注意:我们需要在AndroidManifest.xml的<application>标签中加入以下一行代码,不然程序会出现问题

android:usesCleartextTraffic="true"

接着启动程序点击按钮解析出来的数据就打印出来了。

解析JSON数据

接下来学习如何解析JSON格式的数据,首先说一下JSON相比XML的优势——JSON的体积更小,在网络传输的时候可以更省流量。但同时它的缺点是语义性较差,不如XML看起来直观

在解析之前,我们和前面一样,新建一个get_data.json文件,接下来访问localhost:9000/AndroidStudy/get_data.json,如下所示:

在这里插入图片描述

使用JSONObject

  • 和上面一样,构造url时传入自己数据源的url
.url("http://192.168.??.???:9000/AndroidStudy/get_data.json")
  • 接着实现一个parseJSONWithJSONObject()方法对返回的数据进行解析
	//使用JSONObject解析JSON格式的数据private void parseJSONWithJSONObject(String jsonData){try {JSONArray jsonArray=new JSONArray(jsonData);for(int i=0;i<jsonArray.length();i++){JSONObject jsonObject=jsonArray.getJSONObject(i);String id=jsonObject.getString("id");String version=jsonObject.getString("version");String name=jsonObject.getString("name");Log.d("MainActivity","id is "+id);Log.d("MainActivity","name is "+name);Log.d("MainActivity","version is "+version);}}catch (Exception e){e.printStackTrace();}}

我们前面定义的是一个JSON数组,所以这里我们先将服务器返回的数据传到一个JSONArray对象中,接着遍历JSONArray对象,这里我们使用JSONObject来接收遍历出来的元素,接着取出id、name、version这些数据。

接着启动程序点击按钮解析出来的数据就打印出来了。

使用GSON

可以看到上面使用JSONObject解析JSON数据已经非常方便了,但其实还有更加方便的,那就是Google提供的GSON开源库。

GSON的方便之处在于,它可以将一段JSON格式的数据自动映射成一个对象,例如有一段JSON格式数据如下所示:

{"name":"Tom","age":"20"}

我们就可以先定义一个Person类,并加入name和age属性,接下来只需要使用下面代码即可将JSON数据解析成一个Person对象:

Gson gson=new Gson();
Person person=gson.formJson(jsonData,Person.class);

如果是解析一个JSON数组的话,我们就要借助TypeToken将期望解析成的数据类型传入formJson()方法中:

List<Person> people=gson.formJson(jsonData,new TypeToken<List<Person>>(){}.getType());

下面我们来使用GSON解析JSON数据。

  • 添加依赖
implementation ("com.google.code.gson:gson:2.9.0")
  • 新建App类,添加id、name、version这三个属性
public class App {private String id;private String name;private String version;public String getId() {return id;}public void setId(String id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getVersion() {return version;}public void setVersion(String version) {this.version = version;}
}
  • 实现一个parseJSONWithGSON()方法对JSON数据进行解析
	//使用GSON解析JSON数据private void parseJSONWithGSON(String jsonData){Gson gson=new Gson();List<App> appList=gson.fromJson(jsonData,new TypeToken<List<App>>(){}.getType());for (App app : appList) {Log.d("MainActivity","id is "+app.getId());Log.d("MainActivity","name is "+app.getName());Log.d("MainActivity","version is "+app.getVersion());}}

在这我们先new出一个Gson实例,接着调用formJson方法并将服务器返回的数据和我们期望解析成的数据格式传入,获得一个App集合,最后只需遍历集合获取数据打印即可。

Retrofit

Retrofit是在OkHttp的基础上进一步开发出来的应用层网络通信库,OkHttp注重的是底层通信的实现,而Retrofit注重的是上层接口的封装,Retrofit允许我们用更加面向对象的思维进行网络操作。

设计思想

  • 首先,Retrofit允许我们先配置好一个根路径,在指定服务器接口地址时只需要使用相对路径即可,这样我们就不用每次去指定完整的URL地址了。
  • 其次,Retrofit允许我们对服务器接口进行归类,将功能同属一类的服务器接口定义在同一个接口文件中,这样我们的代码结构会更加合理。
  • 最后,我们完全不用关心网络通信的细节,只需在接口文件中声明一系列方法和返回值,然后通过注解的方式指定该方法对应哪个服务器接口,以及需要提供哪些参数。当我们的程序调用该方法时,Retrofit会自动向对应的服务器发起请求,并将响应的数据解析成返回值声明的类型。

这些设计思想就使得我们可以用更加面向对象的思维来进行网络操作了。

案例

重新新建一个项目RetrofitTest。

在这我们仍继续使用上面的get_data.json数据

  • 添加依赖
implementation ("com.squareup.retrofit2:retrofit:2.6.1")
implementation ("com.squareup.retrofit2:converter-gson:2.6.1")

第一条依赖会将Retrofit、OkHttp、Okio这几个库一起下载,第二条依赖会将GSON库下载下来,所以Retrofit会将返回的JSON数据自动解析成对象。

  • 新建App类
public class App {private String id;private String name;private String version;public String getId() {return id;}public void setId(String id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getVersion() {return version;}public void setVersion(String version) {this.version = version;}
}
  • 创建接口文件并在其内部定义具体的服务器接口方法。我们可以根据功能的不同对接口服务进行分类,例如在一个教务系统中,对老师的操作我们可以定义一个TeacherService,对学生的操作我们可以定义一个StudentService。
public interface AppService {@GET("get_data.json")public Call<List<App>> getAppData();}

在这我们就定义了一个getAppData()方法,返回值类型是Call类型,@GET注解表示发送的是一条GET请求,后面接的是具体路径。这里返回值类型我们需要注意一下,返回值类型我们必须声明成Retrofit内置的Call类型,然后通过泛型指定服务器返回的数据转要换成什么类型,这里我们转换成List<App>。

  • 在布局中添加一个按钮用于发送请求
<?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:orientation="vertical"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><Buttonandroid:id="@+id/getAppDataBtn"android:text="Get App Data"android:layout_width="match_parent"android:layout_height="wrap_content"/></LinearLayout>
  • 点击按钮发送请求并将返回的JSON数据解析出来打印到控制台
public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Button getData=(Button) findViewById(R.id.getAppDataBtn);getData.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {Retrofit retrofit= new Retrofit.Builder()//配置根路径->你自己主机的IP.baseUrl("http://192.168.??.???:9000/AndroidStudy/")//指定解析数据时使用的转换库.addConverterFactory(GsonConverterFactory.create()).build();//创建出AppService接口的动态代理对象,接下来就可以随意调用该接口中的方法了AppService appService=retrofit.create(AppService.class);//调用getAppData方法之后会返回一个Call<List<App>>对象,接着调用enqueue()方法,Retrofit就会根据注解中配置的服务器接口地址去进行网络请求,服务器响应的数据也会回调到enqueue()方法中的Callback实现里appService.getAppData().enqueue(new Callback<List<App>>() {@Overridepublic void onResponse(Call<List<App>> call, Response<List<App>> response) {//调用response.body()后我们会得到Retrofit解析后的对象List<App> appList = response.body();if(appList!=null){for (App app : appList) {Log.d("MainActivity","id is "+app.getId());Log.d("MainActivity","name is "+app.getName());Log.d("MainActivity","version is "+app.getVersion());}}}@Overridepublic void onFailure(Call<List<App>> call, Throwable t) {t.printStackTrace();}});}});}
}

注意:我们在构建Retrofit对象时上面两个方法是必须调用的。发起请求时,Retrofit会自动在内部开启子线程,当数据回调到Callback中之后又会自动切回主线程,整个过程我们都不需要考虑线程切换问题。

  • 最后需要注意的是我们要在AndroidManifest.xml文件中需要加入网络权限以及在<application>标签中加入以下配置
<!--    网络权限-->
<uses-permission android:name="android.permission.INTERNET"/><application>
...
android:networkSecurityConfig="@xml/network_config"
</application>

在res->xml文件下创建network_config,内容如下:

<?xml version="1.0" encoding="utf-8" ?>
<network-security-config><base-config cleartextTrafficPermitted="true"><trust-anchors><certificates src="system"/></trust-anchors></base-config>
</network-security-config>

最后我们点击按钮发送网络请求返回的JSON数据就会被解析并将我们想要的数据打印在控制台了。

处理复杂的接口地址类型

  • 上面我们的接口地址是静态的,但在很多场景下接口地址是动态变化的,如以下接口地址:
GET http://example.com/<page>/get_data.json

上面的<page>表示页数,我们传入不同的页数,显示的内容也是不一样的,服务器返回的数据也是不同的,像这种动态接口地址在Retrofit中应该怎么写呢(实体类为Data,创建了一个DataService接口):

public interface DataService{@GET("{page}/get_data.json")public Call<Data> getData(@Path("page") int page);}

在接口地址中,我们使用{page}来表示一个占位符,在getData()方法中添加了一个page参数,并使用@Path(“page”)注解来声明这个参数,这样当发送请求时,Retrofit会将page参数自动替换掉{page},这样不就实现了动态参数。

  • 另外很多服务器的接口要求我们传入多个参数,如以下接口地址:
GET http://examlpe.com/get_data.json?u=<user>&t=<token>

带参数的GET请求格式如上,使用?来连接参数部分,每个参数都是使用=连接的键值对,多个参数之间使用&分隔,上面的示例就表示我们要传入user和token这两个参数,对于这种接口地址,我们可以使用@Path注解来解决,如下:

public interface DataService{@GET("get_data.json?u={user}&t={token}")public Call<Data> getData(@Path("user") String user,@Path("token") String token);}

也可以使用Retrofit针对这类GET请求提供的语法支持:

public interface DataService{@GET("get_data.json")public Call<Data> getData(@Query("u") String user,@Query("t") String token);}

这两种方式都可以将接口地址中的<user>和<token>用user和token替换。

其他注解

  • @POST:提交数据
  • @PUT、@PATCH:修改数据
  • @DELETE:删除数据
DELETE http://example.com/data/<id>

上述接口地址表示要根据id删除一条数据,要想发出这样的请求我们可以写成下面这样

public interface DataService{@DELETE("data/{id}")public Call<ResponseBody> deleteData(@Path("id") String id);}

可以看到上面我们将Call的泛型指定为ResponseBody,这是因为@POST、@PUT、@PATCH、@DELETE注解与@GET注解不同,它们时操作服务器上的数据而不是获取数据,所以它们对服务器响应的数据并不关心。使用ResponseBody表示Retrofit能接收任何类型的响应数据,并且不会对响应数据进行解析。

提交数据

向服务器提交数据,如以下接口地址

POST http://example.com/data/create

使用POST来提交数据,需要我们将数据放到HTTP请求的body部分,Retrofit给我们提供了一个@Body注解来完成

public interface DataService{@POST("data/create")public Call<ResponseBody> postData(@Body Data data);//Data是你自己定义的要提交的数据}

这样当发送一个POST请求时,Retrofit会将Data对象中的数据转换成JSON格式的数据并放到HTTP请求的body部分。

关于Android网络技术的使用的分享到此结束,希望对您有帮助!

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

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

相关文章

【MySQL 数据宝典】【索引原理】- 001 索引原理分析 (AVL树、B-Tree、B+Tree)

一、索引定义 MySQL官方对索引定义&#xff1a;是存储引擎用于快速查找记录的一种数据结构。需要额外开辟空间和数据维护工作。 索引是物理数据页存储&#xff0c;在数据文件中&#xff08;InnoDB&#xff0c;ibd文件&#xff09;&#xff0c;利用数据页(page)存储。 索引可以…

springcloud微服务搭建多数据源(mysql,oracle,postgres,等等)管理模块,支持通过注解方式切换不同类型的数据库

1.背景 同一套微服务管理系统&#xff0c;业务完全一样&#xff0c;但不同的客户可能要求使用自己熟悉的数据库&#xff0c;比如&#xff0c;mysql&#xff0c;oracle&#xff0c;postgres&#xff0c;还有一些国产数据库。如果能够将数据库模块独立出来&#xff0c;兼容各家的…

【数据结构】算法的效率(时间复杂度和空间复杂度)

目录 一.算法的效率 二.时间复杂度 1.概念 2.大O的渐进表示法 3.常见时间复杂度计算举例 三.空间复杂度 四.常见复杂度对比 五. 复杂度的oj练习 1.消失的数字 2.轮转数字&#xff1a; 一.算法的效率 算法在编写成可执行程序后&#xff0c;运行时需要耗费时间资源和空…

【C++初阶】string

✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅ ✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨ &#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1…

【Linux】信号的产生

目录 一. 信号的概念signal() 函数 二. 信号的产生1. 键盘发送2. 系统调用kill()raise()abort() 3. 软件条件alarm() 4. 硬件异常除零错误:野指针: 三. 核心转储 一. 信号的概念 信号是消息的载体, 标志着不同的行为; 是进程间发送异步信息的一种方式, 属于软中断. 信号随时都…

智能优化算法及 MATLAB 实现(书籍推荐)

智能优化算法及 MATLAB 实现&#xff08;书籍推荐&#xff09; 介绍前言目录第1章 粒子群优化算法原理及其MATLAB实现第2章 哈里斯鹰优化算法原理及其MATLAB实现第3章 沙丘猫群优化算法原理及其MATLAB实现第4章 鲸鱼优化算法原理及其MATLAB实现第5章 大猩猩部队优化算法原理及其…

20232801 2023-2024-2 《网络攻防实践》实践八报告

20232801 2023-2024-2 《网络攻防实践》实践八报告 1.实践内容 1.动手实践任务: 对提供的rada恶意代码样本&#xff0c;进行文件类型识别&#xff0c;脱壳与字符串提取&#xff0c;以获得rada恶意代码的编写作者. 2.动手实践任务二&#xff1a;分析Crackme程序 在WinXP Attac…

Leetcode - 周赛395

目录 一&#xff0c;3131. 找出与数组相加的整数 I 二&#xff0c;3132. 找出与数组相加的整数 II 三&#xff0c;3133. 数组最后一个元素的最小值 四&#xff0c;3134. 找出唯一性数组的中位数 一&#xff0c;3131. 找出与数组相加的整数 I 本题本质就是求两个数组最小值的…

[iOS]组件化开发

一、组件化开发基础 1.组件定义 在软件开发中&#xff0c;一个组件是指一个独立的、可替换的软件单元&#xff0c;它封装了一组相关的功能。组件通过定义的接口与外界交互&#xff0c;并且这些接口隔离了组件内部的实现细节。在Swift语言中&#xff0c;组件可以是一个模块、一…

文献速递:肺癌早期诊断---低剂量胸部计算机断层扫描上的三维深度学习端到端肺癌筛查

Title 题目 End-to-end lung cancer screening with three-dimensional deep learning on low-dose chest computed tomography 低剂量胸部计算机断层扫描上的三维深度学习端到端肺癌筛查 01文献速递介绍 2018年估计有160,000例死亡病例&#xff0c;肺癌是美国最常见的癌症…

青少年软件编程(Scratch一级)等级考试试卷(2019年12月)

客观题 题目总数&#xff1a;35 总分数&#xff1a;70 时间&#xff1a;不限时 CSDN添加图片自带水印&#xff0c;可至文末获取无水印版word文档 第 1 题 单选题 下列关于舞台的描述&#xff0c;不正确的是&#xff1f;&#xff08; &#xff09; A.Scratch只能…

MVVM框架

LifeCycle 翻译为生命周期。意义就是为了监控某个事件的生命周期。 此处用了addObserver函数&#xff0c;代表添加监控者。监控该activity的变化。activity的状态有onCreate,onStop,onDestory等&#xff0c;该函数表示对所有状态进行监听。 该方法使用了标签的方法&#xff0c…

护航智慧交通安全 | 聚铭精彩亮相2024交通科技创新及信创产品推广交流会

4月26日&#xff0c;石家庄希尔顿酒店内&#xff0c;河北省智能交通协会盛大举办2024年度交通科技创新及信创产品推广交流会。聚铭网络受邀参与&#xff0c;携旗下安全产品及解决方案精彩亮相&#xff0c;为智慧交通安全保驾护航。 为深化高速公路创新驱动发展战略&#xff0…

2024 java easyexcel poi word模板填充数据,多个word合成一个word

先看效果 一、准备工作 1.word模版 2.文件路径 二、pom依赖 <!-- easyexcel --><dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>2.1.7</version></dependency><depe…

如何申请免费SSL证书,把网站升级成HTTPS

HTTPS&#xff08;Hyper Text Transfer Protocol Secure&#xff09;是一种用于安全数据传输的网络协议&#xff0c;它可以有效地保护网站和用户之间的通信安全。然而&#xff0c;要使一个网站从HTTP升级到HTTPS&#xff0c;就需要一个SSL证书。那么&#xff0c;如何申请免费的…

一文解读 SQL 生成工具

SQL 生成工具可用于测试 Parser 与其他数据库产品的兼容性&#xff0c;通过解析 YACC 语法文件中的产生式&#xff0c;生成对应的 SQL 语句&#xff0c;再使用数据库执行该 SQL&#xff0c;根据结果判断语句是否与其他数据库语法兼容。 01工具使用 语法文件预处理 预处理目的…

notion使用小tip(待补充)

可以替代思维导图是一个很棒的软件 公式编辑&#xff1a;latex 网站链接&#xff1a;LATEX语法 一些常用的用法&#xff1a; 下标&#xff1a;a_{Si} 分数&#xff1a;\frac{}{} 乘&#xff1a;\times 向量&#xff1a;\vec{} pai (3.14159…) : \pi 直接用公式编辑器&#…

【kettle005】kettle访问Oracle数据库并处理数据至execl文件(已更新)

1.一直以来想写下基于kettle的系列文章&#xff0c;作为较火的数据ETL工具&#xff0c;也是日常项目开发中常用的一款工具&#xff0c;最近刚好挤时间梳理、总结下这块儿的知识体系。 2.熟悉、梳理、总结下Oracle数据库相关知识体系 3.欢迎批评指正&#xff0c;跪谢一键三连&am…

全栈从0到1 3D旅游地图标记和轨迹生成

功能演示 演示视频 体验地址 Vercel App 开发技术栈&#xff1a; NextJs&#xff08;前端框架&#xff09;React&#xff08;前端框架&#xff09;TailwindCSS &#xff08;CSS样式&#xff09;echart echart gl &#xff08;地图生成&#xff09;shadui&#xff08;UI组件…

C语言阶段性测试错题纠正与拓展

引言&#xff1a;在2024年4月26日&#xff0c;我进行了C语言知识的“期末考试”。通过这次考试&#xff0c;我发现了我的知识漏洞。所以&#xff0c;我写下这篇博客来记录我的错题&#xff0c;并进行纠正&#xff0c;然后对于以前遗忘知识的回顾。 更多有关C语言的知识详解可前…