Retrofit2 的简单使用

iMac

Retrofit 是 Square 公司开源的一个高质量高效率的http库,开发者是被称为 Android 之神 的 Jake Wharton。

Retrofit 以其解耦彻底、扩展灵活、使用简单等特性,在 Android 领域声名远播。

Retrofit 已经出来很久了,现在最新版本是 2.3.0 ,如果还没使用过它,就真的是 low 爆了。

这里简单讲解 Retrofit 的使用以及理清楚 Retrofit 的上传。



Retrofit 入门

Retrofit 其实相当简单,简单到源码只有37个文件,其中22个文件是注解还都和HTTP有关,真正暴露给用户的类并不多。Retrofit 必须配合 Okhttp 来使用,并且真正的网络请求是由 Okhttp 完成的,Retrofit 是利用动态代理对数据做了封装后,给到 OKhttp ,最后由Okhttp 完成网络请求后,将数据交给 Retrofit 。


1.创建Retrofit 实例

        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("http://gank.io/api/")
                .build();

创建Retrofit实例时需要通过Retrofit.Builder ,并调用baseUrl方法设置URL,当然,baseUrl 并不是必须的,也可以在后面的接口上设置。

注: Retrofit2 的baseUlr 必须以 /(斜线) 结束,不然会抛出IllegalArgumentException: baseUrl must end in / 的异常


2.定义 API 接口

这里以 「干货集中营」的 API 为例,定义了一个 GET 请求的接口。

public interface API {
    /**
     * 妹纸列表
     */
    @GET("data/福利/{num}/{page}") //里面填写全路径URl,如果设置有baseURL,也可以是相对路径
    Call<ResponseBody> getGirlPic(@Path("num") int num, @Path("page") int page);

    @GET() //也可以不填,然后在参数里面指定全路径的URL
    Call<ResponseBody> getGirlPic(@Url String url);
}

这个interface 内的方法我们是无法直接调用的,我们需要通过Retrofit 创建一个API的代理对象。

  API apiServer = retrofit.create(API.class);

然后,通过这个代理对象来调用方法。


3.调用接口

        API apiServer = retrofit.create(API.class);
        Call<ResponseBody> girlPic = apiServer.getGirlPic(8, 1);
        //调用后,回调方法执行在主线程
        girlPic.enqueue(new Callback<ResponseBody>() {
            @Override
            public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {

            }

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

            }
        });

这样就完成了retrofit 简单的调用了。



Retrofit 注解

想要愉快的使用 Retrofit ,就必须要了解它的22个注解的用途。

为更好理解,可以将这22个注解分为三类。


HTTP 请求类型注解

http请求类型注解

上面 8 个注解都是在方法上使用,除了「 HTTP」 这个注解,其它都对应了HTTP 协议的 7 种请求类型,这个很好理解,只有 HTTP 这个注解有点特殊。

HTTP 注解可以代替以上方法中的任意一个注解,有 3 个属性:methodpathhasBody

下面是用HTTP注解实现:

public interface API {
    /**
     * method 表示请求的方法,区分大小写
     * path表示路径
     * hasBody表示是否有请求体
     */
    @HTTP(method = "GET", path = "data/福利/{num}/{page}", hasBody = false)
    Call<ResponseBody> getGirlPic(@Path("num") int num, @Path("page") int page);
}


标注类注解

标注注解

这 3 个注解也是作用在方法上的,前两个表单请求的注解「FormUrlEncoded」、「Multipart」用于上传,后面会详细了解。

最后一个注解「Streaming」用于文件的下载:

    @GET
    @Streaming
    Call<ResponseBody> download(@Url String url);

「Streaming」注解表示以流的形式返回响应体,避免了因数据过大而造成的内存溢出。



参数类注解

参数注解

上面的注解都有各自的作用,多多练习才能知道每个注解的具体用法。

GET 请求非常简单,网上有很多示例,例如:

@GET("group/users")
Call<ResponseBody> groupList(@Query("sort") String sort);
//Query注解用于添加参数,可添加多个

//如果参数个数不固定,可以使用QueryMap注解用于动态添加参数
@GET("group/users")
Call<ResponseBody> groupList(@QueryMap Map<String, String> options);


复杂的是 POST 请求,许多文章都讲得不够详细完整,这里整理记录一下。

关于 POST 请求上传,可以简单分为 参数上传、文件上传、混合上传



参数上传

参数上传非常常见,在用户登录时,一般都会用到参数上传,将用户名密码上传到服务器 做校验。

这种表单提交需要用到 「FormUrlEncoded」注解。

参数上传可以分为 固定参数上传、动态参数上传、自定义上传


固定参数上传

固定参数是指:接口上传的参数个数固定不变。

@FormUrlEncoded
@POST("/upload")
Call<ResponseBody> uploadParams(
  @Field("username")String username,
  @Field("password")String password);


动态参数上传

动态参数上传是指:接口上传的参数动态改变。

这种情况可以使用 「FieldMap」来添加参数。

@FormUrlEncoded
@POST("/upload")
Call<ResponseBody> uploadParams(@FieldMap Map<String,String> map);


自定义上传

自定义上传是指:直接封装请求体。

