【Android】最好用的网络库:Retrofit

最好用的网络库:Retrofit

文章目录

  • 最好用的网络库:Retrofit
    • Retrofit的基本用法
    • Retrofit的使用逻辑
    • Retrofit的基本操作
    • 处理复杂的接口地址类型
    • 进阶
      • 删除
      • 提交
      • header中指定参数
    • Retrofit构建器的最佳写法
    • Retrofit的使用封装

Retrofit的基本用法

Retrofit是一款由Square公司开发的网络库,但是它和OkHttp的定位完全不同。
OkHttp侧重的是底层通信的实现,而Retrofit侧重的是上层接口的封装。事实上,Retrofit就是Square公司在OkHttp的基础上进一步开发出来的应用层网络通信库,使得我们可以用更加面向对象的思维进行网络操作。Retrofit的项目主页地址是:https://github.com/square/retrofit。

Retrofit的设计基于以下几个事实。同一款应用程序中所发起的网络请求绝大多数指向的是同一个服务器域名。这个很好理解,因为任何公司的产品,客户端和服务器都是配套的,很难想象一个客户端一会去这个服务器获取数据,一会又要去另外一个服务器获取数据吧?
另外,服务器提供的接口通常是可以根据功能来归类的。比如新增用户、修改用户数据、查询用户数据这几个接口就可以归为一类,上架新书、销售图书、查询可供销售图书这几个接口也可以归为一类。
将服务器接口合理归类能够让代码结构变得更加合理,从而提高可阅读性和可维护性。最后,开发者肯定更加习惯于调用一个接口,获取它的返回值”这样的编码方式,但当调用的是服务器接口时,却很难想象该如何使用这样的编码方式。
其实大多数人并不关心网络的具体通信细节,但是传统网络库的用法却需要编写太多网络相关的代码。

Retrofit的使用逻辑

而Retrofit的用法就是基于以上几点来设计的,首先我们可以配置好一个根路径,然后在指定服务器接口地址时只需要使用相对路径即可,这样就不用每次都指定完整的URL地址了。
在这里插入图片描述

另外,Retrofit允许我们对服务器接口进行归类,将功能同属一类的服务器接口定义到同一个接口文件当中,从而让代码结构变得更加合理。
在这里插入图片描述

最后,我们也完全不用关心网络通信的细节,只需要在接口文件中声明一系列方法和返回值,然后通过注解的方式指定该方法对应哪个服务器接口,以及需要提供哪些参数。

当我们在程序中调用该方法时,Retrofit会自动向对应的服务器接口发起请求,并将响应的数据解析成返回值声明的类型。这就使得我们可以用更加面向对象的思维来进行网络操作。

Retrofit的基本操作

要想使用Retrofit,我们需要先在项目中添加必要的依赖库。编辑app/build.gradle文件,在dependencies闭包中添加如下内容:

implementation 'com.squareup.retrofit2:retrofit:2.6.1' 
implementation 'com.squareup.retrofit2:converter-gson:2.6.1'

由于Retrofit是基于OkHttp开发的,因此添加上述第一条依赖会自动将Retrofit、OkHttp和Okio这几个库一起下载,我们无须再手动引入OkHttp库。另外,Retrofit还会将服务器返回的JSON数据自动解析成对象,因此上述第二条依赖就是一个Retrofit的转换库,它是借助GSON来解析JSON数据的,所以会自动将GSON库一起下载下来,这样我们也不用手动引入GSON库了。除了GSON之外,Retrofit还支持各种其他主流的JSON解析库,包括Jackson、Moshi等,不过毫无疑问GSON是最常用的。

由于Retrofit会借助GSON将J将JSON数据转换成对象,因此这里同样需要新增一个App类,并加入id、name和version这3个字段,如下所示

public class App {private String id;private String name;private String version;// 构造函数public App(String id, String name, String version) {this.id = id;this.name = name;this.version = version;}// Getter和Setter方法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;}// toString方法,用于打印对象信息@Overridepublic String toString() {return "App{" +"id='" + id + '\'' +", name='" + name + '\'' +", version='" + version + '\'' +'}';}
}

