<commit_before># Retrofit的使用

最近将Android项目的网络连接库切换到了Retrofit 2.0,以前用的Volley。
使用Retrofit后接口更加接近RESTful风格。逻辑更加清晰，更加灵活。

##基本使用：

定义接口:
  新建一个Service接口类，在该类中声明要调用的接口。
  例如：
  ```java
    public interface GitHubService {
       @GET("users/{user}/repos")
       Call<List<Repo>> listRepos(@Path("user") String user);
    }
  ```
   @GET 表示该接口是用HTTP GET调用，GET后面的括号里面是该接口的Endpoint（主机后面的地址）。
   @Path 是用来动态替换URL中的字段的{}的部分，用来动态生成URL.
   
   POST方式：
   
   ```java
   @POST("users/new")
   Call<User> createUser(@Body User user);
   ```
   @Body 请求的body，Retrofit会自动用配置的格式转换器将User对象转化为对用的格式（如JSON，XML等）
   
   ```java
   @FormUrlEncoded
   @POST("user/edit")
   Call<User> updateUser(@Field("first_name") String first, @Field("last_name") String last);
   ```
   post提交form形式，@FormUrlEncoded 表示该请求的body是Form-encoded数据。@Field就是form的字段名称。
   
  Map:
  ```java
   @ormUrlEncoded
   @POST("/some/endpoint")
   Call<SomeResponse> someEndpoint(@FieldMap Map<String, String> names);
   ```
   
   多参数表单数据：
   ```java
   @Multipart
   @PUT("user/photo")
   Call<User> updateUser(@Part("photo") RequestBody photo, @Part("description")   RequestBody description);
   ```
   设置请求Header:
  ```java
   @Headers({
    "Accept: application/vnd.github.v3.full+json",
    "User-Agent: Retrofit-Sample-App"
   })
   @GET("users/{username}")
   Call<User> getUser(@Path("username") String username);
    ```
    
   ###接口调用：
   ```java
   Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com")
    .addConverterFactory(GsonConverterFactory.create())
    .build();

   GitHubService service = retrofit.create(GitHubService.class);
   Call<List<Repo>> call = service.listRepos("user");
  ```
   
   创建一个Retrofit对象，创建的时候要指定BaseUrl和数据格式转换器。数据格式转换器用来将请求的对象转换为对用的数据格式。例如配置GsonConverterFactory，则会将请求中的对象转换为JSON字符串，将响应中的JSON字符串转换为对象。
   Retrofit支持的转换器：
   
  > **Gson**: com.squareup.retrofit2:converter-gson
  > **Jackson**: com.squareup.retrofit2:converter-jackson
  > **Moshi**: com.squareup.retrofit2:converter-moshi
  > **Protobuf**: com.squareup.retrofit2:converter-protobuf
  > **Wire**: com.squareup.retrofit2:converter-wire
  > **Simple XML**: com.squareup.retrofit2:converter-simplexml
  > **Scalars (primitives, boxed, and String)**: com.squareup.retrofit2:converter-scalars
   
   你也可以自定义一个转换器，例如：
   ```java
   private static class RawJsonConverterFactory extends Converter.Factory {

        public static RawJsonConverterFactory create() {
            return new RawJsonConverterFactory();
        }

        @Override
        public Converter<?, RequestBody> toRequestBody(Type type, Annotation[] annotations) {
            return super.toRequestBody(type, annotations);
        }

        @Override
        public Converter<ResponseBody, ?> fromResponseBody(Type type, Annotation[] annotations) {
            return new RawJsonResponseBodyConverter();
        }
    }

    private static class RawJsonResponseBodyConverter<String> implements Converter<ResponseBody, String> {

        public RawJsonResponseBodyConverter() {

        }

        @Override
        public String convert(ResponseBody value) throws IOException {

            return (String) value.string();
        }
    }
   ```
  接口调用后返回一个Call<?>对象,call的泛型就是接口数据转换后的对象。Call可以指定该次请求同步还是异步。
  同步：
  ```java
  Response<List<Repo>> response = call.execute();
  ```
  同步调用接口直接返回一个Response对象，Response包含响应的所有数据：响应码（the reponse code），响应消息（the response message），以及读取相应头（headers）和body；Body就是接口返回的最终数据。
  
  异步：
  
  ```java
  call.enqueue(new Callback<List<Repo>>() {
     @Override void onResponse(/* ... */) {
        // ...
     }

     @Override void onFailure(Throwable t) {
        // ...
     }
  });
 ```
 
  异步要传一个接口回调接口，接口包含请求成功和失败的方法回调。
  
  ------------
  ##关于打印Log和设置Timeout
  
  由于请求和响应在Retrofit中直接转换处理了，Retrofit中又没暴漏出来可以打印URL和请求Body的方法，所以打印Log就没那么直接了。解决方案：
  指定HTTPClent,在HTTPClient中使用拦截器打印Log.
  Retrofit默认使用的是OkHttp,当然你也可以使用别的Http Client.
  ```java
  OkHttpClient client = new OkHttpClient();
  client.setConnectTimeout(30, TimeUnit.SECONDS);  //连接超时
  client.setReadTimeout(30,TimeUnit.SECONDS);      //读取数据超时
  client.setWriteTimeout(10,TimeUnit.SECONDS);     //发送数据超时
  //Log拦截器，打印请求和响应的LOG
  HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
  logging.setLevel(HttpLoggingInterceptor.Level.BASIC);
  client.interceptors().add(logging);
  Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com")
    .client(client)  //设置Client
    .build();
  ```
  Log的Level有4个级别：NONE，BASIC，HEADERS，BODY
  NONE：不打印Log
  BASIC: 只打印请求和响应的基本信息
  HEADERS：打印基本信息和头信息
  BODY：打印请求和响应的所有信息
  
  -------------------
  ##多converter
  Retrofit可以配置多个转换器，如可以同时设置JSON和XML的转换器，Retrofit会使用配置的所有的converter对数据进行转换。<commit_msg>update post<commit_after>---
