Retrofit2详解

Retrofit2详解Retrofit 框架 它是 Square 公司开发的现在非常流行的网络框架 retrofit2 0 它依赖于 OkHttp 在这里我们也不需要显示的导入 okHttp 在 retrofit 中已经导入 okhttp3 性能好 处理快 使用简单 Retrofit 是安卓上最流行的 HTTPClient 库之一 github 地址使用步骤 1 定义一个接口 封装 URL 地址和数据请求 2 实例化 R

Retrofit介绍

Retrofit框架: 它是Square公司开发的现在非常流行的网络框架

retrofit2.0它依赖于OkHttp,在这里我们也不需要显示的导入okHttp,在retrofit中已经导入okhttp3 性能好,处理快,使用简单,Retrofit 是安卓上最流行的HTTP Client库之一。

准确来说,网络请求的工作本质上是OkHttp完成,而 Retrofit 仅负责网络请求接口的封装。它的一个特点是包含了特别多注解,方便简化你的代码量。并且还支持很多的开源库(著名例子:Retrofit + RxJava)。Retrofit和OkHttp都是square公司写的.

Retrofit官网

github地址

Retrofit的优点

Retrofit依赖

 implementation 'com.squareup.retrofit2:retrofit:2.9.0' //gson转换器 implementation 'com.squareup.retrofit2:converter-gson:2.9.0' //rxjava转换器 implementation 'com.squareup.retrofit2:adapter-rxjava2:2.9.0' //string转换器 implementation 'com.squareup.retrofit2:converter-scalars:2.9.0' 

Retrofit使用步骤

入门案例:get无参异步请求

测试Url:

https://www.wanandroid.com/banner/json

ApiService

public interface ApiService { 
    / * get无参请求 * https://www.wanandroid.com/banner/json */ @GET("banner/json") Call<ResponseBody> getBanner(); } 
public class MainActivity extends AppCompatActivity { 
    public static final String BASE_URL = "https://www.wanandroid.com/"; @Override protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //1.创建Retrofit对象 Retrofit retrofit = new Retrofit.Builder() .baseUrl(BASE_URL) .build(); //2.通过Retrofit实例创建接口服务对象 ApiService apiService = retrofit.create(ApiService.class); //3.接口服务对象调用接口中方法,获得Call对象 Call<ResponseBody> call = apiService.getBanner(); //4.Call对象执行请求(异步、同步请求) //同步请求:不常用,一般使用异步请求 //Response 
   
     execute = call.execute(); 
    //异步请求 call.enqueue(new Callback<ResponseBody>() { 
    @Override public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) { 
    //onResponse方法是运行在主线程也就是UI线程的,所以我们可以在这里直接更新ui if (response.isSuccessful()) { 
    try { 
    String result = response.body().string(); Log.e("xyh", "onResponse: " + result); } catch (IOException e) { 
    e.printStackTrace(); } } } @Override public void onFailure(Call<ResponseBody> call, Throwable t) { 
    //onFailure方法是运行在主线程也就是UI线程的,所以我们可以在这里直接更新ui Toast.makeText(MainActivity.this, t.getMessage(), Toast.LENGTH_SHORT).show(); } }); //call.cancel(); //取消 } } 

请求结果:

2020-10-21 14:58:05.494 9204-9204/com.xyh.retrofit E/xyh: onResponse: { 
   "data":[{ 
   "desc":"享学~","id":29,"imagePath":"https://wanandroid.com/blogimgs/0-1d86-4eb5-8012-b3fd6945cbb1.jpeg","isVisible":1,"order":0,"title":"【Android开发进阶教程】热修复架构方案原理详解与项目实战","type":0,"url":"https://www.bilibili.com/video/BV1f54y1k77e"},{ 
   "desc":"","id":6,"imagePath":"https://www.wanandroid.com/blogimgs/62c1bd68-b5f3-4a3c-a649-7ca8c7dfabe6.png","isVisible":1,"order":1,"title":"我们新增了一个常用导航Tab~","type":1,"url":"https://www.wanandroid.com/navi"},{ 
   "desc":"一起来做个App吧","id":10,"imagePath":"https://www.wanandroid.com/blogimgs/50c115c2-cf6c-4802-aa7b-a4334de444cd.png","isVisible":1,"order":1,"title":"一起来做个App吧","type":1,"url":"https://www.wanandroid.com/blog/show/2"},{ 
   "desc":"","id":20,"imagePath":"https://www.wanandroid.com/blogimgs/90c6cc12-742e-4c9f-b318-b912f163b8d0.png","isVisible":1,"order":2,"title":"flutter 中文社区 ","type":1,"url":"https://flutter.cn/"}],"errorCode":0,"errorMsg":""} 

Retrofit注解

retrofit通过使用注解来简化请求,大体分为以下几类:

  1. 用于标注请求方式的注解
  2. 用于标记请求头的注解
  3. 用于标记请求参数的注解
  4. 用于标记请求和响应格式的注解

请求方法注解:
这里写图片描述
这里写图片描述




这里写图片描述

Retrofit2 的baseUlr 必须以 /(斜线) 结束

创建Retrofit实例时需要通过Retrofit.Builder,并调用baseUrl方法设置URL。

Retrofit2 的baseUlr 必须以 /(斜线) 结束,不然会抛出一个IllegalArgumentException,所以如果你看到别的教程没有以 / 结束,那么多半是直接从Retrofit 1.X 照搬过来的。

有些特殊情况可以不以/结尾,比如 其实这个 URL https://www.baidu.com?key=value用来作为baseUrl其实是可行的,因为这个URL隐含的路径就是 /(斜线,代表根目录) ,而后面的?key=value在拼装请求时会被丢掉所以写上也没用。之所以 Retrofit 2 在文档上要求必须以 /(斜线) 结尾的要求想必是要消除歧义以及简化规则。

get有参请求

 / * get有参请求 * http://qt..com/php_cgi/news/php/varcache_getnews.php?id=12&page=0&plat=android&version=9724 */ @GET("news/php/varcache_getnews.php") Call<ResponseBody> getNewsInfo(@Query("id") String id, @Query("page") String page, @Query("plat") String plat, @Query("version") String version); 
public class MainActivity extends AppCompatActivity { 
    public static final String BASE_URL = "http://qt..com/php_cgi/"; @Override protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Retrofit retrofit = new Retrofit.Builder() .baseUrl(BASE_URL) .build(); retrofit.create(ApiService.class) .getNewsInfo("12", "0", "android", "9724") .enqueue(new Callback<ResponseBody>() { 
    @Override public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) { 
    if (response.isSuccessful()) { 
    try { 
    String string = response.body().string(); Log.e("xyh", "onResponse: " + string); } catch (IOException e) { 
    e.printStackTrace(); } } } @Override public void onFailure(Call<ResponseBody> call, Throwable t) { 
    Log.e("xyh", "onFailure: " + t.getMessage()); } }); } } 

