赞
踩
Android开发中的网络框架经过多年的发展,目前比较主流的就是Retrofit了,Retrofit2版本出现也有几年了,为了方便使用,特封装了一些关于Retrofit2的代码,分享给大家。
框架主要包括:
使用效果预览:
Retrofit框架内部使用的还是OkHttp框架,在实例化的时候可以自定义OkHttpClient来实现一些个性化的设置,如超时时长、HTTPS协议支持等。
1、OkHttpClient实例
private static int TIME_OUT = 30; // 30秒超时断开连接
public static OkHttpClient client = new OkHttpClient.Builder()
.sslSocketFactory(sslSocketFactory, trustAllCert)
.connectTimeout(TIME_OUT, TimeUnit.SECONDS)
.readTimeout(TIME_OUT, TimeUnit.SECONDS)
.writeTimeout(TIME_OUT, TimeUnit.SECONDS)
.build();
其中,自定义了sslSocketFactory和trustAllCert证书管理器:
private static X509TrustManager trustAllCert = new X509TrustManager() { @Override public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { } @Override public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { } @Override public java.security.cert.X509Certificate[] getAcceptedIssuers() { return new java.security.cert.X509Certificate[]{}; } }; private static SSLSocketFactory sslSocketFactory = new SSLSocketFactoryCompat(trustAllCert);
SSLSocketFactoryCompat类是从网络上找的,这里就不多说了,详见源码部分。
2、Retrofit对象
/**
* 网络框架单例
* <p>因为示例中用到不同的接口地址,URL_BASE随便写了一个,如果是实际项目中,则设置为根路径即可</p>
*/
private static Retrofit retrofit = new Retrofit.Builder()
.baseUrl(Constant.URL_BASE)
.client(client)
.build();
定义好OkHttpClient后,Retrofit对象的构造就很简单,需要注意的是,一定要为retrofit对象指定好baseUrl,如果是后台地址有多个,写其中一个就可以了。
网络上有不少涉及到动态注册baseUrl的文章,写demo的时候并没有遇到此类问题,如果有遇到的话再寻找对应方案即可。
封装的框架让使用者传递一个回调的对象,在网络请求开始、结束、异常等阶段根据需要将调用相应的回调接口。
该回调接口还能够自动处理一些网络异常,并给予相应的提示(按需修改)。
/** * 网络请求结果处理类 * @param <T> 请求结果封装对象 */ public static abstract class ResultHandler<T> { Context context; public ResultHandler(Context context) { this.context = context; } /** * 判断网络是否未连接 * * @return */ public boolean isNetDisconnected() { return NetworkUtil.isNetDisconnected(context); } /** * 请求成功之前 */ public abstract void onBeforeResult(); /** * 请求成功时 * * @param t 结果数据 */ public abstract void onResult(T t); /** * 服务器出错 */ public void onServerError() { // 服务器处理出错 Toast.makeText(context, R.string.net_server_error, Toast.LENGTH_SHORT).show(); } /** * 请求失败后的处理 */ public abstract void onAfterFailure(); /** * 请求失败时的处理 * * @param t */ public void onFailure(Throwable t) { if (t instanceof SocketTimeoutException || t instanceof ConnectException) { // 连接异常 if (NetworkUtil.isNetworkConnected(context)) { // 服务器连接出错 Toast.makeText(context, R.string.net_server_connected_error, Toast.LENGTH_SHORT).show(); } else { // 手机网络不通 Toast.makeText(context, R.string.net_not_connected, Toast.LENGTH_SHORT).show(); } } else if (t instanceof Exception) { // 功能异常 Toast.makeText(context, R.string.net_unknown_error, Toast.LENGTH_SHORT).show(); } } }
其中的string定义:
<string name="net_not_connected">发送请求失败,请检查网络连接</string>
<string name="net_server_connected_error">连接服务器失败,请稍后重试</string>
<string name="net_server_error">服务器处理请求失败,请稍后重试</string>
<string name="net_server_param_error">应用数据出错,请刷新重试</string>
<string name="net_unknown_error">未知错误,请稍后重试</string>
1、定义GetRequest
Retrofit通过定义service来发起网络请求服务,Get请求服务可以定义为:
package com.dommy.retrofitframe.network; import okhttp3.ResponseBody; import retrofit2.Call; import retrofit2.http.GET; import retrofit2.http.Url; /** * Get请求封装 */ public interface GetRequest { /** * 发送Get请求请求 * @param url URL路径 * @return */ @GET Call<ResponseBody> getUrl(@Url String url); }
说明:
- 需要加@GET注解;
- url对应的参数需要加@Url注解。
2、封装get请求方法
定义static方法:
/** * 发送GET网络请求 * @param url 请求地址 * @param clazz 返回的数据类型 * @param resultHandler 回调 * @param <T> 泛型 */ public static <T extends BaseResult> void sendGetRequest(String url, final Class<T> clazz, final ResultHandler<T> resultHandler) { // 判断网络连接状况 if (resultHandler.isNetDisconnected()) { resultHandler.onAfterFailure(); return; } GetRequest getRequest = retrofit.create(GetRequest.class); // 构建请求 Call<ResponseBody> call = getRequest.getUrl(url); call.enqueue(new Callback<ResponseBody>() { @Override public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) { resultHandler.onBeforeResult(); try { ResponseBody body = response.body(); if (body == null) { resultHandler.onServerError(); return; } String string = body.string(); T t = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create().fromJson(string, clazz); resultHandler.onResult(t); } catch (IOException e) { e.printStackTrace(); resultHandler.onFailure(e); } } @Override public void onFailure(Call<ResponseBody> call, Throwable t) { resultHandler.onFailure(t); resultHandler.onAfterFailure(); } }); }
其中,返回值的Gson转换过程,可以根据需要选择去掉;如果返回值都是json格式,可以保留,这样在使用处就可以直接拿到Bean对象,比较方便。
3、发起Get请求
RetrofitRequest.sendGetRequest(url, WeatherResult.class, new RetrofitRequest.ResultHandler<WeatherResult>(this) { @Override public void onBeforeResult() { // 这里可以放关闭loading } @Override public void onResult(WeatherResult weatherResult) { String weather = new Gson().toJson(weatherResult); tvContent.setText(weather); } @Override public void onAfterFailure() { // 这里可以放关闭loading } });
其中,WeatherResult类:
package com.dommy.retrofitframe.network.result; import com.google.gson.JsonObject; public class WeatherResult extends BaseResult { private int code; private String msg; private JsonObject data; // 数据部分也是一个bean,用JsonObject代替了 public int getCode() { return code; } public void setCode(int code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public JsonObject getData() { return data; } public void setData(JsonObject data) { this.data = data; } }
1、定义PostRequest
Post请求服务可以定义为:
package com.dommy.retrofitframe.network; import java.util.Map; import okhttp3.ResponseBody; import retrofit2.Call; import retrofit2.http.FieldMap; import retrofit2.http.FormUrlEncoded; import retrofit2.http.POST; import retrofit2.http.Url; /** * Post请求封装 */ public interface PostRequest{ /** * 发送Post请求 * @param url URL路径 * @param requestMap 请求参数 * @return */ @FormUrlEncoded @POST Call<ResponseBody> postMap(@Url String url, @FieldMap Map<String, String> requestMap); }
说明:
- 需要加@FormUrlEncoded、@POST注解;
- url对应的参数需要加@Url注解。
- post提交的参数需要加@FieldMap注解。
2、封装post请求方法
定义static方法:
/** * 发送post网络请求 * @param url 请求地址 * @param paramMap 参数列表 * @param clazz 返回的数据类型 * @param resultHandler 回调 * @param <T> 泛型 */ public static <T extends BaseResult> void sendPostRequest(String url, Map<String, String> paramMap, final Class<T> clazz, final ResultHandler<T> resultHandler) { // 判断网络连接状况 if (resultHandler.isNetDisconnected()) { resultHandler.onAfterFailure(); return; } PostRequest postRequest = retrofit.create(PostRequest.class); // 构建请求 Call<ResponseBody> call = postRequest.postMap(url, paramMap); call.enqueue(new Callback<ResponseBody>() { @Override public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) { resultHandler.onBeforeResult(); try { ResponseBody body = response.body(); if (body == null) { resultHandler.onServerError(); return; } String string = body.string(); T t = new Gson().fromJson(string, clazz); resultHandler.onResult(t); } catch (IOException e) { e.printStackTrace(); resultHandler.onFailure(e); } } @Override public void onFailure(Call<ResponseBody> call, Throwable t) { resultHandler.onFailure(t); resultHandler.onAfterFailure(); } }); }
3、发起Post请求
RetrofitRequest.sendPostRequest(url, paramMap, WeatherResult.class, new RetrofitRequest.ResultHandler<WeatherResult>(this) { @Override public void onBeforeResult() { // 这里可以放关闭loading } @Override public void onResult(WeatherResult weatherResult) { String weather = new Gson().toJson(weatherResult); tvContent.setText(weather); } @Override public void onAfterFailure() { // 这里可以放关闭loading } });
1、定义FileRequest
package com.dommy.retrofitframe.network; import java.util.Map; import okhttp3.RequestBody; import okhttp3.ResponseBody; import retrofit2.Call; import retrofit2.http.GET; import retrofit2.http.Multipart; import retrofit2.http.POST; import retrofit2.http.PartMap; import retrofit2.http.Streaming; import retrofit2.http.Url; /** * 文件上传请求封装 */ public interface FileRequest { /** * 上传文件请求 * @param url URL路径 * @param paramMap 请求参数 * @return */ @Multipart @POST Call<ResponseBody> postFile(@Url String url, @PartMap Map<String, RequestBody> paramMap); /** * 下载文件get请求 * @param url 链接地址 * @return */ @Streaming @GET Call<ResponseBody> download(@Url String url); }
说明:
- 文件上传需要额外添加@Multipart、@PartMap注解;
- 文件下载需要额外添加@Streaming注解。
2、封装文件上传方法
定义static方法:
/** * 发送上传文件网络请求 * @param url 请求地址 * @param file 文件 * @param clazz 返回的数据类型 * @param resultHandler 回调 * @param <T> 泛型 */ public static <T extends BaseResult> void fileUpload(String url, File file, final Class<T> clazz, final ResultHandler<T> resultHandler) { // 判断网络连接状况 if (resultHandler.isNetDisconnected()) { resultHandler.onAfterFailure(); return; } FileRequest fileRequest = retrofit.create(FileRequest.class); Map<String, RequestBody> paramMap = new HashMap<>(); addMultiPart(paramMap, "file", file); // 构建请求 Call<ResponseBody> call = fileRequest.postFile(url, paramMap); call.enqueue(new Callback<ResponseBody>() { @Override public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) { resultHandler.onBeforeResult(); try { ResponseBody body = response.body(); if (body == null) { resultHandler.onServerError(); return; } String string = body.string(); T t = new Gson().fromJson(string, clazz); resultHandler.onResult(t); } catch (IOException e) { e.printStackTrace(); resultHandler.onFailure(e); } } @Override public void onFailure(Call<ResponseBody> call, Throwable t) { resultHandler.onFailure(t); resultHandler.onAfterFailure(); } }); }
3、发起文件上传请求
因为没有找到现成的上传文件接口,所以这里就随便弄了个接口做了个例子。自己写的时候在后台做一个接口文件的接口就行了。
服务端写法如果有不明白的,可以参见:《HttpClient4.5.2由Client客户端上传File文件流至Server服务端》一文中的server端代码。
File file = null; try { // 通过新建文件替代文件寻址 file = File.createTempFile("abc", "txt"); } catch (IOException e) { } String url = Constant.URL_LOGIN; RetrofitRequest.fileUpload(url, file, BaseResult.class, new RetrofitRequest.ResultHandler<BaseResult>(this) { @Override public void onBeforeResult() { // 这里可以放关闭loading } @Override public void onResult(BaseResult baseResult) { tvContent.setText("上传成功"); } @Override public void onAfterFailure() { // 这里可以放关闭loading } });
基于FileRequest的定义,封装fileDownload方法即可。
1、定义DownloadHandler
因为文件下载过程中涉及到进度的计算、显示,所以这里需要定义一个DownloadHandler回调:
/**
* 文件下载回调
*/
public interface DownloadHandler {
/**
* 接收到数据体
* @param body 响应体
*/
public void onBody(ResponseBody body);
/**
* 文件下载出错
*/
public void onError();
}
2、定义Retrofit对象
为了防止回调方法内部涉及到界面操作,出现NetworkOnMainThreadException问题,特别自定义一个回调方法执行器executorService。
ExecutorService executorService = Executors.newFixedThreadPool(1);
// 网络框架
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(Constant.URL_BASE)
.callbackExecutor(executorService)
.build();
3、封装文件下载方法
/** * 文件下载 * @param url 请求地址 * @param downloadHandler 回调接口 */ public static void fileDownload(String url, final DownloadHandler downloadHandler) { // 回调方法执行器,定义回调在子线程中执行,避免Callback返回到MainThread,导致文件下载出现NetworkOnMainThreadException ExecutorService executorService = Executors.newFixedThreadPool(1); // 网络框架 Retrofit retrofit = new Retrofit.Builder() .baseUrl(Constant.URL_BASE) .callbackExecutor(executorService) .build(); FileRequest fileRequest = retrofit.create(FileRequest.class); // 构建请求 Call<ResponseBody> call = fileRequest.download(url); call.enqueue(new Callback<ResponseBody>() { @Override public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) { if (response.isSuccessful()) { // 写入文件 downloadHandler.onBody(response.body()); } else { downloadHandler.onError(); } } @Override public void onFailure(Call<ResponseBody> call, Throwable t) { downloadHandler.onError(); } }); }
4、文件下载示例
RetrofitRequest.fileDownload(Constant.URL_DOWNLOAD, new RetrofitRequest.DownloadHandler() {
@Override
public void onBody(ResponseBody body) {
if (!writeResponseBodyToDisk(body)) {
mHandler.sendEmptyMessage(DOWNLOAD_ERROR);
}
}
@Override
public void onError() {
mHandler.sendEmptyMessage(DOWNLOAD_ERROR);
}
});
其中涉及到的对象和方法:
private static final int DOWNLOAD_ING = 1;// 下载中 private static final int DOWNLOAD_FINISH = 2;// 下载结束 private static final int DOWNLOAD_ERROR = -1;// 下载出错 private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case DOWNLOAD_ING: // 正在下载 showProgress(); break; case DOWNLOAD_FINISH: // 下载完成 onDownloadFinish(); break; case DOWNLOAD_ERROR: // 出错 onDownloadError(); break; } } }; /** * 写文件入磁盘 * * @param body 请求结果 * @return boolean 是否下载写入成功 */ private boolean writeResponseBodyToDisk(ResponseBody body) { savePath = StorageUtil.getDownloadPath(); File apkFile = new File(savePath, fileName); InputStream inputStream = null; OutputStream outputStream = null; try { byte[] fileReader = new byte[4096]; // 获取文件大小 long fileSize = body.contentLength(); long fileSizeDownloaded = 0; inputStream = body.byteStream(); outputStream = new FileOutputStream(apkFile); // byte转Kbyte BigDecimal bd1024 = new BigDecimal(1024); totalByte = new BigDecimal(fileSize).divide(bd1024, BigDecimal.ROUND_HALF_UP).setScale(0).intValue(); // 只要没有取消就一直下载数据 while (!cancelUpdate) { int read = inputStream.read(fileReader); if (read == -1) { // 下载完成 mHandler.sendEmptyMessage(DOWNLOAD_FINISH); break; } outputStream.write(fileReader, 0, read); fileSizeDownloaded += read; // 计算进度 progress = (int) (((float) (fileSizeDownloaded * 100.0 / fileSize))); downByte = new BigDecimal(fileSizeDownloaded).divide(bd1024, BigDecimal.ROUND_HALF_UP).setScale(0).intValue(); // 子线程中,借助handler更新界面 mHandler.sendEmptyMessage(DOWNLOAD_ING); } outputStream.flush(); return true; } catch (Exception e) { e.printStackTrace(); return false; } finally { if (outputStream != null) { try { outputStream.close(); } catch (IOException e) { e.printStackTrace(); } } if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } } /** * 显示下载进度 */ private void showProgress() { String text = progress + "% | " + downByte + "Kb / " + totalByte + "Kb"; tvContent.setText(text); } /** * 下载出错处理 */ private void onDownloadError() { tvContent.setText("下载出错"); } /** * 下载完成 */ private void onDownloadFinish() { tvContent.setText("下载已完成"); }
Retrofit框架在APP中具有出现的性能,鉴于API的调用相对复杂(容易冗余),特封装了一套框架来支持日常开发使用。开发者可根据自身需要定义回调接口包含的方法,裁切部分功能。
特别感谢:
https://blog.csdn.net/c__chao/article/details/78573737 提供的API接口
https://blog.csdn.net/webber888/article/details/80993471 提供的https协议适配方法
https://github.com/ahuyangdong/RetrofitFrame
CSDN下载:
https://download.csdn.net/download/ahuyangdong/10722905
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。