接下来,我们可以根据服务器接口的功能进行归类,创建不同种类的接口文件,并在其中定义对应具体服务器接口的方法。不过由于这里只需要定义一个接口文件,并包含一个方法即可。新建AppService接口,代码如下所示:

import retrofit2.Call;
import retrofit2.http.GET;
import java.util.List;public interface AppService {// 使用HTTP GET方法请求"get_data.json"路径@GET("get_data.json")Call<List<App>> getAppData();
}

通常Retrofit的接口文件建议以具体的功能种类名开头,并以Service结尾,这是一种比较好的命名习惯。上述代码中有两点需要我们注意。

  1. 第一就是在getAppData()方法上面添加的注解,这里使用了一个@GET注解,表示当调用getAppData()方法时Retrofit会发起一条GET请求,请求的地址就是我们在@GET注解中传入的具体参数。注意,这里只需要传入请求地址的相对路径即可,根路径我们会在稍后设置。
  2. 第二就是getAppData()方法的返回值必须声明成Retrofit中内置的Call类型,并通过泛型来指定服务器响应的数据应该转换成什么对象。由于服务器响应的是一个包含App数据的JSON数组,因此这里我们将泛型声明成List。

当然,Retrofit还提供了强大的Call Adapters功能来允许我们自定义方法返回值的类型,比如Retrofit结合RxJava使用就可以将返回值声明成Observable、Flowable等类型
增加一个button用来测试功能

之后开始使用

import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import android.widget.Button;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import java.util.List;
public class MainActivity extends AppCompatActivity {private Button getAppDataBtn;private AppService appService;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);getAppDataBtn = findViewById(R.id.get_app_data_btn); // 假设按钮的ID是get_app_data_btngetAppDataBtn.setOnClickListener(view -> {Retrofit retrofit = new Retrofit.Builder().baseUrl("http://10.0.2.2/").addConverterFactory(GsonConverterFactory.create()).build();appService = retrofit.create(AppService.class);appService.getAppData().enqueue(new Callback<List<App>>() {@Overridepublic void onResponse(Call<List<App>> call, Response<List<App>> response) {List<App> list = response.body();if (list != null) {for (App app : list) {//do}}}@Overridepublic void onFailure(Call<List<App>> call, Throwable t) {t.printStackTrace();}});});}
}

可以看到,在“Get App Data”按钮的点击事件当中,

首先使用了Retrofit.Builder来构建一个Retrofit对象,其中baseUrl()方法用于指定所有Retrofit请求的根路径,addConverterFactory()方法用于指定Retrofit在解析数据时所使用的转换库,这里指定成GsonConverterFactory。

注意这两个方法都是必须调用的。有了Retrofit对象之后,我们就可以调用它的create()方法,并传入具体Service接口所对应的Class类型,创建一个该接口的动态代理对象。如果你并不熟悉什么是动态代理也没有关系,你只需要知道有了动态代理对象之后,我们就可以随意调用接口中定义的所有方法,而Retrofit会自动执行具体的处理就可以了。

对应到上述的代码当中,当调用了AppService的getAppData()方法时,会返回一个Call<List>对象,这时我们再调用一下它的enqueue()方法,Retrofit就会根据注解中配置的服务器接口地址去进行网络请求了,服务器响应的数据会回调到enqueue()方法中传入的Callback实现里面。需要注意的是,当发起请求的时候,Retrofit会自动在内部开启子线程,当数据回调到Callback中之后,Retrofit又会自动切换回主线程,整个操作过程中我们都不用考虑线程切换问题。在Callback的onResponse()方法中,调用response.body()方法将会得到Retrofit解析后的对象,也就是List类型的数据,最后遍历List,将其中的数据打印出来即可。

处理复杂的接口地址类型

为了方便举例,这里先定义一个Data类,并包含id和content这两个字段,如下所示:

