1.Retrofit介绍

retrofit是在okhttp的基础上进行封装的,网络请求是通过okhttp实现的。

具体使用细节可查看retrofit官网

2.Retrofit注解

Retrofit通过注解的方式,进行网络请求。根据功能分类,注解可以分为:

get请求参数特殊符号JAVA_get请求参数特殊符号JAVA

2.1请求方法类:

编号

名称

1

GET

2

POST

3

PUT

4

DELETE

5

PATCH

6

HEAD

7

OPTIONS

8

HTTP

1.GET、POST、PUT、DELETE、PATCH、HEAD、OPTIONS、HTTP(编号8,可以代替上面7个)
 2.分别对应 HTTP 的请求方法;(最后的HTTP除除外)
 3.接收一个字符串表示接口 path ,与 baseUrl 组成完整的 Url;
 4.可以不指定,结合 @Url 注解使用;
 5.url 中可以使用变量,如 {id} ,并使用 @Path(“id”) 注解为 {id} 提供值;
 对于编号8,HTTP的使用方法如下:
   有 3 个属性:method、path、hasBody
public interface RetrofitService
{
    @HTTP(method = "get", path = "new{id}", hasBody = false)
    Call<ResponseBody> getNew(@Path("id") int id);
}
2.1.1:GET
  • 一个简单的Get请求
public interface RetrofitService
{
    // http://192.168.1.102/api/News
    @GET("News")
    Call<NewsBean> getItem();
}
  • URL中有参数的Get请求
public interface RetrofitService
{
    // http://192.168.1.102/api/News/1
    // http://192.168.1.102/api/News/{资讯id}
    @GET("News/{newsId}")
    Call<NewsBean> getItem(@Path("newsId") String newsId);

    // http://192.168.1.102/api/News/1/类型1
    // http://192.168.1.102/api/News/{资讯id}/{类型}
    @GET("News/{newsId}/{type}")
    Call<NewsBean> getItem(@Path("newsId") String newsId, @Path("type") String type);
}
  • 参数在URL问号之后的Get请求
public interface RetrofitService
{
    // http://192.168.1.102/api/News?newsId=1
    // http://192.168.1.102/api/News?newsId={资讯id}
    @GET("News")
    Call<NewsBean> getItem(@Query("newsId") String newsId);

    // http://192.168.1.102/api/News?newsId=1&type=类型1
    // http://192.168.1.102/api/News?newsId={资讯id}&type={类型}
    @GET("News")
    Call<NewsBean> getItem(@Query("newsId") String newsId, @Query("type") String type);
}
  • 多个参数在URL问号之后,且个数不确定的Get请求
public interface RetrofitService
{
    // http://192.168.1.102/api/News?newsId=1&type=类型1...
    // http://192.168.1.102/api/News?newsId={资讯id}&type={类型}...
    @GET("News")
    Call<NewsBean> getItem(@QueryMap Map<String, String> map);

    // http://192.168.1.102/api/News?newsId=1&type=类型1...
    // http://192.168.1.102/api/News?newsId={资讯id}&type={类型}...
    @GET("News")
    Call<NewsBean> getItem(@Query("newsId") String newsId, @QueryMap Map<String, String> map);
}
2.1.2: POST
  • 需要补全URL,post的数据只有一条reason的Post请求
public interface RetrofitService
{
    // http://192.168.1.102/api/Comments/1
    // http://192.168.1.102/api/Comments/{newsId}
    @FormUrlEncoded
    @POST("Comments/{newsId}")
    Call<Comment> reportComment(@Path("newsId") String commentId, @Field("reason") String reason);
}
  • 需要补全URL,问号后加入access_token,post的数据只有一条reason的Post请求
public interface RetrofitService
{
    // http://192.168.1.102/api/Comments/1?access_token=1234123
    // http://192.168.1.102/api/Comments/{newsId}?access_token={access_token}
    @FormUrlEncoded
    @POST("Comments/{newsId}")
    Call<Comment> reportComment(
            @Path("newsId") String commentId,
            @Query("access_token") String access_token,
            @Field("reason") String reason);
}
  • 需要补全URL,问号后加入access_token,post一个body(对象)的Post请求
