總結一下自己對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());
}
}
StringResponseBodyConverterpublic 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);
}
};
}
}
基本步驟都完成了,接下來看看封裝的類MyRetrofitpublic 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的方法進行封裝