public class Data {private String id;private String content;// 构造函数public Data(String id, String content) {this.id = id;this.content = content;}// id的getter和setterpublic String getId() {return id;}public void setId(String id) {this.id = id;}// content的getter和setterpublic String getContent() {return content;}public void setContent(String content) {this.content = content;}// toString方法,用于打印对象信息@Overridepublic String toString() {return "Data{" +"id='" + id + '\'' +", content='" + content + '\'' +'}';}
}

然后我们先从最简单的看起,比如服务器的接口地址如下所示:GET ``http://example.com/get_data.json

这是最简单的一种情况,接口地址是静态的,永远不会改变。

但是显然服务器不可能总是给我们提供静态类型的接口,在很多场景下,接口地址中的部分内容可能会是动态变化的,比如如下的接口地址:

GET ``http://example.com/``<page>``/get_data.json

在这个接口当中,部分代表页数,我们传入不同的页数,服务器返回的数据也会不同。这种接口地址对应到Retrofit当中应该怎么写呢?其实也很简单,如下所示:

import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Path;
public interface ExampleService {// 使用HTTP GET方法请求"{page}/get_data.json"路径// 其中{page}是一个动态的路径参数@GET("{page}/get_data.json")Call<Data> getData(@Path("page") int page);
}

在@GET注解指定的接口地址当中,这里使用了一个{page}的占位符,然后又在getData()方法中添加了一个page参数,并使用@Path(“page”)注解来声明这个参数。

这样当调用getData()方法发起请求时,Retrofit就会自动将page参数的值替换到占位符的位置,从而组成一个合法的请求地址。

另外,很多服务器接口还会要求我们传入一系列的参数,格式如下:

GET ``http://example.com/get_data.json?u=<user>&t=<token>

这是一种标准的带参数GET请求的格式。接口地址的最后使用问号来连接参数部分,每个参数都是一个使用等号连接的键值对,多个参数之间使用“&”符号进行分隔。那么很显然,在上述地址中,服务器要求我们传入user和token这两个参数的值。对于这种格式的服务器接口,我们可以使用刚才所学的@Path注解的方式来解决,但是这样会有些麻烦,Retrofit针对这种带参数的GET请求,专门提供了一种语法支持:

import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Query;public interface ExampleService {// 使用HTTP GET方法请求"get_data.json"路径// 并带有查询参数"u"和"t"@GET("get_data.json")Call<Data> getData(@Query("u") String user, @Query("t") String token);
}

这里在getData()方法中添加了user和token这两个参数,并使用@Query注解对它们进行声明。这样当发起网络请求的时候,Retrofit就会自动按照带参数GET请求的格式将这两个参数构建到请求地址当中。

进阶

HTTP并不是只有GET请求这一种类型,而是有很多种,其中比较常用的有GET、POST、PUT、PATCH、DELETE这几种。它们之间的分工也很明确,简单概括的话,GET请求用于从服务器获取数据,POST请求用于向服务器提交数据,PUT和PATCH请求用于修改服务器上的数据,DELETE请求用于删除服务器上的数据。

删除

而Retrofit对所有常用的HTTP请求类型都进行了支持,使用@GET、@POST、@PUT、@PATCH、@DELETE注解,就可以让Retrofit发出相应类型的请求了。比如服务器提供了如下接口地址:

DELETE ``http://example.com/data/<id>

这种接口通常意味着要根据id删除一条指定的数据,而我们在Retrofit当中想要发出这种请求就可以这样写:

import retrofit2.Call;
import retrofit2.http.DELETE;
import retrofit2.http.Path;
import okhttp3.ResponseBody;public interface ExampleService {// 使用HTTP DELETE方法请求"data/{id}"路径// 其中{id}是一个动态的路径参数@DELETE("data/{id}")Call<ResponseBody> deleteData(@Path("id") String id);
}

这里使用了@DELETE注解来发出DELETE类型的请求,并使用了@Path注解来动态指定id,这些都很好理解。但是在返回值声明的时候,我们将Call的泛型指定成了ResponseBody,这是什么意思呢?