public interface RetrofitService
{
    // http://192.168.1.102/api/Comments/1?access_token=1234123
    // http://192.168.1.102/api/Comments/{newsId}?access_token={access_token}
    @POST("Comments/{newsId}")
    Call<Comment> reportComment(
            @Path("newsId") String commentId, 
            @Query("access_token") String access_token, 
            @Body CommentBean bean);
}
2.1.3:DELETE
  • 需要补全URL的Delete请求
public interface RetrofitService
{
    // http://192.168.1.102/api/Comments/1
    // http://192.168.1.102/api/Comments/{commentId}
    @DELETE("Comments/{commentId}")
    Call<ResponseBody> deleteNewsCommentFromAccount(@Path("commentId") String commentId);
}
  • 需要补全URL,问号后加入access_token的Delete请求
public interface RetrofitService
{
    // http://192.168.1.102/api/Comments/1?access_token=1234123
    // http://192.168.1.102/api/Comments/{commentId}?access_token={access_token}
    @DELETE("Comments/{commentId}")
    Call<ResponseBody> deleteNewsCommentFromAccount(@Path("commentId") String commentId, @Query("access_token") String access_token);
}
  • 带有body的Delete请求
public interface RetrofitService
{
    // http://192.168.1.102/api/Comments
    @HTTP(method = "DELETE",path = "Comments",hasBody = true)
    Call<ResponseBody> deleteCommont(@Body CommentBody body);
}
2.1.4:PUT
public interface RetrofitService
{
    // http://192.168.1.102/api/Accounts/1
    // http://192.168.1.102/api/Accounts/{accountId}
    @PUT("Accounts/{accountId}")
    Call<ExtrasBean> updateExtras(@Path("accountId") String accountId, @Query("access_token") String access_token, @Body ExtrasBean bean);
}
2.2标记类:
2.2.1:FormUrlEncoded

FormUrlEncoded:
1.用于修饰Field注解和FieldMap注解
2.使用该注解,表示请求正文将使用表单网址编码。字段应该声明为参数,并用@Field注释或FieldMap注释。使用FormUrlEncoded注解的请求将具”application / x-www-form-urlencoded” MIME类型。字段名称和值将先进行UTF-8进行编码,再根据RFC-3986进行URI编码.

2.2.2:Multipart

Multipart:
1.作用于方法
2.使用该注解,表示请求体是多部分的。 每一部分作为一个参数,且用Part注解声明

上传单个文件

接口

public interface RetrofitService
{
    @Multipart
    @POST("/uploadImgs")
    Call<ResponseServer<List<PicResultData>>> uploadSingleImg(@Part("description") RequestBody description, @Part MultipartBody.Part file);
}

实现

public void uploadImg(Object pcObj, String fileUrl) 
{
    File file = new File(fileUrl);
    // 创建 RequestBody,用于封装构建RequestBody
    // RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), file);
    RequestBody requestFile = RequestBody.create(MediaType.parse("image/jpg"), file);

    // MultipartBody.Part  和后端约定好Key,这里的partName是用file
    MultipartBody.Part body = MultipartBody.Part.createFormData("file", file.getName(), requestFile);

    // 添加描述
    String descriptionString = "hello, 这是文件描述";
    RequestBody description = RequestBody.create(MediaType.parse("multipart/form-data"), descriptionString);

    // 执行请求
    serviceApi.uploadSingleImg(description, body).enqueue(new BaseViewModel.HttpRequestCallback<List<PicResultData>>() 
    {
        @Override
        public void onSuccess(List<PicResultData> result) 
        {
            super.onSuccess(result);
        }

        @Override
        public void onFailure(int status, String message) 
        {
            super.onFailure(status, message);
        }
    });
}
上传多个文件

接口

public interface RetrofitService
{
    @Multipart
    @POST("/uploadImgs")
    Call<ResponseServer<List<PicResultData>>> uploadMultiImgs(@PartMap Map<String, RequestBody> maps);
}

实现