@POST("/upload")
Call<ResponseBody> uploadParams(@Body RequestBody body);
//组装请求体
FormBody body=new FormBody.Builder()
                .add("username","admin")
                .add("token","psd=dmzkkshf")
                .build();



文件上传

文件上传也有许多应用场景,比如图片上传。

单文件上传

@Multipart
@POST("upload")
Call<ResponseBody> uploadOneFile(@Part MultipartBody.Part body);

//第一个参数表示文件的 MIME类型,第二个参数是文件
RequestBody fileRQ = RequestBody.create(MediaType.parse("image/*"), file);
//第一个参数表示接口定义的参数名称,第二个参数表示文件的名称,第三个参数是 RequestBody
MultipartBody.Part part = MultipartBody.Part.createFormData("picture", file.getName(), fileRQ);

Call<ResponseBody> uploadCall = downloadService.uploadOneFile(part);

MediaType 指的是要传递的数据的MIME类型。

MediaType对象包含了三种信息:type 、subtype以及charset,一般将这些信息传入parse()方法中,这样就可以解析出MediaType对象,

比如 “text/x-markdown; charset=utf-8” ,type值是text,表示是文本这一大类;/后面的x-markdown是subtype,表示是文本这一大类下的markdown这一小类; charset=utf-8 则表示采用UTF-8编码。

如果不知道某种类型数据的MIME类型,可以参见链接Media TypeMIME 参考手册,较详细的列出了所有数据的MIME类型。

常见的 MIME 类型如下:

  • json : application/json
  • xml : application/xml
  • png : image/png
  • jpg : image/jpeg
  • gif : imge/gif


多文件上传

@PartMap
@Multipart
@POST("upload")
Call<ResponseBody> uploadFiles(@PartMap Map<String, RequestBody> map);


RequestBody fb = RequestBody.create(MediaType.parse("text/plain"), "hello,retrofit");
RequestBody fileTwo = RequestBody.create(MediaType.parse("image/*"), new File(Environment.getExternalStorageDirectory()
                + file.separator + "original.png"));
Map<String, RequestBody> map = new HashMap<>();
//这里的key必须这么写,否则服务端无法识别
map.put("file\"; filename=\""+ file.getName(), fileRQ);
map.put("file\"; filename=\""+ "2.png", fileTwo);

Call<ResponseBody> uploadCall = downloadService.uploadFiles(map);
@Part
@Multipart
@POST("upload")
Call<ResponseBody> uploadFiles(@Part List<MultipartBody.Part> parts);


RequestBody fileRQ = RequestBody.create(MediaType.parse("image/*"), file);
MultipartBody.Part part = MultipartBody.Part.createFormData("picture", file.getName(), fileRQ);

RequestBody fb = RequestBody.create(MediaType.parse("text/plain"), "hello,retrofit");
RequestBody fileTwo = RequestBody.create(MediaType.parse("image/*"), new File(Environment.getExternalStorageDirectory()
                + file.separator + "original.png"));
MultipartBody.Part two=MultipartBody.Part.createFormData("one","one.png",fileTwo);
List<MultipartBody.Part> parts=new ArrayList<>();
parts.add(part);
parts.add(two);

Call<ResponseBody> uploadCall = downloadService.uploadFiles(parts);



文件和参数混合上传

@Multipart
@POST("upload")
Call<ResponseBody> uploadFile(
  @Part("body") RequestBody body, 
  @Part MultipartBody.Part file);


MultipartBody.Part part = MultipartBody.Part.createFormData("picture", 
                                                            file.getName(), 
                                                            fileRQ);
RequestBody fb =RequestBody.create(MediaType.parse("text/plain"), "hello,retrofit");
Call<ResponseBody> uploadCall = downloadService.uploadFile(fb,part);



通用上传方式

 @POST("upload")
 Call<ResponseBody> uploadFile(@Body RequestBody body);


String path = Environment.getExternalStorageDirectory() + File.separator + "1.png";
File file = new File(path);
RequestBody fileRQ = RequestBody.create(MediaType.parse("multipart/form-data"), file);
MultipartBody.Part part = MultipartBody.Part.createFormData("picture", 
                                                            file.getName(), 
                                                            fileRQ);

RequestBody body=new MultipartBody.Builder()
                .addFormDataPart("userName","lange")
                .addFormDataPart("token","dxjdkdjkj9203kdckje0")
                .addFormDataPart("header",file.getName(),fileRQ)
                .build();
Call<ResponseBody> uploadCall = downloadService.uploadFile(body);


注意:

@Part 在标注 RequestBody 时,必须携带参数

在标注MultipartBody.Part 时,绝对不能携带参数,如果弄错了,就会直接报异常。

//错误示例代码
@Multipart
@POST("upload")
Call<ResponseBody> uploadOneFile(@Part RequestBody file);//报错
//@Part 标注 RequestBody 时,需要携带参数 如: @Part("file")

//错误示例代码
@Multipart
@POST("upload")
Call<ResponseBody> uploadOneFile(@Part("file") MultipartBody.Part file);//报错
//@Part 标注 MultipartBody.Part 时,不能携带参数



参考

你真的会用Retrofit2吗?Retrofit2完全教程

这是一份很详细的 Retrofit 2.0 使用教程(含实例讲解)

猜你喜欢

转载自blog.csdn.net/deemons/article/details/78187965