当前位置: 首页 > news >正文

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

最好用的网络库:Retrofit

文章目录

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

在这里插入图片描述

  1. 用户网络请求的接口配置繁琐,尤其是需要配置复杂请求body,请求头,参数的时候;

  2. 数据解析过程需要用户手动拿到responsbody进行解析不能复用,

  3. 无法适配自动进行线程的切换

  4. 万一我们的存在嵌套网络请求就会陷入“回调陷阱

Retrofit的基本用法

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

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) {// 错误处理}
}

在这里插入图片描述

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

相关文章:

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

最好用的网络库&#xff1a;Retrofit 文章目录 最好用的网络库&#xff1a;RetrofitRetrofit的基本用法Retrofit的使用逻辑Retrofit的基本操作处理复杂的接口地址类型进阶删除提交header中指定参数 Retrofit构建器的最佳写法Retrofit的使用封装 用户网络请求的接口配置繁琐&…...

SpringBoot自动化配置原理

SpringBoot自动化配置原理 01-SpringBoot2高级-starter依赖管理机制 目的&#xff1a;通过依赖能了解SpringBoot管理了哪些starter 讲解&#xff1a; 通过依赖 spring-boot-dependencies 搜索 starter- 发现非常多的官方starter&#xff0c;并且已经帮助我们管理好了版本。 …...

2024级新生数组字符串专题题解

一、题解&#xff1a; 1.A-[NOIP2005]校门外的树_24级新生数组字符串训练题 (nowcoder.com) 这题常见的解法有两种&#xff1a; 第一种是这道题我们可以直接按照题目意思枚举 #include<bits/stdc.h> #define int long long using namespace std;int road[10010];sig…...

C++学习 虚函数,容器

一、虚函数 虚函数是C中的一种函数&#xff0c;允许子类重写父类中的函数&#xff0c;以便在运行时通过基类指针或引用调用子类的函数实现。虚函数的主要作用是实现多态性&#xff0c;这使得基类指针或引用可以根据实际指向的对象类型调用不同的函数实现。具体用法 虚函数的声…...

MacTalk 测评通义灵码,如何实现“微信表情”小功能?

作者&#xff1a;池建强&#xff0c;墨问西东创始人 前段时间&#xff0c;我写了篇墨问研发团队放弃 GitHub Copilot 的文章&#xff0c;没想到留言区一些读者推荐我们试试通义灵码&#xff0c;说它效果很不错。我呢&#xff0c;一直没腾出时间折腾。 直到月中时&#xff0c;…...

Canvas Confetti - 免费开源的五彩纸屑飞舞特效的 JS 库,多用于在网页上实现欢乐庆祝的场景

今天看科技周刊看到的一个酷炫的动效库&#xff0c;使用简单&#xff0c;视觉效果很好&#xff0c;推荐给大家。 Canvas Confetti 是一个基于 JavaScript 的特效动画库&#xff0c;可以在网页界面上轻松地实现五彩纸屑飞舞的庆祝场景特效。这个特效库封装了几种酷炫的特效&…...

[数据集][目标检测]智慧牧场猪只检测数据集VOC+YOLO格式16245张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;16245 标注数量(xml文件个数)&#xff1a;16245 标注数量(txt文件个数)&#xff1a;16245 标…...

GS-SLAM论文阅读笔记--LoopSplat

介绍 这篇文章看标题是解决GS-SLAM回环检测的&#xff0c;GS-SLAM回环检测之前文章很少&#xff0c;但他对于SLAM又很重要&#xff0c;确实值得阅读一番。而且这些作者的学校又是很厉害的。 文章目录 介绍1.背景介绍2.关键内容2.1 Gaussian Splatting SLAM2.2 Gaussian Splat…...

Mysql数据库表结构迁移PostgreSQL

1、背景&#xff1a; 公司本来用的数据库都是mysql&#xff0c;为了国产化适配兼容pg和dm。dm提供了数据迁移工具&#xff0c;可以直接做数据迁移&#xff0c;生成脚本之后在其他环境执行。但是pg貌似没有工具能直接用。navicat由于版权问题公司也用不了。pgloader使用总是有问…...

店匠科技携手Stripe共谋电商支付新篇章

在全球电商行业蓬勃发展的背景下,支付环节作为交易闭环的核心,其重要性日益凸显。随着消费者对支付体验要求的不断提高,以及跨境电商的迅猛发展,支付市场正经历着前所未有的变革与挑战。在这一充满机遇与竞争的领域,店匠科技(Shoplazza)凭借其创新的嵌入式支付解决方案—— Sho…...

大众(奥迪)汽车继电器编号对照表

数字功能放大器零件编号1化油器进气歧管加热器40a1719063832燃油泵(CE1 MK1 Golf 和 Early Rocco/cabrio K-Jet,无转速限制器)-443906059A 321906059D/E3燃油泵(CE1 MK1 Golf 和 Early Rocco/cabrio K-Jet,无转速限制器)-4439060594安全带警告继电器5早期 MFA 时钟的换档…...

《佛脚闪卡watch》——Apple Watch上的高效学习助手