@QueryMap注解

@QueryMap:参数太多时可以用@QueryMap封装参数,相当于多个@Query

 / * @QueryMap:参数太多时可以用@QueryMap封装参数,相当于多个@Query * * http://qt..com/php_cgi/news/php/varcache_getnews.php?id=12&page=0&plat=android&version=9724 */ @GET("news/php/varcache_getnews.php") Call<ResponseBody> getNewsInfo(@QueryMap Map<String, String> map); 
 Map<String, String> map = new HashMap<>(); map.put("id", "12"); map.put("page", "0"); map.put("plat", "android"); map.put("version", "9724"); Call<ResponseBody> call = apiService.getNewsInfo(map); 

gosn转换器:直接返回对象

默认情况下Retrofit只支持将HTTP的响应体转换换为ResponseBody,而Converter是Retrofit为我们提供用于将ResponseBody转换为我们想要的类型。

通过GsonConverterFactory为Retrofit添加Gson支持

implementation 'com.squareup.retrofit2:converter-gson:2.9.0' 
 / * gson转换器 * https://www.wanandroid.com/article/list/0/json?cid=60 */ @GET("article/list/0/json") Call<BaseEntity<ArticleBean>> getArticleData(@Query("cid") int id); 
public class MainActivity extends AppCompatActivity { 
    public static final String BASE_URL = "https://www.wanandroid.com/"; @Override protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //1.创建Retrofit对象 Retrofit retrofit = new Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create()) //gson转换器 .build(); ApiService apiService = retrofit.create(ApiService.class); Call<BaseEntity<ArticleBean>> call = apiService.getArticleData(60); call.enqueue(new Callback<BaseEntity<ArticleBean>>() { 
    @Override public void onResponse(Call<BaseEntity<ArticleBean>> call, Response<BaseEntity<ArticleBean>> response) { 
    BaseEntity<ArticleBean> entity = response.body(); Log.e("xyh", "onResponse: "+entity.getData().toString() ); } @Override public void onFailure(Call<BaseEntity<ArticleBean>> call, Throwable t) { 
    } }); } } 

@Path :用于url中的占位符,所有在网址中的参数(URL的问号前面的参数)

 / * @Path :用于url中的占位符,所有在网址中的参数(URL的问号前面的参数) * https://www.wanandroid.com/article/list/0/json?cid=60 */ @GET("article/list/{page}/json") Call<BaseEntity<ArticleBean>> getArticleData(@Path("page") int page, @Query("cid") int id); 
 Call<BaseEntity<ArticleBean>> call = apiService.getArticleData(0,60); 

POST请求

表单提交

FormUrlEncoded:表示请求实体是一个Form表单,每个键值对需要使用@Field注解

 / * post请求 * FormUrlEncoded:表示请求实体是一个Form表单,每个键值对需要使用@Field注解 * 该接口是get请求,只是为了演示post请求的用法 * http://qt..com/php_cgi/news/php/varcache_getnews.php?id=12&page=0&plat=android&version=9724 */ @FormUrlEncoded @POST("news/php/varcache_getnews.php") Call<ResponseBody> getGameInfo(@Field("id") String id, @Field("page") String page, @Field("plat") String plat, @Field("version") String version); 

FieldMap

多个参数时可以使用,类型@QueryMap

 @FormUrlEncoded @POST("news/php/varcache_getnews.php") Call<ResponseBody> getGameInfo(@FieldMap Map<String, String> map); 

body注解:上传json数据

方式1:使用RequestBody

 @POST("news/php/varcache_getnews.php") Call<ResponseBody> getNewsInfoByBody(@Body RequestBody Body); 
 String json = ""; RequestBody body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), json); retrofit.create(ApiService.class) .getNewsInfoByBody(body) .enqueue(new Callback<ResponseBody>() { 
    @Override public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) { 
    } @Override public void onFailure(Call<ResponseBody> call, Throwable t) { 
    } }); 

方式2:

