OkHttp3
基于OkHttp3封装的网络请求工具类
DEMO下载地址:
功能点
- 支持Http/Https等协议
- 支持Cookie持久化
- 支持Gzip压缩
- 支持协议头参数Head设置、二进制参数请求
- 支持Unicode自动转码、服务器响应编码设置
- 支持同步/异步请求
- 支持四种缓存类型请求:仅网络、仅缓存、先网络再缓存、先缓存再网络
- 支持自定义缓存存活时间与缓存清理功能
- 当Activity/Fragment销毁时自动取消相应的所有网络请求,支持取消指定请求
- 异步请求响应自动切换到UI线程,摒弃runOnUiThread
- Application中自定义全局配置/增加系统默认配置
- 支持文件和图片上传/批量上传,支持同步/异步上传,支持进度提示
- 支持文件下载/批量下载,支持同步/异步下载,支持进度提示
- 支持文件断点下载,独立下载的模块摒弃了数据库记录断点的过时方法
- 完整的日志跟踪与异常处理
- 支持请求结果拦截以及异常处理拦截
- 支持单例客户端,提高网络请求速率
- 后续优化中...
相关截图
网络请求演示
先网络再缓存演示
先缓存再网络演示
上传图片界面
断点下载文件界面
日志
- GET-URL/POST-URL:请求地址
- CostTime:请求耗时(单位:秒)
- Response:响应串
项目演示DEMO
项目中已包含所有支持业务的demo,详情请下载项目参考源码。
引用方式
Maven
- <dependency>
- <groupId>com.z;/groupId>
- <artifactId>okhttp3</artifactId>
- <version>2.7.0</version>
- <type>pom</type>
- </dependency>
Gradle
compile 'com.z;
若出现V7版本冲突请采用下面方式进行依赖:
- compile ('com.z;){
- exclude(module: 'support-annotations')
- }
ProGuard
如果你使用了ProGuard混淆,请添加如下配置:
-dontwarn okio.**
提交记录
- 2016-6-29 项目提交
- 2016-7-4
- 项目框架调整
- 增加Application中全局配置
- 增加系统默认配置
- 修复内存释放bug
- 2016-7-19
- 代码优化、降低耦合
- 修复已知bug
- 2016-8-9
- 增加文件上传功能,支持批量上传
- 2016-8-10
- 增加文件下载功能,支持批量下载、断点下载
- 2016-10-10
- 增加请求结果拦截以及异常处理拦截
- 2016-10-25
- 支持Cookie持久化、协议头参数Head设置
- 2016-12-12
- 增加单例客户端,提高网络请求速率、取消指定请求功能
- 2017-3-3
- 修复上传文件入参bug(感谢Sanqi5401指正)
- 2017-3-6
- 在集成过程中出现了okio丢失的情况请添加 compile 'com.android.support:multidex:1.0.1' (感谢kevin提供相关解决方案)
- 2017-3-31
- 增加单次批量上传文件功能:一次请求上传多个文件
- 2017-4-21
- 增加二进制流请求功能,DEMO中已添加动态权限申请功能
权限
- <!-- 添加读写权限 -->
- <uses-permission android:name="android.;/>
- <uses-permission android:name="android.;/>
- <!-- 访问互联网权限 -->
- <uses-permission android:name="android.;/>
- <uses-permission android:name="android.;/>
- <uses-permission android:name="android.;/>
- <uses-permission android:name="android.;/>
- <uses-permission android:name="android.;/>
自定义全局配置
在Application中配置如下:
String downloadFileDir = Environment.getExternalStorageDirectory().getPath()+"/okHttp_download/";
String cacheDir = Environment.getExternalStorageDirectory().getPath();
if(getExternalCacheDir() != null){
//缓存目录,APP卸载后会自动删除缓存数据
cacheDir = getExternalCacheDir().getPath();
}
OkH(this)
.setConnectTimeout(15)//连接超时时间
.setWriteTimeout(15)//写超时时间
.setReadTimeout(15)//读超时时间
.setMaxCacheSize(10 * 1024 * 1024)//缓存空间大小
.setCacheType)//缓存类型
.setHttpLogTAG("HttpLog")//设置请求日志标识
.setIsGzip(false)//Gzip压缩,需要服务端支持
.setShowHttpLog(true)//显示请求日志
.setShowLifecycleLog(false)//显示Activity销毁日志
.setRetryOnConnectionFailure(false)//失败后不自动重连
.setCachedDir(new File(cacheDir,"okHttp_cache"))//缓存目录
.setDownloadFileDir(downloadFileDir)//文件下载保存目录
.setResponseEncoding)//设置全局的服务器响应编码
.addResultInterceptor)//请求结果拦截器
.addExceptionInterceptor)//请求链路异常拦截器
.setCookieJar(new PersistentCookieJar(new SetCookieCache(), new SharedPrefsCookiePersistor(this)))//持久化cookie
.build();
获取网络请求客户端单例示例
//获取单例客户端(默认)
方法一、OkH(this)//绑定生命周期
.doGetSync().setUrl(url).build());
方法二、OkH()//不绑定生命周期
.doGetSync().setUrl(url).build());
取消指定请求
建议在视图中采用OkH(this)的方式进行请求绑定,该方式会在Activity/Fragment销毁时自动取消当前视图下的所有请求; 请求标识类型支持Object、String、Integer、Float、Double; 请求标识务必保证唯一。
//*******请求时先绑定请求标识,根据该标识进行取消*******/
//方法一:
OkH()
.setReadTimeout(120)
.build("请求标识")//绑定请求标识
.doDownloadFileAsync(info);
//方法二:
OkH("请求标识")//绑定请求标识
.doGetSync(info);
//*******取消指定请求*******/
OkH().cancelRequest("请求标识");
在Activity中同步调用示例
/**
* 同步请求:由于不能在UI线程中进行网络请求操作,所以采用子线程方式
*/
private void sync() {
new Thread(new Runnable() {
@Override
public void run() {
final HttpInfo info = H()
.setUrl(url)
.setResponseEncoding)//设置服务器响应编码
.build();
OkH(this)
.doGetSync(info);
final String result = in();
runOnUiThread(new Runnable() {
@Override
public void run() {
re("同步请求:" + result);
setFromCacheTV(info);
}
});
}
}).start();
}
在Activity中异步调用示例
/**
* 异步请求:回调方法可以直接操作UI
*/
private void async() {
OkH(this).doGetAsync(
H().setUrl(url).build(),
new Callback() {
@Override
public void onFailure(HttpInfo info) throws IOException {
String result = in();
re("异步请求失败:" + result);
}
@Override
public void onSuccess(HttpInfo info) throws IOException {
String result = in();
re("异步请求成功:" + result);
//GSon解析
TimeAndDate time = new Gson().fromJson(result, TimeAndDa);
LogU("MainActivity", ().toString());
setFromCacheTV(info);
}
});
}
仅网络请求
/**
* 仅网络请求
*/
private void forceNetwork(){
OkH().setCacheType).build(this)
.doGetAsync(
H().setUrl(url).build(),
new Callback() {
@Override
public void onSuccess(HttpInfo info) throws IOException {
String result = in();
re("FORCE_NETWORK:" + result);
setFromCacheTV(info);
}
@Override
public void onFailure(HttpInfo info) throws IOException {
re("FORCE_NETWORK:" + in());
}
}
);
}
仅缓存请求
/**
* 仅缓存请求
*/
private void forceCache(){
OkH().setCacheType).build(this)
.doGetAsync(
H().setUrl(url).build(),
new Callback() {
@Override
public void onSuccess(HttpInfo info) throws IOException {
String result = in();
re("FORCE_CACHE:" + result);
setFromCacheTV(info);
}
@Override
public void onFailure(HttpInfo info) throws IOException {
re("FORCE_CACHE:" + in());
}
}
);
}
先网络再缓存
/**
* 先网络再缓存:先请求网络,失败则请求缓存
*/
private void networkThenCache() {
OkH().setCacheType).build(this)
.doGetAsync(
H().setUrl(url).build(),
new Callback() {
@Override
public void onSuccess(HttpInfo info) throws IOException {
String result = in();
re("NETWORK_THEN_CACHE:" + result);
setFromCacheTV(info);
}
@Override
public void onFailure(HttpInfo info) throws IOException {
re("NETWORK_THEN_CACHE:" + in());
}
}
);
}
先缓存再网络
/**
* 先缓存再网络:先请求缓存,失败则请求网络
*/
private void cacheThenNetwork() {
OkH().setCacheType).build(this)
.doGetAsync(
H().setUrl(url).build(),
new Callback() {
@Override
public void onSuccess(HttpInfo info) throws IOException {
String result = in();
re("CACHE_THEN_NETWORK:" + result);
setFromCacheTV(info);
}
@Override
public void onFailure(HttpInfo info) throws IOException {
re("CACHE_THEN_NETWORK:" + in());
}
}
);
}
缓存10秒失效
/**
* 缓存10秒失效:连续点击进行测试10秒内再次请求为缓存响应,10秒后再请求则缓存失效并进行网络请求
*/
private void tenSecondCache(){
//由于采用同一个url测试,需要先清理缓存
if(isNeedDeleteCache){
isNeedDeleteCache = false;
OkH().deleteCache();
}
OkH()
.setCacheType)
.setCacheSurvivalTime(10)//缓存存活时间为10秒
.build(this)
.doGetAsync(
H().setUrl(url).build(),
new Callback() {
@Override
public void onSuccess(HttpInfo info) throws IOException {
String result = in();
re("缓存10秒失效:" + result);
setFromCacheTV(info);
}
@Override
public void onFailure(HttpInfo info) throws IOException {
re("缓存10秒失效:" + in());
}
}
);
}
在Activity中上传图片示例
/**
* 异步上传图片:显示上传进度
*/
private void doUploadImg() {
HttpInfo info = H()
.setUrl(url)
.addUploadFile("file", filePathOne, new ProgressCallback() {
//onProgressMain为UI线程回调,可以直接操作UI
@Override
public void onProgressMain(int percent, long bytesWritten, long contentLength, boolean done) {
u(percent);
LogU(TAG, "上传进度:" + percent);
}
})
.build();
OkH(this).doUploadFileAsync(info);
}
在Activity中单次批量上传文件示例
- 若服务器为php,接口文件参数名称后面追加"[]"表示数组, 示例:builder.addUploadFile("uploadFile[]",path);
/**
* 单次批量上传:一次请求上传多个文件
*/
private void doUploadBatch(){
imgLi();
imgLi("/storage/emulated/0/okHttp_download;);
imgLi("/storage/emulated/0/okHttp_download;);
H builder = H()
.setUrl(url);
//循环添加上传文件
for (String path: imgList ) {
//若服务器为php,接口文件参数名称后面追加"[]"表示数组,示例:builder.addUploadFile("uploadFile[]",path);
builder.addUploadFile("uploadFile",path);
}
HttpInfo info = builder.build();
OkH).doUploadFileAsync(info,new ProgressCallback(){
@Override
public void onProgressMain(int percent, long bytesWritten, long contentLength, boolean done) {
u(percent);
}
@Override
public void onResponseMain(String filePath, HttpInfo info) {
LogU(TAG, "上传结果:" + in());
(in());
}
});
}
在Activity中断点下载文件示例
@OnClick({R.id.downloadBtn, R.id.pauseBtn, R.id.continueBtn})
public void onClick(View view) {
switch ()) {
case R.id.downloadBtn://下载
download();
break;
case R.id.pauseBtn://暂停下载
if(null != fileInfo)
);
break;
case R.id.continueBtn://继续下载
download();
break;
}
}
private void download(){
if(null == fileInfo)
fileInfo = new DownloadFileInfo(url,"fileName",new ProgressCallback(){
@Override
public void onProgressMain(int percent, long bytesWritten, long contentLength, boolean done) {
downloadProgre(percent);
(percent+"%");
LogU(TAG, "下载进度:" + percent);
}
@Override
public void onResponseMain(String filePath, HttpInfo info) {
i()){
(in()+"\n下载状态:"+());
}else{
Toa(DownloadBreakpointsActivity.this,in(),Toa).show();
}
}
});
HttpInfo info = H().addDownloadFile(fileInfo).build();
OkH().setReadTimeout(120).build(this).doDownloadFileAsync(info);
}
二进制流方式请求
HttpInfo info = new H()
.setUrl(";)
.addParamBytes(byte)//添加二进制流
.build();
OkH().doPostAsync(info, new Callback() {
@Override
public void onSuccess(HttpInfo info) throws IOException {
String result = in();
re("请求失败:"+result);
}
@Override
public void onFailure(HttpInfo info) throws IOException {
re("请求成功:"+in());
}
});
请求结果统一预处理拦截器/请求链路异常信息拦截器示例
请求结果拦截器与链路异常拦截器方便项目进行网络请求业务时对信息返回的统一管理与设置
/**
* Http拦截器
* 1、请求结果统一预处理拦截器
* 2、请求链路异常信息拦截器
* @author zhousf
*/
public class HttpInterceptor {
/**
* 请求结果统一预处理拦截器
* 该拦截器会对所有网络请求返回结果进行预处理并修改
*/
public static ResultInterceptor ResultInterceptor = new ResultInterceptor() {
@Override
public HttpInfo intercept(HttpInfo info) throws Exception {
//请求结果预处理:可以进行GSon过滤与解析
return info;
}
};
/**
* 请求链路异常信息拦截器
* 该拦截器会发送网络请求时链路异常信息进行拦截处理
*/
public static ExceptionInterceptor ExceptionInterceptor = new ExceptionInterceptor() {
@Override
public HttpInfo intercept(HttpInfo info) throws Exception {
switch ()){
case H:
in("网络中断");
break;
case H:
in("网络地址错误["+in()+"]");
break;
case H:
in("协议类型错误["+in()+"]");
break;
case H:
in("请检查网络连接是否正常["+in()+"]");
break;
case H:
in("连接超时");
break;
case H:
in("读写超时");
break;
case H:
in("连接中断");
break;
}
return info;
}
};
}
Cookie持久化示例
没有在Application中进行全局Cookie持久化配置时可以采用以下方式:
OkHttpUtilInterface okHttpUtil = OkH()
.setCacheLevel(FIRST_LEVEL)
.setConnectTimeout(25).build(this);
//一个okHttpUtil即为一个网络连接
okHttpUoGetAsync(
H().setUrl(url).build(),
new CallbackOk() {
@Override
public void onResponse(HttpInfo info) throws IOException {
if ()) {
String result = in();
re("异步请求:"+result);
}
}
});
有问题反馈
在使用中有任何问题,欢迎反馈给我,可以用以下联系方式跟我交流
- QQ: 424427633
感激
感谢以下的项目,排名不分先后
- OkHttp
- PersistentCookieJar
相关示例
OkHttpUtil接口
/**
* 网络请求工具接口
* @author zhousf
*/
public interface OkHttpUtilInterface {
/**
* 同步Post请求
* @param info 请求信息体
* @return HttpInfo
*/
HttpInfo doPostSync(HttpInfo info);
/**
* 同步Post请求
* @param info 请求信息体
* @param callback 进度回调接口
* @return HttpInfo
*/
HttpInfo doPostSync(HttpInfo info, ProgressCallback callback);
/**
* 异步Post请求
* @param info 请求信息体
* @param callback 结果回调接口
*/
void doPostAsync(HttpInfo info, BaseCallback callback);
/**
* 异步Post请求
* @param info 请求信息体
* @param callback 进度回调接口
*/
void doPostAsync(HttpInfo info, ProgressCallback callback);
/**
* 同步Get请求
* @param info 请求信息体
*/
HttpInfo doGetSync(HttpInfo info);
/**
* 异步Get请求
* @param info 请求信息体
* @param callback 结果回调接口
*/
void doGetAsync(HttpInfo info, BaseCallback callback);
/**
* 异步上传文件
* @param info 请求信息体
*/
void doUploadFileAsync(final HttpInfo info);
/**
* 批量异步上传文件
* @param info 请求信息体
* @param callback 进度回调接口
*/
void doUploadFileAsync(final HttpInfo info, ProgressCallback callback);
/**
* 同步上传文件
* @param info 请求信息体
*/
void doUploadFileSync(final HttpInfo info);
/**
* 批量同步上传文件
* @param info 请求信息体
* @param callback 进度回调接口
*/
void doUploadFileSync(final HttpInfo info, ProgressCallback callback);
/**
* 异步下载文件
* @param info 请求信息体
*/
void doDownloadFileAsync(final HttpInfo info);
/**
* 同步下载文件
* @param info 请求信息体
*/
void doDownloadFileSync(final HttpInfo info);
/**
* 取消请求
* @param requestTag 请求标识
*/
void cancelRequest(Object requestTag);
/**
* 获取默认的HttpClient
*/
OkHttpClient getDefaultClient();
/**
* 清理缓存:只清理网络请求的缓存,不清理下载文件
*/
boolean deleteCache();
}
MainActivity
/**
* 网络请求:支持同步/异步、GET/POST、缓存请求
*
* @author zhousf
*/
public class MainActivity extends BaseActivity {
@Bind)
TextView fromCacheTV;
@Bind)
TextView resultTV;
/**
* 注意:测试时请更换该地址
*/
private String url = ";appkey=10003&sign=b59bc3ef6191eb9f747dd4e83c99f2a4&format=json";
private boolean isNeedDeleteCache = true;
@Override
protected int initLayout() {
return R.layout.activity_main;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
(savedInstanceState);
}
@Override
protected void onResume() {
();
}
@Override
protected void onStart() {
();
}
@OnClick({
R.id.sync_btn,
R.id.async_btn,
R.id.force_network_btn,
R.id.force_cache_btn,
R.id.network_then_cache_btn,
R.id.cache_then_network_btn,
R.id.ten_second_cache_btn,
R.id.delete_cache_btn
})
public void onClick(View view) {
switch ()) {
case R.id.sync_btn://同步请求
sync();
break;
case R.id.async_btn://异步请求
async();
break;
case R.id.force_network_btn://仅网络
forceNetwork();
break;
case R.id.force_cache_btn://仅缓存
forceCache();
break;
case R.id.network_then_cache_btn://先网络再缓存
networkThenCache();
break;
case R.id.cache_then_network_btn://先缓存再网络
cacheThenNetwork();
break;
case R.id.ten_second_cache_btn://缓存10秒失效
tenSecondCache();
break;
case R.id.delete_cache_btn://清理缓存
deleteCache();
break;
}
}
/**
* 同步请求:由于不能在UI线程中进行网络请求操作,所以采用子线程方式
*/
private void sync() {
new Thread(new Runnable() {
@Override
public void run() {
final HttpInfo info = H()
.setUrl(url)
.setResponseEncoding)//设置服务器响应编码
.build();
OkH(this)
.doGetSync(info);
final String result = in();
runOnUiThread(new Runnable() {
@Override
public void run() {
re("同步请求:" + result);
setFromCacheTV(info);
}
});
}
}).start();
needDeleteCache(true);
}
/**
* 异步请求:回调方法可以直接操作UI
*/
private void async() {
OkH(this).doGetAsync(
H().setUrl(url).build(),
new Callback() {
@Override
public void onFailure(HttpInfo info) throws IOException {
String result = in();
re("异步请求失败:" + result);
}
@Override
public void onSuccess(HttpInfo info) throws IOException {
String result = in();
re("异步请求成功:" + result);
//GSon解析
TimeAndDate time = new Gson().fromJson(result, TimeAndDa);
LogU("MainActivity", ().toString());
setFromCacheTV(info);
}
});
needDeleteCache(true);
}
/**
* 仅网络请求
*/
private void forceNetwork(){
OkH().setCacheType).build(this)
.doGetAsync(
H().setUrl(url).build(),
new Callback() {
@Override
public void onSuccess(HttpInfo info) throws IOException {
String result = in();
re("FORCE_NETWORK:" + result);
setFromCacheTV(info);
}
@Override
public void onFailure(HttpInfo info) throws IOException {
re("FORCE_NETWORK:" + in());
}
}
);
needDeleteCache(true);
}
/**
* 仅缓存请求
*/
private void forceCache(){
OkH().setCacheType).build(this)
.doGetAsync(
H().setUrl(url).build(),
new Callback() {
@Override
public void onSuccess(HttpInfo info) throws IOException {
String result = in();
re("FORCE_CACHE:" + result);
setFromCacheTV(info);
}
@Override
public void onFailure(HttpInfo info) throws IOException {
re("FORCE_CACHE:" + in());
}
}
);
needDeleteCache(true);
}
/**
* 先网络再缓存:先请求网络,失败则请求缓存
*/
private void networkThenCache() {
OkH().setCacheType).build(this)
.doGetAsync(
H().setUrl(url).build(),
new Callback() {
@Override
public void onSuccess(HttpInfo info) throws IOException {
String result = in();
re("NETWORK_THEN_CACHE:" + result);
setFromCacheTV(info);
}
@Override
public void onFailure(HttpInfo info) throws IOException {
re("NETWORK_THEN_CACHE:" + in());
}
}
);
needDeleteCache(true);
}
/**
* 先缓存再网络:先请求缓存,失败则请求网络
*/
private void cacheThenNetwork() {
OkH().setCacheType).build(this)
.doGetAsync(
H().setUrl(url).build(),
new Callback() {
@Override
public void onSuccess(HttpInfo info) throws IOException {
String result = in();
re("CACHE_THEN_NETWORK:" + result);
setFromCacheTV(info);
}
@Override
public void onFailure(HttpInfo info) throws IOException {
re("CACHE_THEN_NETWORK:" + in());
}
}
);
needDeleteCache(true);
}
/**
* 缓存10秒失效:连续点击进行测试10秒内再次请求为缓存响应,10秒后再请求则缓存失效并进行网络请求
*/
private void tenSecondCache(){
//由于采用同一个url测试,需要先清理缓存
if(isNeedDeleteCache){
isNeedDeleteCache = false;
OkH().deleteCache();
}
OkH()
.setCacheType)
.setCacheSurvivalTime(10)//缓存存活时间为10秒
.build(this)
.doGetAsync(
H().setUrl(url).build(),
new Callback() {
@Override
public void onSuccess(HttpInfo info) throws IOException {
String result = in();
re("缓存10秒失效:" + result);
setFromCacheTV(info);
}
@Override
public void onFailure(HttpInfo info) throws IOException {
re("缓存10秒失效:" + in());
}
}
);
}
private void needDeleteCache(boolean delete){
isNeedDeleteCache = delete;
}
private void setFromCacheTV(HttpInfo info){
()?"缓存请求":"网络请求");
}
/**
* 清理缓存
*/
private void deleteCache(){
if(OkH().deleteCache()){
Toa(this,"清理缓存成功");
}else{
Toa(this,"清理缓存失败");
}
}