由于POST、PUT 、PATCH、DELETE这几种请求类型与GET请求不同,它们更多是用于操作服务器上的数据,而不是获取服务器上的数据,所以通常它们对于服务器响应的数据并不关心。这个时候就可以使用ResponseBody,表示Retrofit能够接收任意类型的响应数据,并且不会对响应数据进行解析。

提交

那么如果我们需要向服务器提交数据该怎么写呢?比如如下的接口地址:

POST http://example.com/data/create 
{"id": 1, "content": "The description for this data."}

使用POST请求来提交数据,需要将数据放到HTTP请求的body部分,这个功能在Retrofit中可以借助@Body注解来完成:

import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.POST;
import okhttp3.ResponseBody;
import java.util.List;public interface ExampleService {// 使用HTTP POST方法请求"data/create"路径// @Body注解用来指定请求体@POST("data/create")Call<ResponseBody> createData(@Body Data data);
}

可以看到,这里我们在createData()方法中声明了一个Data类型的参数,并给它加上了@Body注解。这样当Retrofit发出POST请求时,就会自动将Data对象中的数据转换成JSON格式的文本,并放到HTTP请求的body部分,服务器在收到请求之后只需要从body中将这部分数据解析出来即可。这种写法同样也可以用来给PUT、PATCH、DELETE类型的请求提交数据。

header中指定参数

最后,有些服务器接口还可能会要求我们在HTTP请求的header中指定参数,比如:

GET http://example.com/get_data.json 
User-Agent: okhttp 
Cache-Control: max-age=0

这些header参数其实就是一个个的键值对,我们可以在Retrofit中直接使用@Headers注解来对它们进行声明。

import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Headers;
import okhttp3.ResponseBody;public interface ExampleService {// 使用HTTP GET方法请求"get_data.json"路径// @Headers注解用来添加请求头@Headers({"User-Agent: okhttp","Cache-Control: max-age=0"})@GET("get_data.json")Call<Data> getData();
}

但是这种写法只能进行静态header声明,如果想要动态指定header的值,则需要使用@Header注解,如下所示:

import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Header;
import your.package.name.Data; // 确保替换为Data类的实际包路径public interface ExampleService {// 使用HTTP GET方法请求"get_data.json"路径// @Header注解用来添加动态的请求头@GET("get_data.json")Call<Data> getData(@Header("User-Agent") String userAgent, @Header("Cache-Control") String cacheControl);
}

现在当发起网络请求的时候,Retrofit就会自动将参数中传入的值设置到User-Agent和CacheControl这两个header当中,从而实现了动态指定header值的功能。

Retrofit构建器的最佳写法

获取Service接口的动态代理对象

import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import your.package.name.AppService; // 确保替换为AppService接口的实际包路径// 创建Retrofit实例
Retrofit retrofit = new Retrofit.Builder().baseUrl("http://10.0.2.2/") // 注意这里的URL应该以斜杠("/")结尾.addConverterFactory(GsonConverterFactory.create()).build();// 使用Retrofit实例来创建AppService接口的实例
AppService appService = retrofit.create(AppService.class);

我们想要得到AppService的动态代理对象,需要先使用Retrofit.Builder构建出一个Retrofit对象,然后再调用Retrofit对象的create()方法创建动态代理对象

确实也没有每次都写一遍的必要,因为构建出的Retrofit对象是全局通用的,只需要在调用create()方法时针对不同的Service接口传入相应的Class类型即可。因此,我们可以将通用的这部分功能封装起来,从而简化获取Service接口动态代理对象的过程。

新建一个ServiceCreator单例类,代码如下所示:

import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;public class ServiceCreator {// 定义基础URLprivate static final String BASE_URL = "http://10.0.2.2/";// 静态内部类单例,用于创建Retrofit实例private static class Holder {private static final Retrofit RETROFIT_INSTANCE = new Retrofit.Builder().baseUrl(BASE_URL).addConverterFactory(GsonConverterFactory.create()).build();}// 私有构造函数,防止外部实例化private ServiceCreator() {}// 泛型方法,用于创建服务接口的实例public static <T> T create(Class<T> serviceClass) {return Holder.RETROFIT_INSTANCE.create(serviceClass);}
}