直接传入实体,它会自行转化成Json,这个转化方式是GsonConverterFactory定义的。

 / * 直接传入实体,它会自行转化成Json,这个转化方式是GsonConverterFactory定义的。 * @param bean * @return */ @POST("news/php/varcache_getnews.php") Call<ResponseBody> getNewsInfoByBody(@Body ParmasBean bean); 
 Retrofit retrofit = new Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create()) //gson转换器 .build(); ApiService apiService = retrofit.create(ApiService.class); ParmasBean parmasBean=new ParmasBean(); Call<ResponseBody> call = apiService.getNewsInfoByBody(parmasBean); 

方式3:使用Map集合

上传json格式的数据,也可以使用Map集合,加上body注解。

 @POST("news/php/varcache_getnews.php") Call<ResponseBody> getNewsInfoByBody(@Body Map<String, Object> map); 
 Map<String, Object> parmas = new HashMap<>(); parmas.put("alipay_account", "xx"); parmas.put("real_name", "xx"); Call<ResponseBody> call = apiService.getNewsInfoByBody(parmas); 

@Url:指定请求路径

若需要重新定义接口地址,可以使用@Url,将地址以参数的形式传入即可。

 / * 若需要重新定义接口地址,可以使用@Url,将地址以参数的形式传入即可。 * * @param url * @param map * @return */ @GET Call<List<Activity>> getActivityList(@Url String url, @QueryMap Map<String, String> map); 

@Headers注解

添加请求头,用于添加固定请求头,可以同时添加多个。通过该注解添加的请求头不会相互覆盖,而是共同存在。

 / * 使用@Headers添加多个请求头 * 用于添加固定请求头,可以同时添加多个。通过该注解添加的请求头不会相互覆盖,而是共同存在 * * @param url * @param map * @return */ @Headers({ 
    "User-Agent:android", "apikey:", "Content-Type:application/json", }) @POST() Call<BaseEntity<NewsInfo>> post(@Url String url, @QueryMap Map<String, String> map); 

@Header注解

作为方法的参数传入,用于添加不固定值的Header,该注解会更新已有的请求头。

 / * @Header注解: * 作为方法的参数传入,用于添加不固定值的Header,该注解会更新已有的请求头 * * @param token * @param activeId * @return */ @GET("mobile/active") Call<BaseEntity<NewsInfo>> get(@Header("token") String token, @Query("id") int activeId); 

@HTTP注解

 / * 11.@HTTP注解: * method 表示请求的方法,区分大小写 * path表示路径 * hasBody表示是否有请求体 */ @HTTP(method = "GET", path = "blog/{id}", hasBody = false) Call<ResponseBody> getBlog(@Path("id") int id); 

Streaming注解

表示响应体的数据用流的方式返回,适用于返回的数据比较大,该注解在在下载大文件的特别有用。

 / * 12.Streaming注解:表示响应体的数据用流的方式返回,适用于返回的数据比较大,该注解在在下载大文件的特别有用 */ @Streaming @GET Call<BaseEntity<NewsInfo>> downloadPicture(@Url String fileUrl); 

上传单张图片

 ///上传单张图片// / * Multipart:表示请求实体是一个支持文件上传的Form表单,需要配合使用@Part,适用于 有文件 上传的场景 * Part:用于表单字段,Part和PartMap与Multipart注解结合使用,适合文件上传的情况 * PartMap:用于表单字段,默认接受的类型是Map 
   
     ,可用于实现多文件上传 * Part 后面支持三种类型,{@link RequestBody}、{@link okhttp3.MultipartBody.Part} 、任意类型; * * @param file 服务器指定的上传图片的key值 * @return */ 
    @Multipart @POST("upload/upload") Call<BaseEntity<NewsInfo>> upload1(@Part("file" + "\";filename=\"" + "test.png") RequestBody file); @Multipart @POST("xxxxx") Call<BaseEntity<NewsInfo>> upload2(@Part MultipartBody.Part file); private void upLoadImage1() { 
    Retrofit retrofit = new Retrofit.Builder() .baseUrl("") .addConverterFactory(GsonConverterFactory.create()) //gson转换器 .build(); File file = new File(""); RequestBody requestBody = RequestBody.create(MediaType.parse("image/png"), file); retrofit.create(ApiService.class) .upload1(requestBody) .enqueue(new Callback<BaseEntity<NewsInfo>>() { 
    @Override public void onResponse(Call<BaseEntity<NewsInfo>> call, Response<BaseEntity<NewsInfo>> response) { 
    } @Override public void onFailure(Call<BaseEntity<NewsInfo>> call, Throwable t) { 
    } }); } private void upLoadImage2() { 
    Retrofit retrofit = new Retrofit.Builder() .baseUrl("") .addConverterFactory(GsonConverterFactory.create()) //gson转换器 .build(); File file = new File(""); RequestBody photoRequestBody = RequestBody.create(MediaType.parse("image/png"), file); MultipartBody.Part photo = MultipartBody.Part.createFormData("上传的key", file.getName(), photoRequestBody); retrofit.create(ApiService.class) .upload2(photo) .enqueue(new Callback<BaseEntity<NewsInfo>>() { 
    @Override public void onResponse(Call<BaseEntity<NewsInfo>> call, Response<BaseEntity<NewsInfo>> response) { 
    } @Override public void onFailure(Call<BaseEntity<NewsInfo>> call, Throwable t) { 
    } }); } 

