Retrofit简介

        Rrotrofit是一个RESTful设计风格的对HTTP框架的封装。Retrofit网络请求本质上是Okhttp完成的,Retrofit仅负责网络请求接口的封装。Retrofit接口层封装了请求参数、Header、Url等信息,后续的请求工作由OkHttp来完成。OkHttp将返回结果交给Retrofit,Retrofit根据用户的需求对结果进行解析。

public interface GetRequest_Interface {
    @GET("openapi.do?keyfrom=Yanzhikai&key=2032414398&type=data&doctype=json")
    Call<Translation>  getCall();
    // @GET注解的作用:采用Get方法发送网络请求
    // getCall() = 接收网络请求结果数据的方法
    // 其中返回类型为Call<*>,*是接收数据的类(即上面定义的Translation类)
    // 如果想直接获得Responsebody中的内容,可以定义网络请求返回值为Call<ResponseBody>
}

Retrofit与其他框架的对比


Android freamwork框架 android restful框架_Android freamwork框架



Retrofit中URL组成


Url由 BaseUrl 和 path组成。BaseUrl在 Rotrofit 实例化时进行设置。path在接口描述中设置。

Url = “http://host:port/aaa/pathname”;
 
BaseUrl = “http://host:port/aaa/”;
 
path = “pathname”;


Retrofit注解类型


1.注解---请求方法



Android freamwork框架 android restful框架_Android freamwork框架_02


2.注解---标记类



Android freamwork框架 android restful框架_retrofit_03


3.注解---请求参数



Android freamwork框架 android restful框架_retrofit_04



Retrofit注解详解


1.请求方法---注解


1.1@HTTP注解的作用


@HTTP注解可以替换@GET、@POST、@PUT、@DELETE、@HEAD注解,及更多功能扩展。


public interface GetRequest_Interface {
    /**
     * method:网络请求的方法(区分大小写)
     * path:网络请求地址路径
     * hasBody:是否有请求体
     */
    @HTTP(method = "GET", path = "blog/{id}", hasBody = false)
    Call<ResponseBody> getCall(@Path("id") int id);
    // {id} 表示是一个变量
    // method的值,retrofit不会做检查处理,所以要自行保证准确
}

2.标记类---注解


2.1@FormUrlEncode


    @FormUrlEncode表示请求体是一个Form表单,每个键值对需要用@Filed来注解,随后的对象需要提供键值对的值。它不能用于Get请求,因为Get请求中没有请求体。


public interface GetRequest_Interface {
    /**
     * @FormUrlEncoded表明是一个表单格式的请求(application/x-www-form-urlencoded)
     * Field("username") 表示将后面的String name 中name的取值作为 username 的值
     */
     @POST("/form")
     @FormUrlEncoded
     Call<ResponseBody> testFormUrlEncoded(
        @Field("username") String name,
        @Field("age") int age
     );
}


// 具体使用
GetRequest_Interface service = retrofit.create(GetRequest_Interface.class);
Call<ResponseBody> call1 = service.testFormUrlEncoded("Carson", 24);

2.2@Multipart


    @Multipart表示请求体是一个文件上传的Form表单,每个键值对需要用@Part来注解,随后的对象需要提供键值对的值。


public interface GetRequest_Interface {
   /**
    * @Part后面支持三种类型:RequestBody、okhttp3.MultipartBody.Part、任意类型;
    * 除了okhttp3.MultipartBody.Part类型以外,其它类型都必须带上表单字段,
    * okhttp3.MultipartBody.Part中已经包含了表单字段的信息。
    */
    @POST("/form")
    @Multipart
    Call<ResponseBody> testFileUpload(
        @Part("name") RequestBody name, 
        @Part("age") RequestBody age, 
        @Part MultipartBody.Part file
    );
}

//具体使用
GetRequest_Interface mService = retrofit.create(GetRequest_Interface.class);
RequestBody name = RequestBody.create(textType, "Carson");
RequestBody age = RequestBody.create(textType, "24");
MultipartBody.Part filePart = MultipartBody.Part.createFormData("file", "test.txt", file);
Call<ResponseBody> call3 = mService.testFileUpload(name, age, filePart);

3.请求参数---注解


    3.1 @Headers


    @Headers 用于在描述接口中静态添加请求头