这里我们使用object关键字让ServiceCreator成为了一个单例类,并在它的内部定义了一个BASE_URL常量,用于指定Retrofit的根路径。

然后同样是在内部使用Retrofit.Builder构建一个Retrofit对象,注意这些都是用private修饰符来声明的,相当于对于外部而言它们都是不可见的。

最后,我们提供了一个外部可见的create()方法,并接收一个Class类型的参数。当在外部调用这个方法时,实际上就是调用了Retrofit对象的create()方法,从而创建出相应Service接口的动态代理对象。

经过这样的封装之后,Retrofit的用法将会变得异常简单,比如我们想获取一个AppService接口的动态代理对象,只需要使用如下写法即可:

import your.package.name.AppService; // 确保替换为AppService接口的实际包路径// 创建AppService接口的实例
AppService appService = ServiceCreator.create(AppService.class);

在这里插入图片描述

之后就可以随意调用AppService接口中定义的任何方法了。

Retrofit的使用封装

在经过上面我们对retrofit的一次封装之后,我们现在调用retrofit发送网络请求时,只需要AppService appService = ServiceCreator.create(AppService.class);取得了AppService这个构建器,之后使用这个构建器直接调用方法即可

不过我们也可以将一个常用的请求处理进行封装,在封装的帮助类中进行处理,这样在外部使用时只需要我们new出该类,调用方法就可以直接获取我们请求返回的数据所封装成的类,这个时候就可以直接使用这个类,虽然这一步显得有点鸡肋,不过这一步在我们使用的多的请求中,可以比较方便。

首先写一个回调接口:

public interface AppDataCallback {void onSuccess(App AppData);void onError(Throwable error);
}

在这里插入图片描述

接下来,封装一个类

首先通过构造方法获取一个AppService实例

之后封装一下获取请求过程,获得一个Call对象

接下来进入主要的部分,我们在这里调用内部方法getAppData获取Call对象,再调用enqueue方法进行使用,enqueue方法允许异步地执行这个请求,如果成功获取,我们可以在这里对数据进行处理,比如清理无用数据,扩展数据等等,这里我们就不进行处理,直接返回,我们调用AppDataCallback接口的onsuccess将数据返回。

public class AppServiceHelper {private AppService service;public AppServiceHelper() {this.service = ServiceCreator.create(AppService.class);}//获取AppService实例public AppServiceHelper(AppService service) {this.service = service;}//获取AppService实例// 发送请求并返回 Call<App> 对象public Call<App> getAppData(String apiKey, String cityCode, String extensions) {return service.getAppData(apiKey, cityCode, extensions);}// 异步执行请求并处理结果public void getAppDataAsync(String apiKey, String cityCode, String extensions,  final AppDataCallback callback) {Call<App> call = getAppData(apiKey, cityCode, extensions);call.enqueue(new Callback<App>() {@Overridepublic void onResponse(Call<App> call1, Response<Root> response) {if (response.isSuccessful()) {App AppData = response.body();if (response.isSuccessful()) {callback.onSuccess(AppData);} else {callback.onError(new Throwable("Failed with status code: " + response.code()));}}}@Overridepublic void onFailure(Call<App> call1, Throwable t) {callback.onError(t);}});}
}

在这里插入图片描述

之后我们就可以使用这个辣:

