Retrofit2.0使用文档

参考资料:

http://blog.csdn.net/u014165119/article/details/49280779
https://github.com/square/okhttp/tree/master/samples  OkHttp的Sample
http://blog.csdn.net/ljd2038/article/details/51189334   文件下载案例

系列文章资料:

这是我对Retorfit2.0源码的一些解读,有问题希望大家指正
Retrofit2.0中的Builder模式
Retrofit2.0中的动态代理模式
Retrofit2.0中的适配器模式
Retrofit2.0中的策略模式

一、为什么使用Retrofit2.0

(1).Retrofit2.0是由Square公司开发的一个网络请求框架,已经成为Android开发中非常火热的一个库。是Square公司对OkHttp进行再次封装的框架。

Paste_Image.png

Google在大概Android4.3(具体不太清楚了)的时候已经不推荐使用HttpClient ,改为用推荐使用OkHttp,在6.0的时候更是删除了HttpClient,Retrofit作为Square公司推出的对Okhttp再次封装得库,无疑对OkHttp支持的更好。对比现在主流的网络框架Volly,Volly针对不同的返回类型,需要调用不同的方法,传入参数也非常麻烦,而Retrofit在调用一个接口时,就像是在调用一个本地方法。同时Retrofit采用链式编程的方式,让代码的逻辑结构更加清晰。Retrofit非常灵活,可以做到动态的去配置Url。同时它非常适合RESTFUL API的形式。

示例服务端代码:

@Controller
@EnableWebMvc
@RequestMapping(headers="Accept=*/*", 
 produces="application/json")

public class LoginController {

    @Resource
    LoginService service;// 自動依賴注入
    @Resource
    LoginEntity userEntity;
    @Resource
    ResultEntity resultEntity;
    @RequestMapping(value = "login", method = RequestMethod.GET,produces = "application/json",headers="Accept=*/*")
    @ResponseBody
    public 
 ResultEntity login(HttpServletRequest
 request,HttpServletResponse response) {
        //拿到服务器端的参数
        String username = request.getParameter("username");
        String password = request.getParameter("password");

        if (service.isExitsPeople(username, password)==1) {
            userEntity.setUsername(username);
            userEntity.setPassword(password);
            resultEntity.setIsSuccess(true);
            resultEntity.setMessage("登录成功");
            resultEntity.setResult(userEntity);
            return  resultEntity;
        }else{
            resultEntity.setIsSuccess(false);
            resultEntity.setMessage("登录失败");
            resultEntity.setResult("");
            return resultEntity;
        }    
       //System.out.println("测试login方法是否成功" + service.getUserCount(username, password));
       // System.out.println("测试MyBatis使用是否成功" + service.isExitsPeople(username, password));
    }
}

示例Retrofit api的代码