public interface GetRequest_Interface {
    @Headers(
        "Authorization: authorization”,
        "Accept: application/vnd.yourapi.v1.full+json",
        "User-Agent: Your-App-Name"
    )
    @GET("user")
    Call<User> getUser()
}

3.2 @Header


@Header 用于动态添加请求头,及需要添加的请求头,会作为参数传入


public interface GetRequest_Interface {
    @GET("user")
    Call<User> getUser(
        @Header("Authorization") String authorization
    )
}

3.3 @Body


    @Body用于非表单请求体,并以Post方式提交自定义的数据类型。但是如果提交的是一个Map集合,那么@Body的作用就相当于@Field,不过此时的map需要被FormBody.Builder处理成符合Okhttp的表单。


public interface GetRequest_Interface {
    @FormUrlEncoded
    @POST("book/reviews")
    Call<String> addReviews(@Body Reviews reviews);
}

public class Reviews {
    public String book;
    public String title;
    public String content;
    public String rating;
}

//具体使用
Reviews reviews = new Reviews();
reviews.setBook(“百科全书”);
reviews.setTitle(“标题”);
reviews.setContent(“描述内容”);
reviews.setRating(“hello!”);
Call<ResponseBody> call = service.addReviews(reviews);

//Map处理过程
FormBody.Builder builder = new FormBody.Builder();
builder.add("key","value");

3.4 @Field & @FieldMap


    @Field和FieldMap与标记类@FormUrlEncode配合使用 ,作为发送Post请求时提交请求参数的表单字段。


public interface GetRequest_Interface {
    /**
     * 表明是一个表单格式的请求(Content-Type:application/x-www-form-urlencoded)
     * Field("username")表示将后面的String name中name的取值作为 username 的值
     */
    @POST("/form")
    @FormUrlEncoded
    Call<ResponseBody> testFormUrlEncoded1(
        @Field("username") String name,
        @Field("age") int age
    );

    /**
     * Map的key作为表单的键
     */
    @POST("/form")
    @FormUrlEncoded
    Call<ResponseBody> testFormUrlEncoded2(
        @FieldMap Map<String, Object> map
    );
}

//具体使用
// @Field
Call<ResponseBody> call1 = service.testFormUrlEncoded1("Carson", 24);
// @FieldMap
// 实现的效果与上面相同,但要传入Map
Map<String, Object> map = new HashMap<>();
map.put("username", "Carson");
map.put("age", 24);
Call<ResponseBody> call2 = service.testFormUrlEncoded2(map);

3.5 @Part & @PartMap


    @Part和@PartMap与标记类@Multipart配合使用 ,作为发送Post请求时提交请求参数的表单字段。适用于文件上传的场景。


public interface GetRequest_Interface {
    /**
     * @Part后面支持三种类型:RequestBody、 okhttp3.MultipartBody.Part、任意类型:
     * 除了okhttp3.MultipartBody.Part以外,其它类型都必须带上表单字段,
     * okhttp3.MultipartBody.Part中已经包含了表单字段的信息。
     */
    @POST("/form")
    @Multipart
    Call<ResponseBody> testFileUpload1(
        @Part("name") RequestBody name, 
        @Part("age") RequestBody age,
        @Part MultipartBody.Part file
    );
    /**
     * PartMap 注解支持一个Map作为参数,支持RequestBody类型,
     * 如果有其它的类型,会被retrofit2.Converter转换,如后面会介绍的,
     * 使用com.google.gson.Gson的retrofit2.converter.gson.GsonRequestBodyConverter
     * 所以MultipartBody.Part就不适用了,所以文件只能用@Part MultipartBody.Part
     */
    @POST("/form")
    @Multipart
    Call<ResponseBody> testFileUpload2(
        @PartMap Map<String, RequestBody> args, 
        @Part MultipartBody.Part file
    );
}

// 具体使用
MediaType textType = MediaType.parse("text/plain");
RequestBody name = RequestBody.create(textType, "Carson");
RequestBody age = RequestBody.create(textType, "24");
RequestBody file = RequestBody
                .create(MediaType.parse("application/octet-stream"), "这里是模拟文件的内容");
MultipartBody.Part filePart = MultipartBody.Part.createFormData("file", "test.txt", file);

// @Part
Call<ResponseBody> call3 = service.testFileUpload1(name, age, filePart);
ResponseBodyPrinter.printResponseBody(call3);

