参考资料:
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进行再次封装的框架。
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
示例代码:
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:****
- 默认的服务器地址,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;
}
```