上传多张图片

 //上传多张图片/ / * @param map * @return */ @Multipart @POST("upload/upload") Call<BaseEntity<NewsInfo>> upload3(@PartMap Map<String, RequestBody> map); @Multipart @POST("upload/upload") Call<BaseEntity<NewsInfo>> upload4(@PartMap Map<String, MultipartBody.Part> map); private void upLoadImage3() { 
    Retrofit retrofit = new Retrofit.Builder() .baseUrl("") .addConverterFactory(GsonConverterFactory.create()) //gson转换器 .build(); //图片集合 List<File> files = new ArrayList<>(); Map<String, RequestBody> map = new HashMap<>(); for (int i = 0; i < files.size(); i++) { 
    RequestBody requestBody = RequestBody.create(MediaType.parse("image/png"), files.get(i)); map.put("file" + i + "\";filename=\"" + files.get(i).getName(), requestBody); } retrofit.create(ApiService.class) .upload3(map) .enqueue(new Callback<BaseEntity<NewsInfo>>() { 
    @Override public void onResponse(Call<BaseEntity<NewsInfo>> call, Response<BaseEntity<NewsInfo>> response) { 
    } @Override public void onFailure(Call<BaseEntity<NewsInfo>> call, Throwable t) { 
    } }); } private void upLoadImage4() { 
    Retrofit retrofit = new Retrofit.Builder() .baseUrl("") .addConverterFactory(GsonConverterFactory.create()) //gson转换器 .build(); Map<String, MultipartBody.Part> map = new HashMap<>(); File file1 = new File(""); RequestBody photoRequestBody = RequestBody.create(MediaType.parse("image/png"), file1); MultipartBody.Part photo1 = MultipartBody.Part.createFormData("上传的key1", file1.getName(), photoRequestBody); map.put("上传的key1", photo1); File file2 = new File(""); RequestBody photoRequestBody2 = RequestBody.create(MediaType.parse("image/png"), file2); MultipartBody.Part photo2 = MultipartBody.Part.createFormData("上传的key2", file2.getName(), photoRequestBody2); map.put("上传的key2", photo2); retrofit.create(ApiService.class) .upload4(map) .enqueue(new Callback<BaseEntity<NewsInfo>>() { 
    @Override public void onResponse(Call<BaseEntity<NewsInfo>> call, Response<BaseEntity<NewsInfo>> response) { 
    } @Override public void onFailure(Call<BaseEntity<NewsInfo>> call, Throwable t) { 
    } }); } 

图文混传

 //图文混传/ / * @param params * @param files * @return */ @Multipart @POST("upload/upload") Call<BaseEntity<NewsInfo>> upload5(@FieldMap() Map<String, String> params, @PartMap() Map<String, RequestBody> files); / * Part 后面支持三种类型,{@link RequestBody}、{@link okhttp3.MultipartBody.Part} 、任意类型; * * @param userName * @param passWord * @param file * @return */ @Multipart @POST("xxxxx") Call<BaseEntity<NewsInfo>> upload6(@Part("username") RequestBody userName, @Part("password") RequestBody passWord, @Part MultipartBody.Part file); private void upload6() { 
    Retrofit retrofit = new Retrofit.Builder() .baseUrl("") .addConverterFactory(GsonConverterFactory.create()) //gson转换器 .build(); //RequestBody startTowerId = RequestBody.create(MediaType.parse("multipart/form-data"), "xx"); MediaType textType = MediaType.parse("text/plain"); RequestBody name = RequestBody.create(textType, "二傻子"); RequestBody pass = RequestBody.create(textType, ""); File file = new File(""); RequestBody photoRequestBody = RequestBody.create(MediaType.parse("image/png"), file); MultipartBody.Part photo = MultipartBody.Part.createFormData("上传的key", file.getName(), photoRequestBody); //multipart/form-data : 需要在表单中进行文件上传时,就需要使用该格式 // RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), file); // MultipartBody.Part body = MultipartBody.Part.createFormData("image", file.getName(), requestFile); // // String descriptionString = "hello, 这是文件描述"; // RequestBody description = RequestBody.create(MediaType.parse("multipart/form-data"), descriptionString); retrofit.create(ApiService.class) .upload6(name, pass, photo) .enqueue(new Callback<BaseEntity<NewsInfo>>() { 
    @Override public void onResponse(Call<BaseEntity<NewsInfo>> call, Response<BaseEntity<NewsInfo>> response) { 
    } @Override public void onFailure(Call<BaseEntity<NewsInfo>> call, Throwable t) { 
    } }); } 

和rxjava结合使用

 implementation "io.reactivex.rxjava2:rxjava:2.1.1" implementation 'io.reactivex.rxjava2:rxandroid:2.0.2' 
 / * https://www.wanandroid.com/article/list/0/json?cid=60 */ @GET("article/list/{page}/json") Observable<BaseEntity<ArticleBean>> getArticleData(@Path("page") int page, @Query("cid") int id); 
public class MainActivity extends AppCompatActivity { 
    public static final String BASE_URL = "https://www.wanandroid.com/"; @Override protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Retrofit retrofit = new Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create()) //gson转换器 .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) //RxJava .build(); ApiService apiService = retrofit.create(ApiService.class); apiService .getArticleData(0, 60) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer<BaseEntity<ArticleBean>>() { 
    @Override public void onSubscribe(@NonNull Disposable d) { 
    } @Override public void onNext(@NonNull BaseEntity<ArticleBean> articleBeanBaseEntity) { 
    Log.e("xyh", "onNext: " + articleBeanBaseEntity.getData().toString()); } @Override public void onError(@NonNull Throwable e) { 
    } @Override public void onComplete() { 
    } }); } } 