// @PartMap
// 实现和上面同样的效果
Map<String, RequestBody> fileUpload2Args = new HashMap<>();
fileUpload2Args.put("name", name);
fileUpload2Args.put("age", age);
Call<ResponseBody> call4 = service.testFileUpload2(fileUpload2Args, filePart); 
// 单独处理文件
ResponseBodyPrinter.printResponseBody(call4);


3.6 @Query & @QueryMap


    @Query和@QueryMap用于@Get方法的查询参数 (Query = Url中?后面的 key-value)


public interface GetRequest_Interface {
    @GET("book/search")    
    Call<ResponseBody> cate1(
        @Query("username") String username,
        @Query(“age”) int age
    );
   
    @GET("book/search")
    Call<ResponseBody> cate2(
        @QueryMap Map<String, Object> map
    );

    Call<ResponseBody> cate3(
        @Query("list") List<String> strList,
    );
}

//具体使用
// @Field
Call<ResponseBody> call1 = service.cate1("Carson");
// @FieldMap
// 实现的效果与上面相同,但要传入Map
Map<String, Object> map = new HashMap<>();
map.put("username", "Carson");
map.put("age", 24);
Call<ResponseBody> call2 = service.cate2(map);


3.7 @Path


    @Path 用作Url地址的缺省值 。Post、Get、Put、Delete中都可以使用


public interface GetRequest_Interface {
    @GET("users/{user}/repos")
    Call<ResponseBody> getBlog(@Path("user") String user );
    // 访问的API是:https://api.github.com/users/{user}/repos
    // 在发起请求时, {user} 会被替换为方法的第一个参数 user(被@Path注解作用)
}

//具体使用
Call<ResponseBody> call = service.getBlog(“carson”);
//则描述的接口为:“users/carson/repos”。


3.8 @Url


    @Url 用于在网络请求的时候,直接传入一个请求的Url变量,用于Url设置。


public interface GetRequest_Interface {
    @GET
    Call<ResponseBody> testUrlAndQuery(
        @Url String url, 
        @Query("showAll") boolean showAll
    );
    // 当@HTTP、@POST、@GET中设置了path时,@GET传入的URL就可以省略
    // 当GET、POST...HTTP等方法中没有设置Url时,则必须使用 {@link Url}提供
}

//具体使用
Url url = “http://www.baidu.com/abc”
Call<ResponseBody> call = service.testUrlAndQuery(url, true);
//则描述的接口为:“http://www.baidu.com/abc?showAll=true”。


自定义Interceptor拦截器


创建一个自定义的Interceptor拦截器,然后在创建Retrofit实例的地方通过addInterceptor()进行添加,通过它我们可以实现一些拦截的操作,比如下面:我们想要拦截每一次请求,添加一个公共的请求参数。


//自定义拦截器——添加公共参数
public class CustomInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        HttpUrl httpUrl = request.url().newBuilder()
                .addQueryParameter("token", "tokenValue")
                .build();
        request = request.newBuilder().url(httpUrl).build();
        return chain.proceed(request);
    }
}

//自定义拦截器—添加 header
//添加header参数,Request提供了两个方法,一个是header(key, value),
//另一个是.addHeader(key, value),两者的区别是,header()如果有重名的将会覆盖,
//而addHeader()允许相同key值的header存在
public class RequestInterceptor implements 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);
    }
}

//添加拦截器Interceptor
private static OkHttpClient getNewClient(){
  return new OkHttpClient.Builder()
    .addInterceptor(new CustomInterceptor())
    .addInterceptor(new RequestInterceptor())
    .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
    .build();
}


Retrofit官方提供了一个方便查看日志的Interceptor拦截器,方便的控制打印信息的类型。HttpLoggingInterceptor提供4种控制打印信息类型的等级,分别是:NONE、BASIC、HEADERS、BODY。


//引入依赖
implement 'com.squareup.okhttp3:logging-interceptor:3.4.1’
//具体使用
private static OkHttpClient getNewClient(){
    HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
    logging.setLevel(HttpLoggingInterceptor.Level.BODY);
    return new OkHttpClient.Builder()
           .addInterceptor(logging)
           .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
           .build();
}


Retrofit解析器与适配器


1.数据解析器 Converter,Retrofit支持多种解析器


   

Android freamwork框架 android restful框架_Android freamwork框架_05


2.适配器 CallAdapter,Retrofit支持多种网络请求适配器


   

