第一次使用Retrofit2,并没有使用RxJava,解析返回的是String字符串
在app的build.gradle中添加依赖:
compile 'com.squareup.retrofit2:retrofit:2.3.0'
//字符串解析器
compile 'com.squareup.retrofit2:converter-scalars:2.3.0'
//Gson解析器
//compile 'com.squareup.retrofit2:converter-gson:2.3.0'
添加混淆
## ----------------------------------
## retrofit2相关
## ----------------------------------
-dontwarn retrofit2.**
-dontwarn com.squareup.retrofit2.Platform$Java8
-dontnote com.squareup.retrofit2.Platform
-dontnote retrofit2.Platform$IOS$MainThreadExecutor
-keep class retrofit2.** { *; }
## ----------------------------------
## okhttp3相关
## ----------------------------------
-dontwarn okhttp3.**
-keep class okhttp3.** { *;}
-dontwarn okio.**
创建HttpUtil工具类,retrofit的创建以及一些公共参数的添加和加密都写在util类里。
retrofit的创建:
private static HttpService mService;
/**
* 单例
*
* @return HttpService对象
*/
public static HttpService getInstance() {
synchronized (HttpUtil.class) {
if (mService == null) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(Constants.BASE_URL)
.addConverterFactory(ScalarsConverterFactory.create())
.build();
mService = retrofit.create(HttpService.class);
}
}
return mService;
}
Constants.BASE_URL为服务器根地址。
HttpService类,就是retrofit请求的方法类。
public interface HttpService {
@FormUrlEncoded
@POST("/gateway")
Call<String> userCall(@FieldMap Map<String, String> fields);
@Multipart
@POST("/gateway")
Call<String> uploadSingle(@Part List<MultipartBody.Part> partList);
@Streaming
@GET
Call<ResponseBody> downloadFileWithUrlAsync(@Url String fileUrl);
@GET("/validateAndCacheCardInfo.json?_input_charset=utf-8&cardBinCheck=true")
Call<String> getCardDetail(@Query("cardNo") String cardNo);
}
其中,userCall为我们后台的接口请求方法,请求URL相当于Constants.BASE_URL+“/gateway”,@POST后path路径不能为空,由于我们的接口请求具体方法作为参数放在请求map中,所以所有获取展示信息的接口调用的都是userCall方法(根据服务器配置的不同,可以根据功能分为多个方法,或者多个service类);uploadSingle是文件上传方法;getCardDetail是调用支付宝API银行卡校验使用的查询方法。
userCall的使用(以获取验证码为例):
showProgressDialog("获取验证码...");
startTimer();
HashMap<String, String> postParams = new HashMap<>();
postParams.put("mobile", AppUtil.replaceBlock(phone));
postParams.put("method", Constants.GET_CODE_METHOD);
postParams.put("status", "2");
codeCall = HttpUtil.getInstance()
.userCall(HttpUtil.getParamMap(postParams, false));
codeCall.enqueue(codeCallback);
//验证码请求
private Call<String> codeCall;
//验证回调
private Callback<String> codeCallback = new Callback<String>() {
@Override
public void onResponse(@NonNull Call<String> call, @NonNull Response<String> response) {
MyLog.e(TAG, response.body());
hideProgressDialog();
if (HttpUtil.httpGetCode(response)) {
CodeBean codeBean = new Gson().fromJson(response.body(), CodeBean.class);
if (codeBean == null) {
MyToast.showToast(Constants.ERROR_IN_TRANSLATE);
stopTimer();
} else {
int code = codeBean.code;
if (code == 200) {
hasSendCode = true;
CodeBean.DataBean jsonData = codeBean.data;
sessionId = jsonData.session_id;
MyToast.showToast("发送成功");
} else {
stopTimer();
String message = codeBean.message;
if (code == 201) {
MyToast.showToast(message);
} else {
MyToast.showToast(code + ":" + message);
}
}
}
} else {
stopTimer();
}
}
@Override
public void onFailure(@NonNull Call<String> call, @NonNull Throwable t) {
stopTimer();
hideProgressDialog();
if (!call.isCanceled()) {
if ("timeout".equals(t.getLocalizedMessage())) {
MyToast.showToast(Constants.ERROR_TIME_OUT);
} else {
MyToast.showToast(Constants.ERROR_REQUEST_FAILED);
}
}
}
};
@Override
protected void onStop() {
super.onStop();
if (codeCall != null) {
codeCall.cancel();
}
}
其中HttpUtil.getParamMap(postParams, false)为添加公共参数和加密的方法。
uploadSingle的使用:
与userCall一样:
/**
* 上传图片
*/
private void uploadImage(String path) {
HashMap<String, String> params = new HashMap<>();
params.put("method", Constants.UPLOAD_PHOTO_METHOD);
params.put("user_id", CacheUtil.getString(Constants.ID, "0"));
uploadCall = HttpUtil.getInstance()
.uploadSingle(HttpUtil.getParts(params, activity, new File(path)), true));
showProgressDialog("图片上传中...");
uploadCall.enqueue(uploadCallback);
}
不同的是对传递的参数进行的操作
HttpUtil.getParts(params, activity, new File(path)), true);
/**
* 获取上传到服务器的参数
*
* @return MultipartBody.Part集合
*/
public static List<MultipartBody.Part> getParts(HashMap<String, String> postParams, Activity activity, File logoFile, boolean sso) {
MultipartBody.Builder builder = new MultipartBody.Builder()
.setType(MultipartBody.FORM);//表单类型
Iterator<Map.Entry<String, String>> iterator = getParamMap(postParams, sso).entrySet().iterator();
// 添加参数
while (iterator.hasNext()) {
Map.Entry<String, String> next = iterator.next();
String key = next.getKey();
String value = next.getValue();
builder.addFormDataPart(key, value);
}
File file = logoFile;
if (logoFile.length() > 500 * 1024) {
try {
file = new Compressor(activity)
.setMaxWidth(300)
.setMaxHeight(300)
.setCompressFormat(Bitmap.CompressFormat.PNG)
.compressToFile(logoFile);
MyLog.e(TAG, "图片大小" + android.text.format.Formatter.formatFileSize(activity, file.length()));
} catch (IOException e) {
e.printStackTrace();
}
}
RequestBody imageBody = RequestBody.create(MediaType.parse("multipart/form-data"), file);
builder.addFormDataPart("pic", "logo.png", imageBody);//imgfile 后台接收图片流的参数名
return builder.build().parts();
}
此处为了使图片大小不超过500k,对图片进行了压缩,压缩添加依赖compile ‘id.zelory:compressor:2.1.0’;
builder.addFormDataPart(“pic”, “logo.png”, imageBody);
这句代码中pic为上传图片后台需要的参数名,logo.png为图片名。
downloadFileWithUrlAsync的使用
HttpUtil.getInstance()
.downloadFileWithUrlAsync(downUrl)
.enqueue(downloadCallback);
下载大文件,为避免将整个文件读进内存,需要添加@Streaming注解
//下载回调
private Callback<ResponseBody> downloadCallback = new Callback<ResponseBody>() {
@Override
public void onResponse(@NonNull Call<ResponseBody> call, @NonNull Response<ResponseBody> response) {
if (!response.isSuccessful()) {
try {
if (response.errorBody() == null) {
MyToast.showToast(response.code() + ":" + response.message());
} else {
MyToast.showToast(response.errorBody().string());
}
} catch (IOException e) {
e.printStackTrace();
}
} else if (response.body() == null) {
MyToast.showToast(Constants.ERROR_NO_DATA);
} else {
new Thread() {
@Override
public void run() {
super.run();
downloadFileFromServer(response);
}
}.start();
}
}
@Override
public void onFailure(@NonNull Call<ResponseBody> call, @NonNull Throwable t) {
handler.sendEmptyMessage(UPDATE_END_CODE);
MyToast.showToast("下载更新包失败");
if (!"0".equals(type)) {
finish();
System.exit(0);
}
}
};
getCardDetail的使用
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://ccdcapi.alipay.com")
.addConverterFactory(ScalarsConverterFactory.create())
.build();
HttpService mService = retrofit.create(HttpService.class);
cardDetailCall = mService.getCardDetail(cardNo);
cardDetailCall.enqueue(cardDetailCallback);
这里因为调用的是支付宝的接口,baseUrl不一致,所以retrofit的创建没使用统一的HttpUtil中的实例。
对比HttpService中的getCardDetail方法:
@GET("/validateAndCacheCardInfo.json?_input_charset=utf-8&cardBinCheck=true")
Call<String> getCardDetail(@Query("cardNo")String cardNo);
支付宝接口请求的url为:
http://ccdcapi.alipay.com/validateAndCacheCardInfo.json?_input_charset=utf-8&cardBinCheck=true&cardNo=输入的银行卡号
@Query(“cardNo”)注解表示传入的字符串为查询参数cardNo的值;
也可以使用@Url注解,重新定义接口地址,将地址以参数的形式传入。
@GET
Call<String> getCardDetail(@Url String url);
调用时:
Call<List<Activity>> call = HttpUtil.getInstance()
.getCardDetail(
"http://ccdcapi.alipay.com/validateAndCacheCardInfo.json?_input_charset=utf-8&cardBinCheck=true&cardNo="+cardNo);
注解中还有:
@QueryMap表示传入多个查询参数;
@Path(“test”)表示传入的参数作为路径(如:@GET(“/{test}”)),方法中可传入多个
@Body CommentBody body 表示传入的参数为CommentBody对象
@Field 表示POST请求传入单个字段,使用@Field时记得添加@FormUrlEncoded