总结一下自己对Retrofit的使用,使用办法延续之前封装Volley的使用方法,不好的地方希望看到各位能给予宝贵的建议,谢谢
首先来看看Retrofit的使用
网络上有很多的关于Retrofit的使用的文章,我学习Retrofit使用也是通过网上查找文章
我所参考的原文地址为:
http://www.tuicool.com/articles/AveimyQ
1、添加依赖或者导入Jar包
依赖的添加:
compile 'com.squareup.retrofit2:retrofit:2.1.0'
你也可以导入最新的依赖版本
至于Jar,之后我会随Demo一起上传
2、创建业务请求接口
public interface MyRetrofitService {
//最简单的Get请求,参数以map形式传递
@GET()
Call<BookSearchResponse> getService (@Url String url,@QueryMap Map<String,String> Para);
}
3、创建一个Retrofit实例
Retrofit retrofit = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(IP)
.build();
4、调用请求方法,得到Call的实例,并完成异步请求
BlueService service = retrofit.create(BlueService.class);
Call<BookSearchResponse> call = mBlueService.getSearchBooks("小王子", "", 0, 3);
call.enqueue(new Callback<BookSearchResponse>() {
@Override
public void onResponse(Call<BookSearchResponse> call, Response<BookSearchResponse> response) {
asyncText.setText("异步请求结果: " + response.body().books.get(0).altTitle);
}
@Override
public void onFailure(Call<BookSearchResponse> call, Throwable t) {
}
});
以上4个步骤,就可以完成的Retrofit的基本的网络请求。
但是总感觉使用起来不方便,而且回调里返回BooSearchResponse这个bean对象好像只能应对与简单的Json格式,当公司的接口返回的Json格式复杂的时候,我还是希望将Json的解析与网络的请求返回的数据进行分离,所以重写重写Retrofit的解析器使返回对象为String类型
新建StringConverterFactory类,代码如下:
public class StringConverterFactory extends Converter.Factory {
public static StringConverterFactory create() {
return new StringConverterFactory();
}
private StringConverterFactory() {
}
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
return new StringResponseBodyConverter();
}
@Override
public Converter<?, RequestBody> requestBodyConverter(Type type,
Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
return new StringRequestBodyConverter();
}
}
之后还要实现Resuest请求体类和Response响应体类,分别为StringRequestBodyConverter和StringResponseBodyConverter代码如下:
StringRequestBodyConverter
public class StringRequestBodyConverter implements Converter<String, RequestBody> {
private static final MediaType MEDIA_TYPE = MediaType.parse("application/json; charset=UTF-8");
private static final Charset UTF_8 = Charset.forName("UTF-8");
StringRequestBodyConverter() {
}
@Override public RequestBody convert(String value) throws IOException {
Buffer buffer = new Buffer();
Writer writer = new OutputStreamWriter(buffer.outputStream(), UTF_8);
writer.write(value);
writer.close();
return RequestBody.create(MEDIA_TYPE, buffer.readByteString());
}
}
StringResponseBodyConverter
public class StringResponseBodyConverter implements Converter<ResponseBody, String> {
@Override
public String convert(ResponseBody value) throws IOException {
try {
return value.string();
} finally {
value.close();
}
}
}
之后在创建业务请求接口的时候就可以这样
public interface MyRetrofitService {
//最简单的Get请求,参数以map形式传递
@GET()
Call<String> getService (@Url String url,@QueryMap Map<String,String> Para);
}
Retrofit retrofit = new Retrofit.Builder()
.addConverterFactory(StringConverterFactory.create())
.baseUrl(IP)
.build();
而在异步请求的回调方法得到的body()也将是一个String类型,再写一个回调接口,将Json的解析单独封装,并在其他部分进行解析
封装的Retrofit方法包括简单的Get请求,简单的Post请求,使用Post请求的文件上传,图片按照尺寸压缩的上传,使用Get的文件下载,以及图片的网络加载(由于自己的能力有限,封装的图片加载的方法个人感觉不好用,所有又加入了ImageLoader用于图片的网络加载)
创建业务请求的整体接口如下:
public interface MyRetrofitService {
//最简单的Get请求,参数以map形式传递
@GET()
Call<String> getService (@Url String url,@QueryMap Map<String,String> Para);
@GET()
Call<String> getService (@Url String url);
//下载文件(包含下载进度)
@GET()
Call<ResponseBody> getFileService(@Url String url);
@GET()
Call<ResponseBody> getFileService(@Url String url,@QueryMap Map<String,String> Para);
//Post请求 以表单的形式
@FormUrlEncoded
@POST()
Call<String> postService(@Url String url,@FieldMap Map<String,String> Para);
//上传文件(可以携带参数)用@Part
//如下所示
@Multipart
@POST()//多文件上传
Call<String> putService(@Url String url,@PartMap Map<String, MultipartBody.Part> Filemap);
@Multipart
@POST()//多文件上传,携带参数(在使用的时候根据携带的参数而定)
Call<String> putService(@Url String url,@PartMap Map<String, MultipartBody.Part> Filemap,@Part("name") RequestBody description);
@Multipart
@POST()//单文件上传
Call<String> putService(@Url String url,@Part MultipartBody.Part File);
@Multipart
@POST()//单文件上传,携带参数(在使用的时候根据携带的参数而定)
Call<String> putService(@Url String url,@Part MultipartBody.Part File,@Part("name") RequestBody description);
}
整体的使用方法都是大同小异的,基本都是上面的几步。
但是现在又有一个问题,Retrofit没有提供上传和下载的进度回调,这个时候就感觉*****(和谐)。在网上又找了好长的时间,终于找到了思路,自己重写RequestBody和ResponseBody在其中加入进度的回调
先定义进度回调接口ProgressListener如下:
public interface ProgressListener {
void update(long bytesRead, long contentLength, boolean done);
}
之后重写RequestBody和ResponseBody 如下:
public class ProgressResponseBody extends ResponseBody {
private final ResponseBody responseBody;
private final ProgressListener progressListener;
private BufferedSource bufferedSource;
public ProgressResponseBody(ResponseBody responseBody, ProgressListener progressListener) {
this.responseBody = responseBody;
this.progressListener = progressListener;
}
@Override
public MediaType contentType() {
return responseBody.contentType();
}
@Override
public long contentLength() {
return responseBody.contentLength();
}
@Override
public BufferedSource source() {
if (bufferedSource == null) {
bufferedSource = Okio.buffer(source(responseBody.source()));
}
return bufferedSource;
}
private Source source(Source source) {
return new ForwardingSource(source) {
long totalBytesRead = 0L;
long bytesWritten = responseBody.contentLength();
@Override
public long read(Buffer sink, long byteCount) throws IOException {
long bytesRead = super.read(sink, byteCount);
totalBytesRead += bytesRead != -1 ? bytesRead : 0;
progressListener.update(totalBytesRead,bytesWritten , bytesWritten == totalBytesRead);
return bytesRead;
}
};
}
}
RequestBody:
public class ProgressRequestBody extends RequestBody {
//实际的请求体
private RequestBody body;
//进度回调接口
private ProgressListener progressListener;
//包装完成的BufferedSink
private BufferedSink bufferedSink;
public ProgressRequestBody(RequestBody body,ProgressListener progressListener){
this.body = body;
this.progressListener = progressListener;
}
/**
* 重写调用实际响应体的contentType
* @return
*/
@Override
public MediaType contentType() {
return body.contentType();
}
@Override
public void writeTo(BufferedSink sink) throws IOException {
if (bufferedSink == null) {
//包装
bufferedSink = Okio.buffer(sink(sink));
}
//写入
body.writeTo(bufferedSink);
//必须调用flush,否则最后一部分数据可能不会被写入
bufferedSink.flush();
}
/**
* 写入,回调进度接口
* @param sink
* @return
*/
private Sink sink(Sink sink){
return new ForwardingSink(sink) {
//当前写入字节数
long bytesWritten = 0L;
//总字节长度,避免多次调用contentLength()方法
long contentLength = 0L;
@Override
public void write(Buffer source, long byteCount) throws IOException {
super.write(source, byteCount);
if (contentLength == 0) {
//获得contentLength的值,后续不再调用
contentLength = body.contentLength();
}
//增加当前写入的字节数
bytesWritten += byteCount;
//回调
progressListener.update(bytesWritten, contentLength, bytesWritten == contentLength);
}
};
}
}
基本步骤都完成了,接下来看看封装的类MyRetrofit
public class MyRetrofit {
private static String POST = "post";
private static String GET = "get";
/**
* 此方法是调用Get和Post的请求的基础
* @param type
* @param IP
* @param url
* @param para
* @param handler
* @param success
* @param fail
* @param marker
* @param netSuccess
*/
private static void BaseGetAndPost(String type,String IP,String url,Map<String,String> para, final Handler handler,
final int success, final int fail, final int marker, final NetSuccess netSuccess){
Retrofit retrofit = new Retrofit.Builder()
.addConverterFactory(StringConverterFactory.create())
.baseUrl(IP)
.build();
MyRetrofitService service = retrofit.create(MyRetrofitService.class);
Call<String> call = null;
//判断是POST请求还是GET请求
if(type.equals(POST)){
call = service.postService(url,para);
}else if(type.equals(GET)){
if(para != null && para.size() != 0) {
call = service.getService(url, para);
}else{
call = service.getService(url);
}
}else {
StringUtil.showMessage("参数出错");
return;
}
call.enqueue(new Callback<String>() {
@Override
public void onResponse(Call<String> call, Response<String> response) {
int code = response.code();
Message message = new Message();
message.what = success;
message.arg1 = marker;
if(code == 200){
if(netSuccess != null){
message.obj = netSuccess.onSuccess(response.body().toString());
}
}else {
StringUtil.showMessage("连接服务器出错");
}
handler.sendMessage(message);
}
@Override
public void onFailure(Call<String> call, Throwable throwable) {
Message message = new Message();
message.what = fail;
message.obj = "FAIL";
handler.sendMessage(message);
}
});
}
/**
* 此方法为单一文件和多文件的上传的基础方法
* 传递过来的为文件路径,且能显示上传进度
* @param IP
* @param url
* @param FilePath
* @param description
* @param handler
* @param success
* @param fail
* @param marker
* @param netSuccess
*/
private static void putFileBase(String IP,String url,List<String> FilePath,String description,final Handler handler
, final int success, final int fail, final int marker, final NetSuccess netSuccess,final ProgressListener listener){
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(IP)
.addConverterFactory(StringConverterFactory.create())
.build();
MyRetrofitService service = retrofit.create(MyRetrofitService.class);
Call<String> call = null;
if(FilePath == null || FilePath.size() == 0){
StringUtil.showMessage("请选择文件");
return;
}else{
if(FilePath.size() ==1) {//单文件上传
File file = new File(FilePath.get(0));
RequestBody fileBody = RequestBody.create(MediaType.parse("multipart/form-data"), file);
ProgressRequestBody requestBody = new ProgressRequestBody(fileBody,listener);
MultipartBody.Part part = MultipartBody.Part.createFormData("file", file.getName(), requestBody);
if (description == null) {
call = service.putService(url, part);
} else {
RequestBody descriptionBody = RequestBody.create(null, description);
call = service.putService(url, part, descriptionBody);
}
}else{//多文件上传
Map<String,MultipartBody.Part> fileBody = null;
for(int i=0;i<FilePath.size();i++){
File file = new File(FilePath.get(i));
RequestBody body = RequestBody.create(MediaType.parse("multipart/form-data"), file);
ProgressRequestBody requestBody = new ProgressRequestBody(body,listener);
MultipartBody.Part part = MultipartBody.Part.createFormData("file", file.getName(), requestBody);
fileBody.put(""+i,part);
}
if (description == null) {
call = service.putService(url, fileBody);
} else {
RequestBody descriptionBody = RequestBody.create(null, description);
call = service.putService(url, fileBody, descriptionBody);
}
}
}
call.enqueue(new Callback<String>() {
@Override
public void onResponse(Call<String> call, Response<String> response) {
int code = response.code();
Message message = new Message();
message.what = success;
message.arg1 = marker;
if(code == 200){
if(netSuccess != null){
message.obj = netSuccess.onSuccess(response.body().toString());
}
}else{
StringUtil.showMessage("连接服务器出错");
}
handler.sendMessage(message);
}
@Override
public void onFailure(Call<String> call, Throwable throwable) {
Message message = new Message();
message.what = fail;
message.obj = "FAIL";
handler.sendMessage(message);
}
});
}
/**
* 此方法为单一文件和多文件的上传的基础方法
* 一般用于图片的上传(可进行图片的压缩)
* @param IP
* @param url
* @param FilePath
* @param description
* @param handler
* @param success
* @param fail
* @param marker
* @param netSuccess
*/
private static void putFileBase(String IP,String url,List<String> FilePath,int width,int hight,String description
,final Handler handler, final int success, final int fail, final int marker, final NetSuccess netSuccess){
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(IP)
.addConverterFactory(StringConverterFactory.create())
.build();
MyRetrofitService service = retrofit.create(MyRetrofitService.class);
Call<String> call = null;
if(FilePath == null || FilePath.size() == 0){
StringUtil.showMessage("请选择文件");
return;
}else{
if(FilePath.size() ==1) {//单文件上传
File file = new File(FilePath.get(0));
Bitmap bitmap = CommonUtil.changeBitmap(FilePath.get(0), width, hight);
String[] arr = FilePath.get(0).split("\\.");
int len = arr.length;
byte[] byteArr = CommonUtil.bitMap2byteArr(bitmap, arr[len - 1]);
RequestBody fileBody = RequestBody.create(MediaType.parse("multipart/form-data"), byteArr);
MultipartBody.Part part = MultipartBody.Part.createFormData("file", file.getName(), fileBody);
if (description == null) {
call = service.putService(url, part);
} else {
RequestBody descriptionBody = RequestBody.create(null, description);
call = service.putService(url, part, descriptionBody);
}
}else{//多文件上传
Map<String,MultipartBody.Part> fileBody = null;
for(int i=0;i<FilePath.size();i++){
File file = new File(FilePath.get(i));
Bitmap bitmap = CommonUtil.changeBitmap(FilePath.get(0), width, hight);
String[] arr = FilePath.get(i).split("\\.");
int len = arr.length;
byte[] byteArr = CommonUtil.bitMap2byteArr(bitmap, arr[len - 1]);
RequestBody body = RequestBody.create(MediaType.parse("multipart/form-data"),byteArr);
MultipartBody.Part part = MultipartBody.Part.createFormData("file", file.getName(), body);
fileBody.put(""+i,part);
}
if (description == null) {
call = service.putService(url, fileBody);
} else {
RequestBody descriptionBody = RequestBody.create(null, description);
call = service.putService(url, fileBody, descriptionBody);
}
}
}
call.enqueue(new Callback<String>() {
@Override
public void onResponse(Call<String> call, Response<String> response) {
int code = response.code();
Message message = new Message();
message.what = success;
message.arg1 = marker;
if(code == 200){
if(netSuccess != null){
message.obj = netSuccess.onSuccess(response.body().toString());
}
}else{
StringUtil.showMessage("连接服务器出错");
}
handler.sendMessage(message);
}
@Override
public void onFailure(Call<String> call, Throwable throwable) {
Message message = new Message();
message.what = fail;
message.obj = "FAIL";
handler.sendMessage(message);
}
});
}
/**
* 用于下载文件(应该先检查文件是否存在)
* @param IP
* @param url
* @param savePath
* @param para
* @param handler
* @param success
* @param fail
* @param marker
* @param netSuccess
* @param listener
*/
private static void getFileBase(String IP,String url, final String savePath,Map<String,String> para, final Handler handler
, final int success, final int fail, final int marker, final NetSuccess netSuccess, final ProgressListener listener){
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(IP)
.addConverterFactory(StringConverterFactory.create())
.build();
MyRetrofitService service = retrofit.create(MyRetrofitService.class);
Call<ResponseBody> call = null;
if(para ==null || para.size() ==0) {
call = service.getFileService(url);
}else{
call = service.getFileService(url,para);
}
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
int code = response.code();
Message message = new Message();
message.what = success;
message.arg1 = marker;
if(code == 200){
if(netSuccess != null){
ResponseBody body = response.body();
ProgressResponseBody responseBody = new ProgressResponseBody(body,listener);
byte[] bytes = new byte[0];
try {
bytes = responseBody.bytes();
} catch (IOException e) {
e.printStackTrace();
}
if(CommonUtil.saveBytesToFile(bytes,savePath)){
StringUtil.showMessage("下载完成");
}else{
StringUtil.showMessage("下载失败");
}
}
}else{
StringUtil.showMessage("连接服务器出错");
}
handler.sendMessage(message);
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable throwable) {
Message message = new Message();
message.what = fail;
message.obj = "FAIL";
handler.sendMessage(message);
}
});
}
private static void getImageBase(String IP,String url,Map<String,String> para, final Handler handler
, final int success, final int fail, final int marker){
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(IP)
.addConverterFactory(StringConverterFactory.create())
.build();
MyRetrofitService service = retrofit.create(MyRetrofitService.class);
Call<ResponseBody> call = null;
if(para ==null || para.size() ==0) {
call = service.getFileService(url);
}else{
call = service.getFileService(url,para);
}
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
int code = response.code();
Message message = new Message();
message.what = success;
message.arg1 = marker;
if(code == 200){
ResponseBody body = response.body();
byte[] bytes = new byte[0];
try {
bytes = body.bytes();
} catch (IOException e) {
e.printStackTrace();
}
message.obj = CommonUtil.Bytes2Bimap(bytes);
}else{
StringUtil.showMessage("连接服务器出错");
}
handler.sendMessage(message);
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable throwable) {
Message message = new Message();
message.what = fail;
message.obj = "FAIL";
handler.sendMessage(message);
}
});
}
public interface NetSuccess{
/**
* 成功获取数据
* @param result json数据
* @return 解析之后的结果
*/
public <T> T onSuccess(T result);
}
/**
* Get请求的使用方法
* @param handler
* @param name
* @param marker
*/
public static void getConCeShi(Handler handler,String name,int marker){
Map<String,String> para = new HashMap<>();
if(!StringUtil.isEmpty(name)) {
para.put("name", name);
}
BaseGetAndPost(GET, Contents.IP, Contents.getCeShi, para, handler, Contents.SUCCESS,
Contents.FAIL, marker, new NetSuccess() {
@Override
public <T> T onSuccess(T result) {
//返回的结果,字符串类型
return result;
}
});
}
/**
* Post请求的是用方法
* @param handler
* @param name
* @param marker
*/
public static void postConCeShi(Handler handler,String name,int marker){
Map<String,String> para = new HashMap<>();
if(!StringUtil.isEmpty(name)) {
para.put("name", name);
}
BaseGetAndPost(POST, Contents.IP, Contents.postCeShi, para, handler, Contents.SUCCESS,
Contents.FAIL, marker, new NetSuccess() {
@Override
public <T> T onSuccess(T result) {
//返回的结果,字符串类型
return result;
}
});
}
/**
* 文件的上传图片不需要压缩的话也可以用此方法
* @param handler
* @param filePath
* @param descrtion
* @param marker
*/
public static void putFileCeShi(Handler handler,List<String> filePath,String descrtion,int marker){
putFileBase(Contents.IP, Contents.putCeShi, filePath, descrtion, handler, Contents.SUCCESS,
Contents.FAIL, marker, new NetSuccess() {
@Override
public <T> T onSuccess(T result) {
//返回的结果,字符串类型
return result;
}
}, new ProgressListener() {
@Override
public void update(long bytesRead, long contentLength, boolean done) {
//上传进度的展示
}
});
}
/**
* 图片的按照尺寸的压缩上传
* @param handler
* @param filePath
* @param width
* @param hight
* @param descrtion
* @param marker
*/
public static void putImageCeShi(Handler handler,List<String> filePath,int width,int hight,String descrtion,int marker){
putFileBase(Contents.IP, Contents.putCeShi, filePath,width,hight, descrtion, handler, Contents.SUCCESS,
Contents.FAIL, marker, new NetSuccess() {
@Override
public <T> T onSuccess(T result) {
return result;
}
});
}
public static void getFileCeShi(Handler handler,String savePath,String curfile,String path,int marker){
Map<String,String> para = new HashMap<>();
para.put("curfile",curfile);
para.put("path",path);
getFileBase(Contents.IP, Contents.setCeShi, savePath, para, handler, Contents.SUCCESS,
Contents.FAIL, marker, new NetSuccess() {
@Override
public <T> T onSuccess(T result) {
return null;
}
}, new ProgressListener() {
@Override
public void update(long bytesRead, long contentLength, boolean done) {
//此处显示下载进度的对应UI
}
});
}
public static void displayNetImage(Handler handler,String curfile,String path,int marker){
Map<String,String> para = new HashMap<>();
para.put("curfile",curfile);
para.put("path",path);
getImageBase(Contents.IP, Contents.setCeShi, para, handler, Contents.SUCCESS, Contents.FAIL
, marker);
}
}
到此已经是封装完成了,使用方法如下:
//GET请求的测试
// MyRetrofit.getConCeShi(handler,"", BackMakerUtil.CESHIBACK);
//Post请求的测试
// MyRetrofit.postConCeShi(handler,"fei",BackMakerUtil.CESHIBACK);
//文件上传(有说明信息就写,没有就传null,不需要压缩的图片也可以用此方法)
// MyRetrofit.putFileCeShi(handler, file, "fei", BackMakerUtil.CESHIBACK);
//需要压缩的图片的上传
// MyRetrofit.putImageCeShi(handler, file, 300, 150, "fei", BackMakerUtil.CESHIBACK);
//下载文件
// MyRetrofit.getFileCeShi(handler,path,"a.jpg","D:/aDown",BackMakerUtil.CESHIBACK);
//加载图片
MyRetrofit.displayNetImage(handler,"a.jpg","D://aDown",BackMakerUtil.CESHIBACK);
到此就全部写完了,希望看到各位能提出宝贵的看法
本人只是一个Android菜鸟,由于还不会RxJava,所以只能这样封装了,学会了RxJava还会继续采用RxJava+Retrofit的方法进行封装