Retrofit请求直接返回string

在默认情况下Retrofit只支持将HTTP的响应体转换换为ResponseBody,这也是为什么我在前面的例子接口的返回值都是 Call,但如果响应体只是支持转换为ResponseBody的话何必要引入泛型呢,返回值直接用一个Call就行了嘛,既然支持泛型,那说明泛型参数可以是其它类型的,而Converter就是Retrofit为我们提供用于将ResponseBody转换为我们想要的类型。

string转换器:

implementation 'com.squareup.retrofit2:converter-scalars:2.9.0' 
 / * 不加转化器类型必须是ResponseBody * https://www.wanandroid.com/article/list/0/json?cid=60 */ @GET("article/list/{page}/json") Call<ResponseBody> getArticleData1(@Path("page") int page, @Query("cid") int id); / * gson转换器:GsonConverterFactory.create() * https://www.wanandroid.com/article/list/0/json?cid=60 */ @GET("article/list/{page}/json") Call<BaseEntity<ArticleBean>> getArticleData2(@Path("page") int page, @Query("cid") int id); / * 字符串转换器:ScalarsConverterFactory.create() * https://www.wanandroid.com/article/list/0/json?cid=60 */ @GET("article/list/{page}/json") Call<String> getArticleData3(@Path("page") int page, @Query("cid") int id); 
public class MainActivity extends AppCompatActivity { 
    public static final String BASE_URL = "https://www.wanandroid.com/"; @Override protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Retrofit retrofit = new Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(ScalarsConverterFactory.create()) //字符串转换器,这个要写在gson转换器前面,不然不起作用 .addConverterFactory(GsonConverterFactory.create()) //gson转换器 .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) //RxJava .build(); ApiService apiService = retrofit.create(ApiService.class); apiService.getArticleData1(0, 60).enqueue(new Callback<ResponseBody>() { 
    @Override public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) { 
    try { 
    Log.e("xyh", "onResponse1: " + response.body().string()); } catch (IOException e) { 
    e.printStackTrace(); } } @Override public void onFailure(Call<ResponseBody> call, Throwable t) { 
    Log.e("xyh", "onFailure1: "+t.getMessage() ); } }); apiService.getArticleData2(0,60).enqueue(new Callback<BaseEntity<ArticleBean>>() { 
    @Override public void onResponse(Call<BaseEntity<ArticleBean>> call, Response<BaseEntity<ArticleBean>> response) { 
    Log.e("xyh", "onResponse2: " + response.body().getData().toString()); } @Override public void onFailure(Call<BaseEntity<ArticleBean>> call, Throwable t) { 
    Log.e("xyh", "onFailure2: "+t.getMessage() ); } }); apiService.getArticleData3(0, 60).enqueue(new Callback<String>() { 
    @Override public void onResponse(Call<String> call, Response<String> response) { 
    Log.e("xyh", "onResponse3: " + response.body()); } @Override public void onFailure(Call<String> call, Throwable t) { 
    Log.e("xyh", "onFailure3: "+t.getMessage() ); } }); } } 

自定义一个转换器,把请求到的数据转换成字符串

 Retrofit retrofit = new Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(new ToStringConverterFactory()) //字符串转换器,这个要写在gson转换器前面,不然不起作用 .addConverterFactory(GsonConverterFactory.create()) //gson转换器 .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) //RxJava .build(); 
 / * 定义一个转换器,把请求到的数据转换成字符串 */ private class ToStringConverterFactory extends Converter.Factory { 
    private final MediaType MEDIA_TYPE = MediaType.parse("text/plain"); @Override public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) { 
    if (String.class.equals(type)) { 
    return new Converter<ResponseBody, String>() { 
    @Override public String convert(ResponseBody value) throws IOException { 
    return value.string(); } }; } return null; } @Override public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) { 
    if (String.class.equals(type)) { 
    return new Converter<String, RequestBody>() { 
    @Override public RequestBody convert(String value) throws IOException { 
    return RequestBody.create(MEDIA_TYPE, value); } }; } return null; } } 

Retrofit数据转换器(Converter)

在默认情况下Retrofit只支持将HTTP的响应体转换换为ResponseBody, 这也是什么我在前面的例子接口的返回值都是 Call, 但如果响应体只是支持转换为ResponseBody的话何必要引用泛型呢, 返回值直接用一个Call就行了嘛,既然支持泛型,那说明泛型参数可以是其它类型的, 而Converter就是Retrofit为我们提供用于将ResponseBody转换为我们想要的类型.

转换器就是把服务器返回的json格式数据,多做了一步处理,转换成你希望的类型.

转换器可以被添加到支持其他类型。提供了方便适应流行的串行化库, Retrofit 提供了六兄弟模块如下: Gson: com.squareup.retrofit:converter-gson Jackson: com.squareup.retrofit:converter-jackson Moshi: com.squareup.retrofit:converter-moshi Protobuf: com.squareup.retrofit:converter-protobuf Wire: com.squareup.retrofit:converter-wire Simple XML: com.squareup.retrofit:converter-simplexml 

Retrofit封装

