使用Retrofit 2 请求HTTP

什么是Retrofit

Retrofit是一个基于OkHttp的RESTful HTTP网络请求库,是目前比较流行的HttpClient库之一。它使得开发者可以更方便地与Web服务交互,通过简洁的API接口,实现了将REST API请求转化为Java接口调用的方式。

Retrofit的主要特点如下:

  • 简单易用:Retrofit使用简洁的注解方式定义REST API的接口,支持多种HTTP请求方法(如GET、POST、PUT、DELETE等),并自动将响应结果转换为Java对象。
  • 支持异步请求:Retrofit支持异步网络请求,可以使用RxJava、Kotlin协程等方式实现异步请求,避免在主线程中执行耗时操作。
  • 支持自定义拦截器和转换器:Retrofit可以通过自定义拦截器和转换器实现各种功能,如添加请求头、日志记录、错误处理、JSON解析等。
  • 支持文件上传和下载:Retrofit支持文件上传和下载,可以将文件作为请求体或响应体发送或接收。
  • 支持URL替换和查询参数:Retrofit支持URL替换和查询参数,可以在URL中动态替换参数,或在查询参数中传递参数。
  • 兼容多种序列化库:Retrofit支持多种序列化库,如Gson、Jackson等,可以自动将响应结果转换为Java对象。

Retrofit的使用方式

1、定义一个接口(封装URL地址和数据请求)
2、实例化Retrofit
3、通过Retrofit实例创建接口服务对象
4、接口服务对象调用接口中方法,获得Call对象
5、Call对象执行请求(异步、同步请求)

入门案例

通过GET请求访问https://www.wanandroid.com/banner/json

public interface ApiService {

    /**
     * get无参请求
     * https://www.wanandroid.com/banner/json
     */
    @GET("banner/json")
    Call<ResponseBody> getBanner();
 
}

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

public class MainActivity {

     public static final String BASE_URL = "https://www.wanandroid.com/";

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        //2.实例化Retrofit对象
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(BASE_URL)
                .build();

        //3.通过Retrofit实例创建接口服务对象
        ApiService apiService = retrofit.create(ApiService.class);

        //4.接口服务对象调用接口中方法,获得Call对象
        Call<ResponseBody> call = apiService.getBanner();

        //5.Call对象执行请求(异步、同步请求)

        //同步请求:不常用,一般使用异步请求
        //Response<ResponseBody> 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.info("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(); //取消
    }

Retrofit注解

请求方式注解

在这里插入图片描述

GET有参请求

public interface ApiService {
/**
* get有参请求
* http://qt.qq.com/php_cgi/news/php/varcache?id=12&page=0&plat=android&version=9724
*/
@GET("/news/php/varcache")
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.qq.com/php_cgi";

    Retrofit retrofit = new Retrofit.Builder()
      .baseUrl(BASE_URL)
      .build();

    ApiService apiService = retrofit.create(ApiService.class);
  
    Call<ResponseBody> call = apiService.getNewsInfo("12", "0", "android", "9724");
  
    call.enqueue(new Callback<ResponseBody>() {
        @Override
        public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
          if (response.isSuccessful()) {
            try {
              String string = response.body().string();
            } catch (IOException e) {
              e.printStackTrace();
            }
          }
        }

        @Override
        public void onFailure(Call<ResponseBody> call, Throwable t) {
          Log.e("xyh", "onFailure: " + t.getMessage());
        }
      });
}

@Query 注解:用于Get中指定参数。

@QueryMap注解:如果参数过多的话可以使用@QueryMap封装参数,相当于多个@Query

@GET("news/php/varcache")
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);

POST请求

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

/**
* post请求
* FormUrlEncoded:表示请求实体是一个Form表单,每个键值对需要使用@Field注解
* http://qt.qq.com/php_cgi/news/php/update?id=12&page=0&plat=android&version=9724
*/
@FormUrlEncoded
@POST("news/php/update")
Call<ResponseBody> updateGameInfo(@Field("id") String id,
                               @Field("page") String page,
                               @Field("plat") String plat,
                               @Field("version") String version);

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

@FormUrlEncoded
@POST("news/php/update")
Call<ResponseBody> updateGameInfo(@FieldMap Map<String, String> map);

POST添加Body

@Body注解:设置请求的请求体

方法一:使用RequestBody

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

      }

      @Override
      public void onFailure(Call<ResponseBody> call, Throwable t) {

      }
    });

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