AppServiceHelper helper = new AppServiceHelper();
helper.getWeatherDataAsync(....){
@Overridepublic void onSuccess(Root weatherData) {// 这里仍然是在子线程中// 切换到主线程来更新UIrunOnUiThread(new Runnable() {@Overridepublic void run() {// 在这里更新UI}});}@Overridepublic void onError(Throwable t) {// 错误处理}
}

在这里插入图片描述

使用的时候我们获取这个帮助类实例,然后直接调用方法,即可获得数据对象,之后开始使用即可。

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

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

相关文章

html2Canvas和jspdf导出长pdf

续使用html2canvas和jspdf导出pdf包含跨页以及页脚_jspdf.umd.min.js-CSDN博客我的这篇文章再写一种情况因为最近我也使用到了 具体的html2Canvas和jspdf的我就不说了&#xff0c;直接开始了&#xff0c; 在公共方法的文件夹中建立一个新的文件htmlToPdf.js用来写咱们得方法然…

SpringBoot SSM vue在线作业考试系统

SpringBoot SSM vue在线作业考试系统 首页 图片轮播 作业信息 通知公告 登录注册 留言板 个人中心 我的收藏 后台管理 登录注册 个人中心 教师信息管理 学生信息管理 学院信息管理 专业信息管理 班级信息管理 作业信息管理 作业提交管理 通知公告管理 试卷管理 试题管理 系统…

关于LLC知识14

1、LLC必须工作在感性区 2、为了降低LLC进入容性区后MOS管的电流应力&#xff0c;必须要选择快管&#xff0c;对体二极管的反向恢复参数有要求&#xff1a;trr<200ns 3、对于上下管的死区时间不能太短&#xff0c;否则电容无法充放电完成&#xff0c;就无法实现ZVS导通 如…

Nginx简单的安全性配置

文章目录 引言I Nginx简单的安全性配置禁止特定的HTTP方法限制URL长度禁止某些用户代理限制请求速率连接限制禁止访问某些文件类型II 常见的安全规则防御CC攻击User-Agent过滤GET-URL过滤GET-参数过滤POST过滤(sql注入、xss攻击 )引言 Nginx本身并不具备复杂的防火墙规则定制…

LeetCode题集-1- 两数之和

这个题目是什么意思呢&#xff1f;简单来说就是在一个数组中找出两个元素&#xff0c;使其和为我们设定的值&#xff0c;并且每个元素只能用一次。 如下图具体示例&#xff1a; 到这里不知道你是否已经有解题思路了呢&#xff1f; 解法一&#xff1a;双层循环 我第一反应就是…

2024了,Neo4j能显示节点图片吗?

经过一番调研&#xff0c;答案是官方的是不能的.但有一个中文版可以显示网络图片作为节点背景 如通义千问说说&#xff1a; Neo4j 图数据库本身并不直接支持在节点中存储和显示图片。但是&#xff0c;你可以通过几种方式间接实现这一功能&#xff1a;1. 存储图片URL 最简单的…

【数据结构】关于哈希表内部原理,你到底了解多少???(超详解)

前言&#xff1a; &#x1f31f;&#x1f31f;本期讲解关于哈希表的内部实现原理&#xff0c;希望能帮到屏幕前的你。 &#x1f308;上期博客在这里&#xff1a;http://t.csdnimg.cn/7D225 &#x1f308;感兴趣的小伙伴看一看小编主页&#xff1a;GGBondlctrl-CSDN博客 目录 &a…

6.2K star!推荐一款开源混沌工程测试平台:Chaos Mesh

1、Chaos Mesh 介绍 Chaos Mesh是一个开源的混沌工程平台&#xff0c;旨在帮助用户在生产环境中测试、验证和优化其应用程序的可靠性和稳定性。通过引入故障注入和混沌工程原则&#xff0c;Chaos Mesh可以模拟各种故障场景&#xff0c;如网络延迟、节点故障、磁盘故障等&#…

JavaWeb JavaScript ⑧ DOM编程

在光芒万丈之前&#xff0c;我们都要欣然接受眼下的难堪和不易&#xff0c;接受一个人的孤独和无助&#xff0c;认真做好眼前的每一件事&#xff0c;你想要的都会有 —— 24.8.29 一、什么是DOM编程 简单来说&#xff1a;DOM(Document obiect Model)编程就是使用document对象的…

重大内幕!揭秘数据“零丢失”,全靠它

2017年&#xff0c;某运营商设备扩容&#xff0c;误删80万用户数据… 2020年初疫情期间&#xff0c;某电商公司恶意删库事件&#xff0c;导致业务停机3天&#xff0c;公司赔付1.5亿元人民币 “链家程序员删库”事件&#xff0c;恶意删除公司 9TB 数据&#xff0c;造成公司财务…

鸿蒙HarmonyOS开发:如何灵活运用服务卡片提升用户体验

文章目录 一、ArkTS卡片相关模块二、卡片事件能力说明三、卡片事件的主要使用场景3.1、使用router事件跳转到指定UIAbility3.1.1、卡片内按钮跳转到应用的不同页面3.1.2、服务卡片的点击跳转事件 3.2、通过message事件刷新卡片内容3.2.1、在卡片页面调用postCardAction接口触发…

网络安全的历史

如今&#xff0c;网络安全几乎成为各大公司和利益相关者关注的焦点。但在早期&#xff0c;网络安全的概念非常模糊。 直到多年以后&#xff0c;由于网络攻击和危险实体威胁的频繁发生&#xff0c;网络安全的发展才受到重视。这些措施的发展成为了网络安全的演变。 网络安全起…

CentOS全面停服,国产化提速,央国企信创即时通讯/协同门户如何选型?

01. CentOS停服带来安全新风险&#xff0c; 国产操作系统迎来新的发展机遇 2024年6月30日&#xff0c;CentOS 7版本全面停服&#xff0c;于2014年发布的开源类服务器操作系统——CentOS全系列版本生命周期画上了句号。国内大量基于CentOS开发和适配的服务器及平台&#xff0c…

500Kg载重无线遥控履带式无人车技术详解

500Kg载重无线遥控履带式无人车是一种专为复杂环境与多样化任务设计的高科技产品&#xff0c;具备卓越的机动性、承载能力和无线遥控功能。以下是对其技术特点的详细解析&#xff1a; 一、技术特点 履带式驱动系统 地形适应性&#xff1a;采用履带式驱动&#xff0c;能够…

实时图像编辑大革新!Adobe发布TurboEdit:可以通过文本来编辑图像,编辑时间<0.5秒!

今天给大家介绍Adobe研究院新的研究TurboEdit&#xff0c;可以通过文本来编辑图像&#xff0c;通过一句话就能改变图像中的头发颜色、衣服、帽子、围巾等等。而且编辑飞快&#xff0c;<0.5秒。简直是图像编辑的利器。 相关链接 项目&#xff1a;betterze.github.io/TurboE…

问题-解决

1. 在collection中的SetTest4_Student上面 此时我想解决LinkedHashSet的自定义降序身高问题是现在不行了 难道只能在构造器里面完成吗 还是说明只能为int类型 Double.compare(o1.getHeight(),o2.getHeight()) 在这道题中我在使用为什么不通过 Map<String, Integer>…

视频结构化从入门到精通——图像算法类型介绍

视频结构化主要图像算法 1 认识“数组、矩阵和张量” 1.1 什么是维度 在图像算法中&#xff0c;“维度”这个概念非常重要&#xff0c;它描述了数据的结构和形状。在不同的上下文中&#xff0c;维度可能有不同的含义&#xff0c;但总体来说&#xff0c;它们都与数据的排列方式…

【WiFi主要技术学习2】

WiFi协议学习2 WiFi SPEC理解频段信道带宽协商速率安全与加密WiFi主要技术理解BP直接序列扩频(Direct Sequence Spread Spectrum,DSSS)BPSKQPSK正交幅度调制(Quadrature Amplitude Modulation,QAM)互补码键控(Complementary Code Keying,CCK)正交频分复用(Orthogonal…

基于mallat小波变换的图像分解和重建算法matlab仿真,对比不同分解层数图像重建质量

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 (完整程序运行后无水印) 2.算法运行软件版本 matlab2022a 3.部分核心程序 &#xff08;完整版代码包含详细中文注释和操作步骤视频&#xff09…

axios取消请求CancelToken的原理解析及用法示例

文章目录 一、axios的实例与请求流程二、CancelToken 的作用三、CancelToken 的实现原理四、取消请求的流程五、CancelToken用法六、利用拦截器取消请求1、axios请求拦截器2、axios响应拦截器3、利用路由导航守卫取消请求 一、axios的实例与请求流程 下图是axios实例属性的简图…