Android freamwork框架 android restful框架_网络_06



Retrofit中的API

Call<ResponseBody> call = servers.lookBookDetail(“参数”);
call.enqueue(new Callback<Translation>() {
    //请求成功时回调
    @Override
    public void onResponse(Call<Translation> call, Response<Translation> response) {
        // 对返回数据进行处理
        response.body().show();
    }
    //请求失败时候的回调
    @Override
    public void onFailure(Call<Translation> call, Throwable throwable) {
         System.out.println("连接失败");
    }
});
1.异步请求
   call.enqueue();
2.同步请求
   call.execute();
3.取消请求
   call.cancel();
4.对数据进行处理
   respose.body().show();

Retrofit的使用


1.在app模块的build.gradle文件中引入Retrofit框架。

implement 'com.squareup.retrofit2:retrofit:2.0.2’
   
implement 'com.squareup.retrofit2:converter-gson:2.0.2'

2.自定义数据接收实体类


3.创建用于描述网络请求的接口


4.创建Retrofit对象


5.创建网络请求接口实例


6.发送网络请求


7.处理返回数据、


//创建Retrofit对象

Retrofit retrofit = new Retrofit.Builder()
   
  .baseUrl(""http://fanyi.youdao.com/"")
   
  .addConverterFactory(ProtoConverterFactory.create()) // 支持Prototocobuff解析
   
  .addConverterFactory(GsonConverterFactory.create()) // 支持Gson解析
   
  .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) // 支持RxJava
   
  .build();

//创建 网络请求接口 的实例


PostRequest_Interface request = retrofit.create(PostRequest_Interface.class);


//对网络请求进行封装


Call<Translation1> call = request.getCall("I love you”);


//发送网络请求(异步)

call.enqueue(new Callback<Translation1>() {
   

    //请求成功时回调

   
    @Override
   
    public void onResponse(Call<Translation1> call, Response<Translation1> response) {
   
        // 步骤7:处理返回的数据结果:输出翻译的内容  
   
        System.out.println(response.body().getTranslateResult().get(0).get(0).getTgt());
   
    }
   

    //请求失败时回调

   
    @Override
   
    public void onFailure(Call<Translation1> call, Throwable throwable) {
   
        System.out.println("请求失败");
   
        System.out.println(throwable.getMessage());
   
    }
   
});



Retrofit实现文件上传


public interface FileUploadService {  
    // 上传单个文件
    @Multipart
    @POST("upload")
    Call<ResponseBody> uploadFile(
            @Part("description") RequestBody description,
            @Part MultipartBody.Part file);

    // 上传多个文件
    @Multipart
    @POST("upload")
    Call<ResponseBody> uploadMultipleFiles(
            @Part("description") RequestBody description,
            @Part MultipartBody.Part file1,
            @Part MultipartBody.Part file2);
}


Retrofit上传文件的工具类

public class retrofitFileUtil{
    public static final String MULTIPART_FORM_DATA = "multipart/form-data”;

    private RequestBody createPartFromString(String descriptionString) {  
        return RequestBody.create(
                 MediaType.parse(MULTIPART_FORM_DATA), descriptionString);
    }

    private MultipartBody.Part prepareFilePart(String partName, Uri fileUri) {  
        File file = FileUtils.getFile(this, fileUri);
        // 为file建立RequestBody实例
        RequestBody rf = RequestBody.create(MediaType.parse(MULTIPART_FORM_DATA), file);
        // MultipartBody.Part借助文件名完成最终的上传
        return MultipartBody.Part.createFormData(partName, file.getName(), rf);
    }
}


Uri file1Uri = ... // 从文件选择器或者摄像头中获取
Uri file2Uri = …

// 创建上传的service实例
FileUploadService service = ServiceGenerator.createService(FileUploadService.class);
// 创建文件的part (photo, video, ...)
MultipartBody.Part body1 = prepareFilePart("video", file1Uri);  
MultipartBody.Part body2 = prepareFilePart("thumbnail", file2Uri);
// 添加其他的part
RequestBody description = createPartFromString("hello, this is description speaking");
// 最后执行异步请求操作
Call<ResponseBody> call = service.uploadMultipleFiles(description, body1, body2);  
call.enqueue(new Callback<ResponseBody>() {  
    @Override
    public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
        Log.v("Upload", "success");
    }

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