public void uploadImgs(Object pcObj, List<String> imgStrs) 
{
    Map<String, RequestBody> map = new HashMap<>();
    for (String imgUrl : imgStrs) 
    {
        File file = new File(imgUrl);
        // create RequestBody instance from file
        // RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), file);
        RequestBody requestFile = RequestBody.create(MediaType.parse("image/jpg"), file);
        // 注意:file就是与服务器对应的key,后面filename是服务器得到的文件名
        map.put("file\"; filename=\"" + file.getName(), requestFile);
    }

    // 执行请求
    serviceApi.uploadMultiImgs(map).enqueue(new BaseViewModel.HttpRequestCallback<List<PicResultData>>() 
    {
        @Override
        public void onSuccess(List<PicResultData> result) 
        {
            super.onSuccess(result);
        }

        @Override
        public void onFailure(int status, String message) 
        {
            super.onFailure(status, message);
        }
    });
}
2.2.3:Streaming

Streaming:
1.作用于方法
2.处理返回Response的方法的响应体,即没有将body()转换为byte []
3.响应体的数据用流的形式返回
4.未使用该注解,默认会把数据全部载入内存,之后通过流获取数据也是读取内存中数据,所以返回数据较大时,需要使用该注解

public interface RetrofitService
{
    @Streaming
    @GET("/update_apk")
    Call<ResponseServer<News>> downloadFile(@Url String fileUrl);
}
2.3参数类:
2.3.1:Headers

Headers:
1.作用于方法,用于添加一个或多个请求头
2.使用 @Headers 注解设置固定的请求头,所有请求头不会相互覆盖,即使名字相同

定义Headers

public interface RetrofitService
{
    // 添加一个请求体
    @Headers("Cache-Control: max-age=640000")
    @GET("/widget/list")
    Call<List<Widget>> widgetList();

    // 添加多个请求体
    @Headers({"Accept: application/vnd.yourapi.v1.full+json", "User-Agent: Your-App-Name"})
    @GET("/users/{username}")
    Call<User> getUser(@Path("username") String username)
}

使用Headers,需要使用拦截器

private RetrofitHelper()
{
    OkHttpClient.Builder builder = new OkHttpClient.Builder();
    builder.addInterceptor(new Interceptor()
    {
        @Override
        public Response intercept(Chain chain) throws IOException
        {
            Request original = chain.request();

            Request request = original.newBuilder()
                    .header("User-Agent", "Your-App-Name")
                    .header("Accept", "application/vnd.yourapi.v1.full+json")
                    .method(original.method(), original.body())
                    .build();

            return chain.proceed(request);
        }
    });

    OkHttpClient client = builder.build();
    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(BASE_URL_WEATHER)
            .addConverterFactory(GsonConverterFactory.create())
            .client(client)
            .build();
}
2.3.2:Header

Header:
1.作用于方法参数(形参)
2.使用 @Header 注解动态更新请求头,匹配的参数必须提供给 @Header ,若参数值为 null ,这个头会被省略;当传入一个List或array时,为拼接每个非空的item的值到请求头中
3.具有相同名称的请求头不会相互覆盖,而是会照样添加到请求头中

public interface RetrofitService
{
    @GET("/tasks")
    Call<List<Task>> getTasks(@Header("Content-Range") String contentRange);
}
2.3.3:Body

Body:
1.作用于方法参数(形参)
2.使用该注解定义的参数不可为null
3.当发送一个post或put请求,但是又不想作为请求参数或表单的方式发送请求时,使用该注解定义的参数可以直接传入一个实体类,retrofit会通过convert把该实体序列化并将序列化后的结果直接作为请求体发送出去;如果提交的是一个Map,那么作用相当于 @Field

public interface RetrofitService
{
    @GET("/users/new")
    Call<ResponseBody> createUser(@Body RequestBody requestBody);
}
2.3.4:Field

Field:
1.作用于方法参数(形参)
2.发送 Post请求 时提交请求的表单字段,与 @FormUrlEncoded 注解配合使用
3.用String.valueOf()把参数值转换为String,然后进行URL编码,当参数值为null值时,会自动忽略,如果传入的是一个List或array,则为每一个非空的item拼接一个键值对,每一个键值对中的键是相同的,值就是非空item的值,如: name=张三&name=李四&name=王五,另外,如果item的值有空格,在拼接时会自动忽略,例如某个item的值为:张 三,则拼接后为name=张三.

public interface RetrofitService
{
    // 普通参数
    @FormUrlEncoded
    @POST("/users/addphoto")
    Call<List<Repo>> listRepos(@Field("time") long time);

    // 固定或可变数组
    @FormUrlEncoded
    @POST("/users/addphoto")
    Call<List<Repo>> listRepos(@Field("name") String... names);
}
2.3.5:FieldMap