layout:     post
title:      "Retrofit的使用"
subtitle:   ""
date:       2015-03-29 12:00:00
author:     "steven"
catalog:    false
tags:
    - Android
---

最近将Android项目的网络连接库切换到了Retrofit 2.0,以前用的Volley。
使用Retrofit后接口更加接近RESTful风格。逻辑更加清晰，更加灵活。

##基本使用：

定义接口:
  新建一个Service接口类，在该类中声明要调用的接口。
  例如：
  ```java
    public interface GitHubService {
       @GET("users/{user}/repos")
       Call<List<Repo>> listRepos(@Path("user") String user);
    }
  ```
   @GET 表示该接口是用HTTP GET调用，GET后面的括号里面是该接口的Endpoint（主机后面的地址）。
   @Path 是用来动态替换URL中的字段的{}的部分，用来动态生成URL.

   POST方式：

   ```java
   @POST("users/new")
   Call<User> createUser(@Body User user);
   ```
   @Body 请求的body，Retrofit会自动用配置的格式转换器将User对象转化为对用的格式（如JSON，XML等）

   ```java
   @FormUrlEncoded
   @POST("user/edit")
   Call<User> updateUser(@Field("first_name") String first, @Field("last_name") String last);
   ```
   post提交form形式，@FormUrlEncoded 表示该请求的body是Form-encoded数据。@Field就是form的字段名称。

  Map:
  ```java
   @ormUrlEncoded
   @POST("/some/endpoint")
   Call<SomeResponse> someEndpoint(@FieldMap Map<String, String> names);
   ```

   多参数表单数据：
   ```java
   @Multipart
   @PUT("user/photo")
   Call<User> updateUser(@Part("photo") RequestBody photo, @Part("description")   RequestBody description);
   ```
   设置请求Header:
  ```java
   @Headers({
    "Accept: application/vnd.github.v3.full+json",
    "User-Agent: Retrofit-Sample-App"
   })
   @GET("users/{username}")
   Call<User> getUser(@Path("username") String username);
    ```

   ###接口调用：
   ```java
   Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com")
    .addConverterFactory(GsonConverterFactory.create())
    .build();

   GitHubService service = retrofit.create(GitHubService.class);
   Call<List<Repo>> call = service.listRepos("user");
  ```

   创建一个Retrofit对象，创建的时候要指定BaseUrl和数据格式转换器。数据格式转换器用来将请求的对象转换为对用的数据格式。例如配置GsonConverterFactory，则会将请求中的对象转换为JSON字符串，将响应中的JSON字符串转换为对象。
   Retrofit支持的转换器：

  > **Gson**: com.squareup.retrofit2:converter-gson
  > **Jackson**: com.squareup.retrofit2:converter-jackson
  > **Moshi**: com.squareup.retrofit2:converter-moshi
  > **Protobuf**: com.squareup.retrofit2:converter-protobuf
  > **Wire**: com.squareup.retrofit2:converter-wire
  > **Simple XML**: com.squareup.retrofit2:converter-simplexml
  > **Scalars (primitives, boxed, and String)**: com.squareup.retrofit2:converter-scalars

   你也可以自定义一个转换器，例如：
   ```java
   private static class RawJsonConverterFactory extends Converter.Factory {

        public static RawJsonConverterFactory create() {
            return new RawJsonConverterFactory();
        }

        @Override
        public Converter<?, RequestBody> toRequestBody(Type type, Annotation[] annotations) {
            return super.toRequestBody(type, annotations);
        }

        @Override
        public Converter<ResponseBody, ?> fromResponseBody(Type type, Annotation[] annotations) {
            return new RawJsonResponseBodyConverter();
        }
    }

    private static class RawJsonResponseBodyConverter<String> implements Converter<ResponseBody, String> {

        public RawJsonResponseBodyConverter() {

        }

        @Override
        public String convert(ResponseBody value) throws IOException {

            return (String) value.string();
        }
    }
   ```
  接口调用后返回一个Call<?>对象,call的泛型就是接口数据转换后的对象。Call可以指定该次请求同步还是异步。
  同步：
  ```java
  Response<List<Repo>> response = call.execute();
  ```
  同步调用接口直接返回一个Response对象，Response包含响应的所有数据：响应码（the reponse code），响应消息（the response message），以及读取相应头（headers）和body；Body就是接口返回的最终数据。

  异步：

  ```java
  call.enqueue(new Callback<List<Repo>>() {
     @Override void onResponse(/* ... */) {
        // ...
     }

     @Override void onFailure(Throwable t) {
        // ...
     }
  });
 ```

  异步要传一个接口回调接口，接口包含请求成功和失败的方法回调。

  ------------
  ##关于打印Log和设置Timeout

  由于请求和响应在Retrofit中直接转换处理了，Retrofit中又没暴漏出来可以打印URL和请求Body的方法，所以打印Log就没那么直接了。解决方案：
  指定HTTPClent,在HTTPClient中使用拦截器打印Log.
  Retrofit默认使用的是OkHttp,当然你也可以使用别的Http Client.
  ```java
  OkHttpClient client = new OkHttpClient();
  client.setConnectTimeout(30, TimeUnit.SECONDS);  //连接超时
  client.setReadTimeout(30,TimeUnit.SECONDS);      //读取数据超时
  client.setWriteTimeout(10,TimeUnit.SECONDS);     //发送数据超时
  //Log拦截器，打印请求和响应的LOG
  HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
  logging.setLevel(HttpLoggingInterceptor.Level.BASIC);
  client.interceptors().add(logging);
  Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com")
    .client(client)  //设置Client
    .build();
  ```
  Log的Level有4个级别：NONE，BASIC，HEADERS，BODY
  NONE：不打印Log
  BASIC: 只打印请求和响应的基本信息
  HEADERS：打印基本信息和头信息
  BODY：打印请求和响应的所有信息

  -------------------
  ##多converter
  Retrofit可以配置多个转换器，如可以同时设置JSON和XML的转换器，Retrofit会使用配置的所有的converter对数据进行转换。
{'fraction_non_alphanumeric': 0.1348373557187828, 'fraction_numerical': 0.005036726128016789, 'mean_word_length': 3.83316430020284, 'pattern_counts': {'":': 0, '<': 41, '<?xml version=': 0, '>': 55, 'https://': 4, 'lorem ipsum': 0, 'www.': 0, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2633691', 'n_tokens_mistral': 3959, 'n_tokens_neox': 4134, 'n_words': 473}