/ * Created by : xiaoyehai * description :Retrofit的封装 */ public class RetrofitManager { 
    private static RetrofitManager mInstance; private final Retrofit mRetrofit; private RetrofitManager() { 
    mRetrofit = new Retrofit.Builder() .baseUrl(ConstantUrls.BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .client(getOkhttpClient()) .build(); } public static RetrofitManager getInstance() { 
    if (mInstance == null) { 
    synchronized (RetrofitManager.class) { 
    if (mInstance == null) { 
    mInstance = new RetrofitManager(); } } } return mInstance; } private OkHttpClient getOkhttpClient() { 
    OkHttpClient.Builder builder = new OkHttpClient.Builder(); if (BuildConfig.DEBUG) { 
    builder.addInterceptor(getHttpLoggingInterceptor()); //日志拦截器 } return builder .addInterceptor(getInterceptor()) //通用拦截器 .connectTimeout(20, TimeUnit.SECONDS) //设置连接超时时间 .readTimeout(20, TimeUnit.SECONDS) //设置读取超时时间 .retryOnConnectionFailure(true) .build(); } / * 日志拦截器 * * @return */ private Interceptor getHttpLoggingInterceptor() { 
    HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(); interceptor.setLevel(HttpLoggingInterceptor.Level.BODY); return interceptor; } / * 通用拦截器 * 根据自己项目的需求添加公共的请求头和请求参数 * * @return */ private Interceptor getInterceptor() { 
    Interceptor interceptor = new Interceptor() { 
    @Override public Response intercept(Chain chain) throws IOException { 
    Request request = chain.request() .newBuilder() .addHeader("token", "xxx") .addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8") .addHeader("Accept-Encoding", "gzip, deflate") .addHeader("Connection", "keep-alive") .addHeader("Accept", "*/*") .addHeader("Cookie", "add cookies here") .build(); return chain.proceed(request); } }; return interceptor; } public <T> T create(Class<T> clazz) { 
    return mRetrofit.create(clazz); } } 

使用retrofit做为网络请求时,解决多个BaseURL切换的问题

项目中使用Retrofit进行请求时,后台接口的域名有多个:

 public static final String BASE_URL_APP = "https://app.tjinzhu.com/"; public static final String BASE_URL_H5 = "https://res.tjinzhu.com/"; public static final String BASE_URL_MAT = "https://mat.tjinzhu.com/"; public static final String BASE_URL_LOGIN = "https://login.tjinzhu.com/"; public static final String BASE_URL_HOME_MGI = "https://mgi.sitezt.cn/"; public static final String BASE_URL_HOME_MGAPP = "https://mgapp.sitezt.cn/"; 

在service代码中添加@Headers():

//默认baseurl @GET("api/tjz/v1/tao/assets") Observable<BaseResp<UserAssets>> getUserAssets(@Header("token") String token); @Headers({ 
   "baseurl:mat"}) @GET("api/tjzadmin/v1/appver/new") Observable<BaseResp<AppVersionResp>> getAppVersionInfo(); @Headers({ 
   "baseurl:homeapp"}) @GET("api/info/item/getdetailpics") Observable<List<GoodsDetailPicInfo>> getDetailpics(@Query("itemId") String itemId); 

添加okhttpclient拦截器,捕获添加的Headers,然后修改baseURL

/ * Cerated by xiaoyehai * Create date : 2020/4/3 11:17 * description : okhttpclient拦截器,捕获添加的Headers,然后修改baseURL */ public class BaseUrlInterceptor implements Interceptor { 
    @Override public Response intercept(Chain chain) throws IOException { 
    //获取request Request request = chain.request(); //从request中获取原有的HttpUrl实例oldHttpUrl HttpUrl oldHttpUrl = request.url(); //获取request的创建者builder Request.Builder builder = request.newBuilder(); //从request中获取headers,通过给定的键url_name List<String> headerValues = request.headers("baseurl"); if (headerValues != null && headerValues.size() > 0) { 
    //如果有这个header,先将配置的header删除,因此header仅用作app和okhttp之间使用 builder.removeHeader("baseurl"); //匹配获得新的BaseUrl String headerValue = headerValues.get(0); HttpUrl newBaseUrl = null; if ("game".equals(headerValue)) { 
    newBaseUrl = HttpUrl.parse(ConstantUrls.BASE_URL_LOGIN); } else if ("homeapp".equals(headerValue)) { 
    newBaseUrl = HttpUrl.parse(ConstantUrls.BASE_URL_HOME_MGAPP); } else if ("homeagi".equals(headerValue)) { 
    newBaseUrl = HttpUrl.parse(ConstantUrls.BASE_URL_HOME_MGI); } else if ("mat".equals(headerValue)) { 
    newBaseUrl = HttpUrl.parse(ConstantUrls.BASE_URL_MAT); } else { 
    newBaseUrl = oldHttpUrl; } //重建新的HttpUrl,修改需要修改的url部分 HttpUrl newFullUrl = oldHttpUrl .newBuilder() .scheme("https")//更换网络协议 .host(newBaseUrl.host())//更换主机名 .port(newBaseUrl.port())//更换端口 //.removePathSegment(0)//移除第一个参数(根据baseurl移除相关参数) //.removePathSegment(1)//移除第二个参数(根据baseurl移除相关参数) //.removePathSegment(2)//移除第三个参数(根据baseurl移除相关参数) .build(); //重建这个request,通过builder.url(newFullUrl).build(); // 然后返回一个response至此结束修改 Log.e("xyh1", "intercept: " + newFullUrl.toString()); return chain.proceed(builder.url(newFullUrl).build()); } return chain.proceed(request); } } 

在okhttpclient中设置

 private OkHttpClient getOkhttpClient() { 
    OkHttpClient.Builder builder = new OkHttpClient.Builder(); if (BuildConfig.DEBUG) { 
    builder.addInterceptor(getHttpLoggingInterceptor()); //日志拦截器 } return builder // .addInterceptor(getInterceptor()) //通用拦截器 .addInterceptor(new BaseUrlInterceptor()) //多个baseurl动态切换 .connectTimeout(20, TimeUnit.SECONDS) //设置连接超时时间 .readTimeout(20, TimeUnit.SECONDS) //设置读取超时时间 .retryOnConnectionFailure(true) .build(); } 

使用okhttp拦截器添加公共的参数和请求头

下面是我项目中的一个案例,给大家一个参考。

public class PublicHeaderInterceptor implements Interceptor { 
    private static final String TAG = "PublicHeaderInterceptor"; private static final String TIMES_TAMP = "timestamp"; private static final String TOUCH_ID = "touchid"; private static final String DEVICE = "device"; private static final String VERSION = "version"; private static final String TOKEN = "token"; private static final String SIGN = "sign"; private static final String KEY = "key"; private static final String KEY_VALUE = "base64:qC93ZPHeTNxh2SwB/DeOSb0zUwhHWHWHiU61ZDAPvdnOjkOYE="; private Gson gson; private String imeiMd5; private String androidId; public PublicHeaderInterceptor() { 
    gson = createGsonObj(); } @Override public Response intercept(Chain chain) throws IOException { 
    Request request = chain.request(); // 单位秒 10位 long timeMillis = DateUtils.getSystemTimestamp(); long time = timeMillis / 1000L; String device = "android"; String touchId = UTDevice.getUtdid(Utils.getContext()); String token = StringUtils.null2Length0(UserManager.getDefault().getLoginToken()); HttpUrl url = request.url(); // Log.e(TAG, "intercept: " + url); String sign = ""; Request.Builder builder = request.newBuilder(); // 获取参数 Map<String, String> params = new HashMap<>(10); // 获取 url 参数 List<String> urlKeys = new ArrayList<>(url.queryParameterNames()); String urlKey = ""; for (int i = 0; i < urlKeys.size(); i++) { 
    urlKey = urlKeys.get(i); params.put(urlKey, StringUtils.null2Length0(url.queryParameter(urlKey))); } // 获取 Body 参数 String bodyString = "{}"; if (request.body() != null) { 
    final Buffer buffer = new Buffer(); request.body().writeTo(buffer); bodyString = buffer.readUtf8(); } KLog.d(String.format("bodyString=%s", bodyString)); if (isJsonObject(bodyString) || isJsonArray(bodyString)) { 
    TreeMap<String, String> map = gson.fromJson(bodyString, new TypeToken<TreeMap<String, String>>() { 
    }.getType()); if (map != null) { 
    for (String key : map.keySet()) { 
    params.put(key, map.get(key)); } } } // 传入指定参数 params.put(PublicHeaderInterceptor.TIMES_TAMP, String.valueOf(time)); if (!TextUtils.isEmpty(touchId)) { 
    params.put(PublicHeaderInterceptor.TOUCH_ID, touchId); } params.put(PublicHeaderInterceptor.DEVICE, device); if (!TextUtils.isEmpty(token)) { 
    params.put(PublicHeaderInterceptor.TOKEN, token); } params.put(PublicHeaderInterceptor.KEY, KEY_VALUE); StringBuilder signStringBuilder = new StringBuilder(); List<String> keys = new ArrayList<>(params.keySet()); // 移除SIGN key keys.remove(SIGN); // 排序 Collections.sort(keys); // 排序后 拼接 for (int i = 0; i < keys.size(); i++) { 
    String key = keys.get(i); if (i != 0) { 
    signStringBuilder.append("&"); } String valueObj = params.get(key); String value = ""; if (valueObj != null) { 
    value = valueObj; } signStringBuilder.append(String.format("%s=%s", key, value)); } String signBefore = signStringBuilder.toString().toLowerCase(); Log.e(TAG, "signBefore: " + signBefore); // MD5加密 sign = MD5Util.md5(signBefore.getBytes()); // 在URL末尾追加签名与时间 String newUrl = url.toString(); if (!newUrl.contains("?")) { 
    newUrl = String.format("%s?sign=%s×tamp=%s", newUrl, sign, time); } else { 
    newUrl = String.format("%s&sign=%s×tamp=%s", newUrl, sign, time); } builder.url(newUrl); KLog.d("request", String.format("newUrl=%s \n md5_str=%s timeMillis=%s timeDiffForLocalAndService=%s", newUrl, signBefore, timeMillis, Constants.getTimeDiffForLocalAndService())); // --start 将验签参数加入header if (!TextUtils.isEmpty(token)) { 
    builder.addHeader(TOKEN, token); } builder.addHeader(PublicHeaderInterceptor.TOUCH_ID, touchId); builder.addHeader(PublicHeaderInterceptor.DEVICE, device); builder.addHeader(PublicHeaderInterceptor.VERSION, BuildConfig.VERSION_NAME); // --end // builder.addHeader("platform", "android"); builder.addHeader("v_code", BuildConfig.VERSION_CODE + ""); builder.addHeader("channel", ConfigManager.getDefault().getAppChannel()); // samsung SM-G6200 8.1.0 builder.addHeader("systemVersion", String.format("%s|%s|%s", Build.BRAND, Build.MODEL, Build.VERSION.RELEASE)); if (TextUtils.isEmpty(imeiMd5)) { 
    String imei = AppUtils.getImei(Utils.getContext()); if (!TextUtils.isEmpty(imei)) { 
    imeiMd5 = MD5Util.md5(imei.getBytes()); } } if (!TextUtils.isEmpty(imeiMd5)) { 
    builder.addHeader("imei", imeiMd5); } if (TextUtils.isEmpty(androidId)) { 
    androidId = AppUtils.getAndroidId(Utils.getContext()); } builder.addHeader("androidId", androidId); Request req = builder.build(); Log.e(TAG, "url: " + req.url()); //Log.e(TAG, "intercept3: " + req.toString()); Headers headers = req.headers(); Iterator<Pair<String, String>> iterator = headers.iterator(); while (iterator.hasNext()) { 
    Pair<String, String> pair = iterator.next(); Log.e(TAG, "headers: " + pair.component1() + "==" + pair.component2()); } //请求信息 return chain.proceed(builder.build()); } private boolean isJsonObject(String content) { 
    return !StringUtils.isEmpty(content) && (content.trim().startsWith("{") && content.trim().endsWith("}")); } private boolean isJsonArray(String content) { 
    return !StringUtils.isEmpty(content) && (content.trim().startsWith("[") && content.trim().endsWith("]")); } / * Gson 自动将 int 转为 double 问题解决 */ private Gson createGsonObj() { 
    return new GsonBuilder() .registerTypeAdapter( new TypeToken<TreeMap<String, String>>() { 
    }.getType(), new JsonDeserializer<TreeMap<String, String>>() { 
    @Override public TreeMap<String, String> deserialize( JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { 
    TreeMap<String, String> treeMap = new TreeMap<>(); JsonObject jsonObject = json.getAsJsonObject(); Set<Map.Entry<String, JsonElement>> entrySet = jsonObject.entrySet(); for (Map.Entry<String, JsonElement> entry : entrySet) { 
    if (entry.getValue().isJsonArray()) { 
    treeMap.put(entry.getKey(), Utils.getGson().toJson(entry.getValue())); } else { 
    treeMap.put(entry.getKey(), entry.getValue().getAsString()); } } return treeMap; } }).create(); } } 
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/222300.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月17日 下午4:06
下一篇 2026年3月17日 下午4:06


相关推荐

  • 用 PyTorch 从零创建 CIFAR-10 的图像分类器神经网络,并将测试准确率达到 85%

    用 PyTorch 从零创建 CIFAR-10 的图像分类器神经网络,并将测试准确率达到 85%一般 深度学习的教材或者是视频 作者都会通过 MNIST 这个数据集 讲解深度学习的效果 但这个数据集太小了 而且是单色图片 随便弄些模型就可以取得比较好的结果 但如果我们不满足于此 想要训练一个神经网络来对彩色图像进行分类 可以不可以呢 当然可以的 但是没有想象的容易 我最开始亲自设置神经网络去训练时 训练准确度还不到 30 并且不能收敛 后来逐步运用自己对于深度学习的理解去不断调整网络的

    2026年3月18日
    2
  • Allure趋势图本地显示

    Allure趋势图本地显示Allure趋势图本地显示众所周知,allure趋势图在本地运行的时候,总是显示的空白,但与Jenkins集成后,生成的报告却显示了整个趋势如果不与Jenkins集成就真的没办法展示趋势图吗?答案是NO,没有趋势图我们就自己写????一、首先看下Jenkins集成allure展示的趋势图是什么样子的展示了每次运行的结果对应构建的次数点击可以跳转到对应的构建结果报告整体趋势一目了然二、研究Jenkins生成的allure报告有什么规律1、打开家目录中的.jenkins/jobs/te

    2022年7月26日
    37
  • 保姆级教程:基于LangChain+通义千问+Faiss手写RAG系统,代码全开源

    保姆级教程:基于LangChain+通义千问+Faiss手写RAG系统,代码全开源

    2026年3月14日
    1
  • 构建算法模型_模型与算法有什么不同

    构建算法模型_模型与算法有什么不同文章目录1.情感词典内容2.情感倾向点互信息算法(SO-PMI)算法点互信息算法PMI情感倾向点互信息算法SO-PMI3.构建情感词典1.导入项目2.构建情感种子词3.使用TF-IDF方便构建情感种子词4.构建专业词典的效果与使用方法5.其他说明1.情感词典内容情感词典构建方法归为三类,分别是:词关系扩展法、迭代路径法和释义扩展法情感极性:正向、负向;表示正面情感(开心愉快)或负面情感(伤心愤怒)情感极值:情感倾向(微笑、开怀大笑、手舞足蹈虽然都可以表示开心,但有不一样的

    2022年8月23日
    8
  • 用ThreadLocal来优化下代码吧

    用ThreadLocal来优化下代码吧

    2020年11月19日
    192
  • JSP页面通过URL传递中文参数出现中文乱码问题

    JSP页面通过URL传递中文参数出现中文乱码问题做项目时遇到这个问题问题描述:通过点击这个参数名并进行跳转control层,进行参数接受并处理。${cs.name}里面包含中文&lt;a href="${pageContext.request.contextPath}/recommend_navCategory?name=${cs.name}"&gt;${cs.name}&lt;/a&gt;服务器端:ISO-8859-1是tomca…

    2022年6月13日
    32

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号