FieldMap:
1.作用于方法参数(形参)
2.表单字段,与 Field、FormUrlEncoded 配合;接受 Map<String, String> 类型,非 String 类型会调用 toString() 方法
3.map中每一项的键和值都不能为空,否则抛出IllegalArgumentException异常

public interface RetrofitService
{
    @FormUrlEncoded
    @POST("/things")
    Call<List<Repo>> things(@FieldMap Map<String, String> params);
}
2.3.6:Part

Part:
1.作用于方法参数(形参)
2.post请求时,提交请求的表单字段,与 @ Multipart注解 配合
3.使用该注解定义的参数,参数值可以为空,为空时,则忽略
4.使用该注解定义的参数类型有以下3种方式可选:
a, 如果类型是okhttp3.MultipartBody.Part,内容将被直接使用。 省略part中的名称,即 @Part MultipartBody.Part part
b, 如果类型是RequestBody,那么该值将直接与其内容类型一起使用。 在注释中提供part名称(例如,@Part(“foo”)RequestBody foo)
c, 其他对象类型将通过使用转换器转换为适当的格式。 在注释中提供part名称(例如,@Part(“foo”)Image photo)

上传单个文件

接口

public interface RetrofitService
{
    @Multipart
    @POST("/uploadImgs")
    Call<ResponseServer<List<PicResultData>>> uploadSingleImg(@Part("description") RequestBody description, @Part MultipartBody.Part file);
}

实现

public void uploadImg(Object pcObj, String fileUrl) 
{
    File file = new File(fileUrl);
    // 创建 RequestBody,用于封装构建RequestBody
    // RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), file);
    RequestBody requestFile = RequestBody.create(MediaType.parse("image/jpg"), file);

    // MultipartBody.Part  和后端约定好Key,这里的partName是用file
    MultipartBody.Part body = MultipartBody.Part.createFormData("file", file.getName(), requestFile);

    // 添加描述
    String descriptionString = "hello, 这是文件描述";
    RequestBody description = RequestBody.create(MediaType.parse("multipart/form-data"), descriptionString);

    // 执行请求
    serviceApi.uploadSingleImg(description, body).enqueue(new BaseViewModel.HttpRequestCallback<List<PicResultData>>() 
    {
        @Override
        public void onSuccess(List<PicResultData> result) 
        {
            super.onSuccess(result);
        }

        @Override
        public void onFailure(int status, String message) 
        {
            super.onFailure(status, message);
        }
    });
}
2.3.7:PartMap

PartMap:
1.作用于方法参数(形参)
2.post请求时,提交请求的表单字段,与 @ Multipart注解 配合
3.map中每一项的键和值都不能为空,否则抛出IllegalArgumentException异常
4.使用该注解定义的参数类型有以下2种方式可选:
a, 如果类型是RequestBody,那么该值将直接与其内容类型一起使用
b, 其他对象类型将通过使用转换器转换为适当的格式。
5.携带的参数类型更加丰富,包括数据流,所以适用于 有文件上传 的场景

上传多个文件

接口

public interface RetrofitService
{
    @Multipart
    @POST("/uploadImgs")
    Call<ResponseServer<List<PicResultData>>> uploadMultiImgs(@PartMap Map<String, RequestBody> maps);
}

实现

public void uploadImgs(Object pcObj, List<String> imgStrs) 
{
    Map<String, RequestBody> map = new HashMap<>();
    for (String imgUrl : imgStrs) 
    {
        File file = new File(imgUrl);
        // create RequestBody instance from file
        // RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), file);
        RequestBody requestFile = RequestBody.create(MediaType.parse("image/jpg"), file);
        // 注意:file就是与服务器对应的key,后面filename是服务器得到的文件名
        map.put("file\"; filename=\"" + file.getName(), requestFile);
    }

    // 执行请求
    serviceApi.uploadMultiImgs(map).enqueue(new BaseViewModel.HttpRequestCallback<List<PicResultData>>() 
    {
        @Override
        public void onSuccess(List<PicResultData> result) 
        {
            super.onSuccess(result);
        }

        @Override
        public void onFailure(int status, String message) 
        {
            super.onFailure(status, message);
        }
    });
}
2.3.8:Path