/**
* 直接传入实体,它会自行转化成Json,这个转化方式是GsonConverterFactory定义的。
*/
@POST("news/php/send")
Call<ResponseBody> sendNewsInfoByBody(@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.sendNewsInfoByBody(parmasBean);

方式3:使用Map集合

@POST("news/php/send")
Call<ResponseBody> sendNewsInfoByBody(@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);

HTTP注解

这些注解分别对应Http的8种请求方法,注解中都是接收一个字符串作为请求路径的一部分,有点像@RequestMapping(),HTTP注解则可以代替以上方法中的任意一个注解,有3个属性:method、path、hasBody:

请求头注解

注解 说明
@Headers 用于添加固定请求头,可以同时添加多个,通过该注解添加的请求头不会相互覆盖,而是会共同存在
@Header 作为方法的参数传入,用于添加不固定值的Header,该注解会更新已有的请求头

@Url注解:用于指定请求的路径,若需要重新定义接口地址,可以使用@Url,将地址以参数的形式传入即可。

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

Headers注解

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

Header注解

 /**
 * @Header注解:
 */
@GET("mobile/active")
Call<BaseEntity<NewsInfo>> get(@Header("token") String token, @Query("id") int activeId);

请求参数注解

请求参数注解已经在上面的案例中进行了介绍,这里进行简单的总结:

请求参数注解 说明
@Body 多用于Post请求发送非表达数据,根据转换方式将实例对象转化为对应字符串传递参数,比如使用Post发送Json数据,添加GsonConverterFactory则是将body转化为json字符串进行传递
@Filed 多用于Post方式传递参数,需要结合@FromUrlEncoded使用,即以表单的形式传递参数
@FiledMap 多用于Post请求中的表单字段,需要结合@FromUrlEncoded使用
@Part 用于表单字段,Part和PartMap与@multipart注解结合使用,适合文件上传的情况
@PartMap 用于表单字段,默认接受类型是Map<String,RequestBody>,可用于实现多文件上传
@Path 用于Url中的占位符
@Query 用于Get请求中的参数
@QueryMap 与Query类似,用于不确定表单参数
@Url 指定请求路径

项目代码实践

Get请求

获取人员信息service:

    @Autowired
    private OpenApi openApi;

    public UserMeta getUserFromOpenApi(String name) throws BondingException {
        OpenApiResult<UserMeta> result;
        try {
            result = openApi.getUserFromOpenApi(name).execute().body();
            log.info("getUserFromOpenApi, name: {}, result: {}", name, result);
        } catch (Exception e) {
            log.error("getUserMetaFromOpenApi error", e);
            throw new Exception(RELY_SERVICE_BUSY);
        }
        if (result == null || result.getStatus() != SUCCESS_STATUS) {
            throw new Exception(RELY_SERVICE_BUSY);
        }
      

HTTP接口,通过@Path注解可以给URL动态赋值:

public interface OpenApi {

    /**
     * 获取人员信息
     */
    @GET("/v2/user/user/{username}")
    Call<OpenApiResult<UserMeta>> getUserFromOpenApi(@Path("username") String username);
  
}

根据用户名模糊查询公司员工信息,返回前N条数据service:

public class IHRService {

    @Autowired
    private OpenApi openApi;

    public JSONArray getUserInfoList(String username, int limit) throws BondingException {
        try {
            JsonArray result =
                    openApi.getUserMetaFromOpenApi(username, limit).execute().body();
            JSONArray responseJson = new Gson().fromJson(result, JSONArray.class);
            return responseJson;
        } catch (Exception e) {
            log.error("getUserInfoList error", e);
            throw new Exception(RELY_SERVICE_BUSY);
        }
    }
}

HTTP接口,通过@Query接口传递GET中参数:

public interface OpenApi {

    @GET("/v1/person")
    Call<JsonArray> getUserMetaFromOpenApi(
            @Query("name") String name,
            @Query("limit") int limit);
  
}

批量查询Team任务信息service:

    /**
     * 批量查询任务信息(包含状态)
     */
    public Map<String, String> batchGetTask(List<String> taskIdList, String operator)
            throws BondingException {
        Map<String, String> resultMap = new HashMap<>();
        try {
            String url = "external/task/taskInfos?operator="
                    + operator + "&taskIds=" + String.join("&taskIds=", taskIdList);
            OpenApiResult<List<TaskInfoResult>> result = openApi.batchGetTaskInfo(url).execute().body();
            if (result == null || result.getStatus() != SUCCESS_STATUS) {
                throw new Exception(RELY_SERVICE_BUSY);
            }
            result.getResult().forEach(taskBaseModel -> {
                TaskBaseModel baseModel = taskBaseModel.getTaskBaseModel();
                resultMap.put(baseModel.getTaskId(), baseModel.getStatusPhase());
            });
            return resultMap;
        } catch (Exception e) {
            log.error("batchGetTask error", e);
            throw new Exception(RELY_SERVICE_BUSY);
        }
    }

HTTP接口,通过@Url注解可以直接指定接口路径:

public interface OpenApi {

    /**
     * 批量查询任务信息(包含状态)
     */
    @GET
    Call<OpenApiResult<List<TaskInfoResult>>> batchGetTaskInfo(@Url String url);
  
}

POST请求

HTTP接口,@Header注解用户增加Header,@Body注解用于POST请中的请求体

public interface OpenApi {

    /**
     * 新增某个角色对用户的授权
     */
    @POST("/api/roles/{roleCode}/users")
    Call<OpenApiResult<String>> addUsersForRole(
            @Header("AMC-User-Type") String type,
            @Path("roleCode") String roleCode,
            @Body List<String> authUserList);
  
}

HTTP接口,@Headers注解用于添加固定请求头,可以同时添加多个:

public interface OpenApi {

    /**
     * 批量获取人员信息
     */
    @POST("/openapi/v2/usernames")
    @Headers({"Content-Type: application/json;charset=utf-8"})
    Call<OpenApiResult<List<UserMeta>>> batchGetUserMetaFromOpenApi(@Body List<String> userNameList);
  
}

DELETE请求

HTTP接口,@DELETE注解默认情况下是不能够有Body的,如果想要有Body的DELETE请求,需要通过@HTTP注解的方式:

public interface OpenApi {

    /**
     * 取消某个角色对某用户的授权
     */
    @HTTP(method = "DELETE", path = "/api/roles/{roleCode}/users", hasBody = true)
    Call<OpenApiResult<String>> revokeUsersForRole(
            @Header("type") String type,
            @Path("roleCode") String roleCode,
            @Body List<String> authUserList);
  
}

猜你喜欢

转载自blog.csdn.net/zzu_seu/article/details/129101558
今日推荐