示例客户端API代码:
public interface LoginService {
@FormUrlEncoded
@POST("Member/Login")
    Call<UserModel>
loginApp(@Field("username") String name, @Field("password")
String pwd               
}

(2).它的请求速度非常快
图片来自:
http://blog.csdn.net/xiongge358/article/details/50990864

Paste_Image.png

示例代码:

public
 class RetrofitClient {
    private Retrofit client;
    private String BaseUrl;
    /**
     * 初始化Retrofit的内容
     */
    public RetrofitClient(String BaseUrl) {
        this.BaseUrl = BaseUrl;
        client = new Retrofit.Builder()     
 .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
 .addConverterFactory(GsonConverterFactory.create(
 .baseUrl(BaseUrl)
 .build();
    }
    public SmileAPI getSmileAPI() {
        return client.create(SmileAPI.class);
    }
    public WeatherAPI getWeatherAPI() {
        return client.create(WeatherAPI.class);
    }
}

二、配置Retrofit2.0

在Moudel的gradle中配置

 compile'com.squareup.retrofit2:retrofit:2.1.0'
 compile 'com.squareup.retrofit2:converter-gson:2.1.0'

同样Retrofit也可以对OkHttp进行自己的定制,所以如果需要定制OkHttp同样可以引入Okhttp的库

 compile 'com.squareup.okhttp3:okhttp:3.3.1'

三、使用Retrofit2.0

1.配置Retrofit

***baseUrl:****

  1. 默认的服务器地址,Retrofit在进行请求一个接口时会根据你填写的 baseurl+方法名 去请求。

****addConverterFactory:****

添加返回数据的解析方式,Retrofit支持多种格式的解析,xml,json,jackson,Moshi,Protobuf,wire等,添加对这些数据格式的支持同样需要在Gradle中进行依赖。这些依赖Square公司同样提供了支持。
这里是Square提供的官方Converter modules列表。选择一个最满足你需求的。

Gson: com.squareup.retrofit:converter-gson
Jackson: com.squareup.retrofit:converter-jackson
Moshi: com.squareup.retrofit:converter-moshi
Protobuf: com.squareup.retrofit:converter-protobuf
Wire: com.squareup.retrofit:converter-wire
Simple XML: com.squareup.retrofit:converter-simplexml

****CallAdapter:****

Retrofit会将网络请求的接口以回调的方式返回,我们通常会得到一个叫做Call<类型>的结果,如果我们需要返回其他的结果,例如RxJava形式的结果,需要对RxJava做支持。如果不需要特殊类型的返回结果,我们是不需要配置的。

****Client:****

通过该方法我们可以添加自己定制的OkHttp。
定制OkHttp

HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder()
        .addInterceptor(interceptor)
        .retryOnConnectionFailure(true)
        .connectTimeout(15, TimeUnit.SECONDS)
        .addNetworkInterceptor(mTokenInterceptor)
        .build();
//将定制的OkHttp加入到Retrofit中
Retrofit retrofit = new Retrofit.Builder()
        .baseUrl(AppConfig.BASE_URL)
        .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
        .addConverterFactory(GsonConverterFactory.create(gson))
        .client(client)
        .build();
```
***Create:***
如果一切都配置好了,那么通过调用Create,并传入你的接口方法类型,就可以请求服务端了。
使用Retrofit

####一、  定义请求的接口
Retrofit非常适合Restful API,它采用注解,加动态代理的方式去请求一个服务。
1.     **定义你要请求的接口**

因为Retrofit采用动态代理的方式去请求一个接口,所以在定义我们需要请求的接口时,我们需要定义一个接口,并添加相应的注解,告诉Retrofit如何请求我们需要的接口。参数的返回类型默认情况下需要通过Call方法进行包装。

```
public interface LoginService {
@FormUrlEncoded
@POST("Member/Login")
  Call<UserModel>loginApp(@Field("username") String name, @Field("password")String pwd  ,@Field("grid") String grid);
}
```
 

####二、   Retrofit的注解

**方法上的注解**

1.请求的类型
```
@POST
@GET
参数:请求的方法,对应接口文档的方法名。
```
2.添加静态的头部协议
```
@Headers
例如:@Headers("Cache-Control: max-age=640000")
```
3.数据提交的方式
```
@FormUrlEncoded
添加该注解后,将会发送form-encode的数据类型
```
4.数据提交的方式
```
@Multipart
添加该注解后,将会发送Mulitipar类型的数据。
```
**参数注解**

```
1.@Part:如果你在方法注解上加入了@Multipart,那么对应的方法参数必须要用该注解
```
 
```
2.@Field:如果你在方法注解上加入了@FormUrlEncoded注解,那么对应的方法参数必须用该注解。
```
 
```
3.   @Body:如果传递的是一个序列化的参数
例如:@POST("users/new")
Call<User> createUser(@Body User user);
```
 

**Retrofit请求的Url同样是可以动态配置的。请求的URL可以在函数中使用替换块和参数进行动态更新,替换块是{ and }包围的字母数字组成的字符串。
**

```
3.@Path :Url中有一段是需要动态配置的时候
例如:
@GET("group/{id}/users")
List<User>
groupList(@Path("id") int groupId);

``` 
 
```
4.@Query 查询参数
例如:
@GET("group/{id}/users")
```

```
List<User> groupList(@Path("id") int groupId,
@Query("sort") String sort);
```
 

5.复杂的参数同样可以通过Map方式

```
例如:
@GET("group/{id}/users")
List<User>
groupList(@Path("id") int groupId, @QueryMap Map<String,
String> options);

 ```

 

####四、处理Call返回的参数

1. 如果我们定义好了接口。那么这个时候就可以调用create方法,并且传入我们需要请求的接口类型,拿到返回值了。
使用Call:

例子:
```
 MyService service =retrofit.create(MyService.class);
 Call<ResultModel<UserInfoModel>>call = service.loginApp("","");
 Service.loginApp:就是执行我们定义的方法,并且拿到返回值。
``` 

2. 接着我们使用Call:
```
call.enqueue(new Callback<Repo>() {
   @Override
public void onResponse(Response<UserInfoModel response) {
       // 通过reponse拿到结果
      UserModelInfo info=response.body();
    }  
     @Override
     public void onFailure(Throwable t) {  
   }
});
```
 

####五、Retrofit实现文件上传(方式1)

1.使用@Mulitpart注解标记接口 
2.@Part              注解标记文件类型的参数
3.MultipartBody.Part   标记文件的参数

 ```
public interface FileUploadService {  
 @Multipart
 @POST("upload")
 Call<ResponseBody> upload(@Part("description") RequestBody description,
                          @Part MultipartBody.Part file);
}
 ```

4.使用RequestBody包装上传文件

```
//构建要上传的文件
File file = new File(filename);
RequestBody requestFile =RequestBody.create(MediaType.parse("multipart/form-data"),file);
```
***注:MediaType.parse 参数代表上传文件的类型(Content-Type)的类型,例如:如果是上传的是图片,可以写成MediaType.parse("image/jpg")***


5.使用MultipartBody.Part对Request再次包装

```
//构建要上传的文件
参数1:对应的接口参数
参数2:上传的文件名称
参数3:包装的RequestBody

MultipartBody.Part body = MultipartBody.Part.createFormData("aFile", file.getName(),
requestFile);
```
 

 

6.调用上传方法:
```
Call<ResponseBody> call = service.upload(description,body);
```
####六、Retrofit实现文件上传(方式2)

1.通过定义自己的FileConvertFactory,我们可以将File类型,不通过包装,作为参数传入。
```
public interface FileUploadService {     
@Multipart   
@POST("upload")                    //注意这里的参数 "aFile" 之前是在创建MultipartBody.Part 的时候传入的
Call<ResponseBody> upload(@Part("description") RequestBody description,       
@Part("aFile") File file);
}
```
 
####7.使用ConvertFactory进行上传

继承Converter.Factory :
```
static class FileRequestBodyConverterFactory extends Converter.Factory {
@Override
public Converter<File, RequestBody> requestBodyConverter(Type type, Annotation[]
parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {   
return new FileRequestBodyConverter();
  }
}
static class FileRequestBodyConverter implements Converter<File, RequestBody> {
 @Override
  public RequestBody convert(File file) throws IOException {   
return RequestBody.create(MediaType.parse("application/otcet-stream"),file); 
 }
}
```
2.在配置Retrofit时加入我们自己的文件类型支持

```
.addConverterFactory(new
FileRequestBodyConverterFactory())
```

####七、文件下载

使用Okhttp的ReponseBody做为返回类型

示例代码: 
```
Call<ResponseBody> call =mService.getFile();
call.enqueue(new Callback<ResponseBody>() {
    @Override
    public void onResponse(Call<ResponseBody> call, Response<ResponseBody>
response) {
       try {
            writeResponseBodyToDisk(MainActivity.this, response.body());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    @Override
    public void
onFailure(Call<ResponseBody> call, Throwable t) {
        Log.e("H", "result
: " + t.toString());
    }
});
```
 

 

ReponseBody会拿到文件流,对拿到的文件流做处理
示例代码:

```
public boolean writeResponseBodyToDisk(Context context, ResponseBody body) throws IOException {
    Log.d("tag","开始下载"+"/"+body.contentType().toString());
    boolean flag = true;
    String type =
    body.contentType().toString();

 if (type.equals(".apk")) {
        mFileSuffix = ".apk";
    } else if (type.equals(".png")) {
        mFileSuffix = ".png";
   } 
 else if(type.equals(".jpg")) {
   mFileSuffix = ".jpg"; }
    if (type.equals(".zip")) {
        mFileSuffix = ".zip";
    }
    // 其他同上 自己判断加入
    final String name ="guo"+System.currentTimeMillis() + mFileSuffix;
    final String path =Environment.getExternalStorageDirectory() + File.separator + name;
    /**
     * 写入流
     */
    InputStream is = body.byteStream();
    File file = new File(path);
    FileOutputStream fos = new FileOutputStream(file);
    BufferedInputStream bis = new BufferedInputStream(is);
    byte[] buffer = new byte[1024];
    int len;
    while ((len = bis.read(buffer)) !=-1) {
        fos.write(buffer, 0, len);
        fos.flush();
    }
    fos.close();
    bis.close();
    is.close();
    Log.d("tag","下载完毕");
    return flag;
}
```

推荐阅读更多精彩内容