Path:
1.作用于方法参数(形参)
2.在URL路径段中替换指定的参数值。
3.使用String.valueOf()和URL编码将值转换为字符串。
4.使用该注解定义的参数的值不可为空参数值默认使用URL编码

public interface RetrofitService
{
    @GET("/data/sk/{cityId}.html")
    Call<ResponseBody> getWeatherByCityId(@Path("cityId") String cityId);
}
2.3.9:Query

Query:
1.作用于方法参数(形参)
2.用于添加查询参数,即请求参数(Query = Url 中 ‘?’ 后面的 key-value)
3.参数值通过String.valueOf()转换为String并进行URL编码
4.使用该注解定义的参数,参数值可以为空,为空时,忽略该值,当传入一个List或array时,为每个非空item拼接请求键值对,所有的键是统一的,如: name=张三&name=李四&name=王五.

public interface RetrofitService
{
    // 普通参数
    @GET("/list")
    Call<ResponseBody> list(@Query("catrgory") String catrgory);

    // 传入一个数组
    @GET("/list")
    Call<ResponseBody> list(@Query("catrgory") String... catrgory);

    // 不进行URL编码
    @GET("/search")
    Call<ResponseBody> list(@Query(value="foo", encoded=true) String foo);
}
2.3.10:QueryMap

QueryMap:
1.作用于方法参数(形参)
2.以map的形式添加查询参数,即请求参数
3.参数的键和值都通过String.valueOf()转换为String格式
4.map的键和值默认进行URL编码map中每一项的键和值都不能为空,否则抛出IllegalArgumentException异常

public interface RetrofitService
{
    // 使用默认URL编码
    @GET("/search")
    Call<ResponseBody> list(@QueryMap Map<String, String> filters);

    // 不使用默认URL编码
    @GET("/search")
    Call<ResponseBody> list(@QueryMap(encoded=true) Map<String, String> filters);
}
2.3.11:Url

Url:
1.作用于方法参数(形参)
2.直接传入一个请求的 URL变量 用于URL设置

public interface RetrofitService
{
    @GET
    Call<ResponseBody> urlAndQuery(@Url String url, @Query("showAll") boolean showAll);
}
3.参数注解小结:

1.Map 用来组合复杂的参数;
2.Query、QueryMap 与 Field、FieldMap 功能一样,生成的数据形式一样;
Query、QueryMap 的数据体现在 Url 上;
Field、FieldMap 的数据是请求体;
3.{占位符}和 PATH 尽量只用在URL的 path 部分,url 中的参数使用 Query、QueryMap 代替,保证接口的简洁;
4.Query、Field、Part 支持数组和实现了 Iterable 接口的类型, 如 List、Set等,方便向后台传递数组。

4.注意事项:
1,以上部分注解真正的实现在ParameterHandler类中,每个注解的真正实现都是ParameterHandler类中的一个final类型的内部类,每个内部类都对各个注解的使用要求做了限制,比如参数是否可空,键和值是否可空等.
 2,FormUrlEncoded注解和Multipart注解不能同时使用,否则会抛出methodError(“Only one encoding annotation is allowed.”);可在ServiceMethod类中parseMethodAnnotation()方法中找到不能同时使用的具体原因.
 3,Path注解与Url注解不能同时使用,否则会抛出parameterError(p, “@Path parameters may not be used with @Url.”),可在ServiceMethod类中parseParameterAnnotation()方法中找到不能同时使用的具体代码.其实原因也很好理解: Path注解用于替换url路径中的参数,这就要求在使用path注解时,必须已经存在请求路径,不然没法替换路径中指定的参数啊,而Url注解是在参数中指定的请求路径的,这个时候指定请求路径已经晚了,path注解找不到请求路径,更别提更换请求路径中的参数了.
 4,对于FiledMap,HeaderMap,PartMap,QueryMap这四种作用于方法的注解,其参数类型必须为Map的实例,且key的类型必须为String类型,否则抛出异常(以PartMap注解为例):parameterError(p, “@PartMap keys must be of type String: ” + keyType);
 5,使用Body注解的参数不能使用form 或multi-part编码,即如果为方法使用了FormUrlEncoded或Multipart注解,则方法的参数中不能使用Body注解,否则抛出异常parameterError(p, “@Body parameters cannot be used with form or multi-part encoding.”);