在快节奏的生活环境中&#xff0c;时间管理成为了学习成功的关键因素之一。**《佛脚闪卡watch》**是一款专为Apple Watch设计的应用程序&#xff0c;旨在帮助学生和自学者更高效地利用碎片时间进行学习。无论是等待公交、散步还是短暂休息&#xff0c;您都可以随时随地进行复习…...

六、桥接模式

桥接模式&#xff08;Bridge Pattern&#xff09;是一种结构型设计模式&#xff0c;旨在将抽象与实现分离&#xff0c;使得两者可以独立变化。通过使用桥接模式&#xff0c;可以避免在多个维度上进行继承&#xff0c;降低代码的复杂度&#xff0c;从而提高系统的可扩展性。 组成…...

Vue eslint 语法检测问题

1. 修改 prettier 配置文件 确保你的项目中有 .prettierrc 配置文件&#xff0c;并在其中添加或修改 endOfLine 设置为 lf&#xff0c;确保统一使用 LF 换行符。 在 .prettierrc 文件中添加&#xff1a; {"endOfLine": "lf" }2. 修改 .editorconfig 文件…...

QT Creater实现国庆节主题项目【0基础完成版】

本文适用对象 想要学习qt creater的小白;想要学习c++制作软件的编程爱好者。可以先下载这篇博客绑定的资源,然后一边操作,一边学习,会更高效~0. 创建初始项目 一步步来操作吧,首先下载qt creter,之前发布过相关资源,大家直接查找下载,或者自行下载。 1. 初始代码 mai…...

Qt 加载 WPS 时提示要登录

项目中Qt加载word时 默认用wps打开word文档 程序一运行老是提示要立即登录 看着很烦 可以按下面的方法去掉这个烦人的东西 在下面的项目中新建字符串enableforceloginforfirstinstalldevice&#xff0c;值为false即可。...

vue3的el-tree的default-checked-keys无法勾选的问题解决

前言:有些树形控件是需要默认勾选的 但是请求后渲染不显示 刷新外部的key值也没有用 看了一下文档 我们使用自带的方法来解决 <el-treenode-key"id":data"state.parentMenuList":default-checked-keys"state.checkIdList":check-on-click-n…...

class 5: vue.js 3 v-model和表单输入

v-model是Vue.js 3中用于实现双向绑定的重要指令&#xff0c;双向绑定就是对于数据的修改会映射回UI组件上&#xff0c;同时对于UI组件上数据的变更也会映射回底层数据当中&#xff0c;v-model会根据控件的类型自动选取正确的方法来更新元素v-model底层实现的原理实际上是v-bin…...

了解一下HTTP 与 HTTPS 的区别

介绍&#xff1a; HTTP是超文本传输协议。规定了客户端&#xff08;通常是浏览器&#xff09;和服务器之间如何传输超文本&#xff0c;也就是包含链接的文本。通常使用TCP【1】/IP协议来传输数据&#xff0c;默认端口为80。 HTTPS是超文本传输安全协议&#xff0c;具有CA证书。…...

Opencv中的直方图(1)计算反向投影直方图函数calcBackProject()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 计算直方图的反向投影。 cv::calcBackProject 函数计算直方图的反向投影。也就是说&#xff0c;类似于 calcHist&#xff0c;在每个位置 (x, y)…...

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…...

AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; 目前2025年06月05日更新到&#xff1a; AI炼丹日志-28 - Aud…...

谷歌浏览器插件

项目中有时候会用到插件 sync-cookie-extension1.0.0&#xff1a;开发环境同步测试 cookie 至 localhost&#xff0c;便于本地请求服务携带 cookie 参考地址&#xff1a;https://juejin.cn/post/7139354571712757767 里面有源码下载下来&#xff0c;加在到扩展即可使用FeHelp…...

ES6从入门到精通:前言

ES6简介 ES6&#xff08;ECMAScript 2015&#xff09;是JavaScript语言的重大更新&#xff0c;引入了许多新特性&#xff0c;包括语法糖、新数据类型、模块化支持等&#xff0c;显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var&#xf…...

DockerHub与私有镜像仓库在容器化中的应用与管理

哈喽&#xff0c;大家好&#xff0c;我是左手python&#xff01; Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库&#xff0c;用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例

使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件&#xff0c;常用于在两个集合之间进行数据转移&#xff0c;如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model&#xff1a;绑定右侧列表的值&…...

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...

反射获取方法和属性

Java反射获取方法 在Java中&#xff0c;反射&#xff08;Reflection&#xff09;是一种强大的机制&#xff0c;允许程序在运行时访问和操作类的内部属性和方法。通过反射&#xff0c;可以动态地创建对象、调用方法、改变属性值&#xff0c;这在很多Java框架中如Spring和Hiberna…...

WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成

厌倦手动写WordPress文章&#xff1f;AI自动生成&#xff0c;效率提升10倍&#xff01; 支持多语言、自动配图、定时发布&#xff0c;让内容创作更轻松&#xff01; AI内容生成 → 不想每天写文章&#xff1f;AI一键生成高质量内容&#xff01;多语言支持 → 跨境电商必备&am…...

(转)什么是DockerCompose?它有什么作用?

一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用&#xff0c;而无需手动一个个创建和运行容器。 Compose文件是一个文本文件&#xff0c;通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...