本文主要讲解okhttp3的使用方法,包括get请求,post请求,文件上传下载。
首先要知道,如果我们不设置缓存,okhttp默认是没有做数据缓存的,需要自己手动添加缓存
1.简单的get请求,异步请求(即直接在主线程发起的请求,okhttp内部实际是开启了子线程处理网络请求,但是回调方法还是在子线程,需要手动切换到主线程更新UI)
//创建okhttp客户端对象
OkHttpClient okHttpClient = new OkHttpClient();
//创建请求对象
Request request = new Request.Builder().get().url(url).build();
//创建回调对象
Call call = okHttpClient.newCall(request);
//发起请求
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.e("result", e.getMessage());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
final String content = response.body().string();
//返回的时候在子线程,需要handler更新UI
runOnUiThread(new Runnable() {
@Override
public void run() {
mTextView.setText(content);
}
});
}
});
同步get请求,主要区别在于创建请求对象时就发起请求,回调方法在发起请求的同一个线程,尽管如此,由于发起网络请求是耗时操作,也需要在子线程执行,更新UI需要切换到主线程
new Thread(new Runnable() {
@Override
public void run() {
//创建客户端对象
OkHttpClient okHttpClient = new OkHttpClient();
//创建请求对象
Request request = new Request.Builder()
.get()
.url(url).build();
//创建响应对象时就发起请求
try {
Response response = okHttpClient.newCall(request).execute();
final String string = response.body().string();
runOnUiThread(new Runnable() {
@Override
public void run() {
mTextView.setText(string);
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
2.post请求,需要添加请求体,可以添加请求头
new Thread(new Runnable() {
@Override
public void run() {
String name = mEtname.getText().toString().trim();
String pasw = mEtpsw.getText().toString().trim();
OkHttpClient okHttpClient = new OkHttpClient();
//创建请求体
/**
* 注意,okhttp3.FormBody instead of FormEncodingBuilder.
* (OkHttp3.x,FormEncodingBuilder已被FormBody取代)
*/
FormBody body = new FormBody.Builder()
.add("name",name)
.add("pasw",pasw)
.build();
//创建请求对象
Request request = new Request.Builder()
.post(body)
.url(url)
.build();
try {
Response response = okHttpClient.newCall(request).execute();
final String string = response.body().string();
runOnUiThread(new Runnable() {
@Override
public void run() {
mTextView.setText(string);
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
okhttp文件上传还比较麻烦,先使用okhttputis进行上传
okhttputuils文件上传
//拿到SD卡路径
File directory = Environment.getExternalStorageDirectory();
//拼接路径
File file = new File(directory, "MobiSafe.apk");
//先使用okhttputils执行文件上传
OkHttpUtils
.postFile()
.url(url)
.file(file)
.build()
.execute(new Callback() {
@Override
public void inProgress(float progress, long total, int id) {
super.inProgress(progress, total, id);
//上传或下载进度的回调,返回的Progress从0到1
mProgressBar.setProgress((int) (progress * 100));
}
@Override
public Object parseNetworkResponse(Response response, int id) throws Exception {
return null;
}
@Override
public void onError(Call call, Exception e, int id) {
mTextView.setText(e.getMessage());
}
@Override
public void onResponse(Object response, int id) {
mTextView.setText("上传成功");
}
});
服务器端使用流的形式直接写入硬盘,这里只是为了测试自己写了个简单的servlet,实际的服务器逻辑要复杂多了。
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
// doGet(request, response);
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
/*String name = request.getParameter("type");
System.out.println(name);*/
PrintWriter writer = response.getWriter();
//创建目录保存上船的图片
File dir = new File("E:/uploadFile");
if(!dir.exists()){
dir.mkdir();
}
String setName = UUID.randomUUID() + ".zip";
File file = new File(dir,setName);
ServletInputStream inputStream = request.getInputStream();
if(inputStream!=null){
FileOutputStream fos = new FileOutputStream(file);
byte[] buffer = new byte[1024];
int len = 0;
while((len=inputStream.read(buffer))!=-1){
fos.write(buffer,0,len);
}
inputStream.close();
fos.close();
writer.write("上傳成功");
System.out.println("上傳成功");
}else{
writer.write("上傳失敗");
}
okhttputils文件下载,设置下载进度时候,需要服务器设置response.setContentLength
String finleName = "meizi.jpg";//保存的文件名
String fileDir = directory.getAbsolutePath();//保存路径
OkHttpUtils
.get()
.url(url)
.addParams("type","downLoad")
.build()
.execute(new FileCallBack(fileDir, finleName) {
@Override
public void inProgress(float progress, long total, int id) {
super.inProgress(progress, total, id);
//如果服务器没有设置长度,输出结果是负数
Log.e("result", "progress=" + progress);
mProgressBar.setProgress((int) (progress*100));
}
@Override
public void onError(Call call, Exception e, int id) {
mTextView.setText(e.getMessage());
}
@Override
public void onResponse(File response, int id) {
mTextView.setText("下载成功!");
}
});
服务器端下载对应的代码
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setCharacterEncoding("utf-8");
ServletOutputStream outputStream = response.getOutputStream();
String type = request.getParameter("type");
if("downLoad".equals(type)){
String file = "E:/uploadFile/src.zip";
FileInputStream fis = new FileInputStream(file);
File calcFile = new File(file);
long length = calcFile.length();
response.setContentLength((int) length);
byte[] buffer = new byte[1024];
int len = 0;
while((len=fis.read(buffer))!=-1){
outputStream.write(buffer,0,len);
}
fis.close();
outputStream.close();
System.out.println("下載成功");
}else{
System.out.println("沒有下載");
}
3.OKHttp文件上传
好了,理解okhttputils的上传下载方式,现在来看okhttp上传,以表单的形式
private void uploadByOk(final File directory) {
new Thread(new Runnable() {
@Override
public void run() {
//要上传的文件
File file = new File(directory, "src.zip");
//创建客户端对象
OkHttpClient okHttpClient = new OkHttpClient();
//指定上传的文件类型,application/octet-stream指的是二进制流,可以上传任意类型
Request request = getFileRequest(url, file, null);
//创建回调对象
Call call = okHttpClient.newCall(request);
call.enqueue(new okhttp3.Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.e("result", e.getMessage());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
final String string = response.body().string();
runOnUiThread(new Runnable() {
@Override
public void run() {
mTextView.setText("成功");
}
});
}
});
}
}).start();
}
public static Request getFileRequest(String url,File file,Map<String, String> maps){
//设置为表单的形式提交
MultipartBody.Builder builder= new MultipartBody.Builder().setType(MultipartBody.FORM);
//这个name="file",相当于表单的name=file,在服务器上通过request.getPart("file");然后通过part.write(fileName)写到文件中
// 传入这个键才能得到对应part的文件
//这个搞了我一个晚上,操!
if(maps==null){
//如果是参数,则通过addFormDataPart添加 如果是文件,则通过addPart添加
//图片的格式"image/png",如果是流的格式application/octet-stream
builder.addPart( Headers.of("Content-Disposition", "form-data; name=\"file\";filename=\"file.jpg\""),
RequestBody.create(MediaType.parse("application/octet-stream"),file)
).build();
}else{
for (String key : maps.keySet()) {
//这里相当于添加参数
builder.addFormDataPart(key, maps.get(key));
}
builder.addPart( Headers.of("Content-Disposition", "form-data; name=\"file\";filename=\"file.jpg\""),
RequestBody.create(MediaType.parse("application/octet-stream"),file)
);
}
RequestBody body=builder.build();
return new Request.Builder().url(url).post(body).build();
}
4.okhttp下载
private void downLoadByOK(final File directory) {
//用okhttp下载
new Thread(new Runnable() {
@Override
public void run() {
//直接在url上添加参数
String newurl = url + "?type=downLoad";
//创建客户端对象
OkHttpClient okHttpClient = new OkHttpClient();
//创建请求对象
Request request = new Request.Builder()
.get()
.url(newurl)
.build();
//创建回调对象
Call call = okHttpClient.newCall(request);
call.enqueue(new okhttp3.Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.e("result", "结果:" + e.getMessage());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
InputStream inputStream = response.body().byteStream();
long lenth = response.body().contentLength();
Log.e("result", "长度=" + lenth);
File file = new File(directory, "src.zip");
FileOutputStream fos = new FileOutputStream(file);
byte[] buffer = new byte[1024];
int len = 0;
int progress = 0;
while ((len = inputStream.read(buffer)) != -1) {
fos.write(buffer, 0, len);
progress = progress + len;
mProgressBar.setProgress((int) (progress * 100 / lenth));
}
inputStream.close();
fos.close();
}
});
}
}).start();
对比Okhttputils和okhttp,其实Okhttputils是对okhttp的封装,所以使用上okhttputils会比较方便省事,但自定义的内容相对较少了。比如下载时不能自定义每次读取的缓存数组容量,就是byte[ ]数组。
6.设置缓存
虽然OKHttp支持缓存,但默认不会帮我们设置缓存,所以需要我们自己手动设置,这也是合理的,因为okhttp不知道我们要怎么缓存,缓存空间多大,这些需要我们自己实现。
服务器支持缓存
如果服务器支持缓存,请求返回的Response会带有这样的Header:Cache-Control, max-age=xxx,这种情况下我们只需要手动给okhttp设置缓存就可以让okhttp自动帮你缓存了。这里的max-age的值代表了缓存在你本地存放的时间,可以根据实际需要来设置其大小。
-
首先我们要提供了一个文件路径用来存放缓存,出于安全性的考虑,在Android中我们推荐使用Context.getCacheDir()来作为缓存的存放路径,另外还需要指定缓存的大小就可以创建一个缓存了。如下所示:
public Cache provideCache() { return new Cache(mContext.getCacheDir(), 10240*1024); }
-
创建了这个缓存后我们还需要将其设置到okttpClient对象里面:
OkHttpClient okHttpClient = new OkHttpClient(); OkHttpClient newClient = okHttpClient.newBuilder() .cache(cache) .connectTimeout(20, TimeUnit.SECONDS) .readTimeout(20, TimeUnit.SECONDS) .build();
服务器不支持缓存
- 如果服务器不支持缓存就可能没有指定这个头部,或者指定的值是如no-store等,但是我们还想在本地使用缓存的话要怎么办呢?这种情况下我们就需要使用Interceptor来重写Respose的头部信息,从而让okhttp支持缓存。
-
如下所示,我们重写的Response的Cache-Control字段
public class CacheInterceptor implements Interceptor { @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); Response response = chain.proceed(request); Response response1 = response.newBuilder() .removeHeader("Pragma") .removeHeader("Cache-Control") //cache for 30 days .header("Cache-Control", "max-age=" + 3600 * 24 * 30) .build(); return response1; } }
-
然后将该Intercepter作为一个NetworkInterceptor加入到okhttpClient中
OkHttpClient okHttpClient = new OkHttpClient(); OkHttpClient newClient = okHttpClient.newBuilder() .addNetworkInterceptor(new CacheInterceptor()) .cache(cache) .connectTimeout(20, TimeUnit.SECONDS) .readTimeout(20, TimeUnit.SECONDS) .build();
这样我们就可以在服务器不支持缓存的情况下使用缓存了。
为了使用同一个okhttp对象,可以在Application中设置全局单例
//创建客户端
okHttpClient = new OkHttpClient();
-
然后在Protocol类中根据需要是否设置缓存
package org.skxy.www.okhttpdemo; import android.os.Handler; import android.os.Message; import android.support.annotation.NonNull; import android.util.Log; import java.io.File; import java.io.IOException; import java.util.concurrent.TimeUnit; import okhttp3.Cache; import okhttp3.Call; import okhttp3.Callback; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; /** * ClassName : MyTaskProtocol * Created by: skxy on 2016/10/19. * DES :网络请求类 */ public class MyTaskProtocol implements Runnable { public static final int SUCCESS = 1; public static final int FAIL = 0; private boolean isCache = true; private long maxSize = 10*1024*1024;//设置缓存为10M private final String url; public MyTaskProtocol(String url,OnNetworkResultListener mListener,boolean isCache) { this.url = url; this.mListener = mListener; this.isCache = isCache; } //访问网络的方法 public void getDatas(String url) { //为了实现全局单例,将客户端对象放在application中初始化 OkHttpClient okHttpClient = MyApplication.getClient(); //创建请求对象 Request request = new Request.Builder() .get() .url(url) .build(); //可以直接创建响应对象发起请求,同步请求 /* try { Response response =okHttpClient.newCall(request).execute(); response.body().string(); } catch (IOException e) { e.printStackTrace(); }*/ //根据是否需要做缓存创建回调对象,有些需要实事更新的页面不需要缓存 Call call; if (isCache){ //有设置缓存 OkHttpClient client = getOkHttpCacheClient(okHttpClient); call = client.newCall(request); }else {//没有设置缓存 call = okHttpClient.newCall(request); } //发起请求 call.enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { Message msg = Message.obtain(); msg.what = FAIL; msg.obj = e.getMessage(); mHandler.sendMessage(msg); Log.e("result", "onResponse: 错误返回" ); } @Override public void onResponse(Call call, Response response) throws IOException { Message msg = Message.obtain(); msg.what = SUCCESS; msg.obj = response.body().string(); mHandler.sendMessage(msg); Log.e("result", "onResponse: 成功返回" ); } }); } @NonNull private OkHttpClient getOkHttpCacheClient(OkHttpClient okHttpClient) { //设置缓存 File file = MyApplication.getContext().getCacheDir(); Cache cache = new Cache(file, maxSize); //设置缓存到客户端 OkHttpClient client = okHttpClient.newBuilder() .addNetworkInterceptor(new CacheInterceptor()) .cache(cache) .connectTimeout(10, TimeUnit.SECONDS) .readTimeout(10, TimeUnit.SECONDS) .build(); return client; } /** * 创建handler将返回的数据推送到主线程 */ Handler mHandler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); String result = (String) msg.obj; int what = msg.what; switch (what) { case FAIL: mListener.onFail(result); break; case SUCCESS: mListener.onSuccess(result); break; } } }; /** * 创建回调监听,获取网络返回的数据 */ private OnNetworkResultListener mListener; public interface OnNetworkResultListener { void onFail(String result);//失败回调 void onSuccess(String result);//成功回调 } /* public void setOnNetworkResultListener(OnNetworkResultListener listener) { this.mListener = listener; }*/ /** * 执行网络请求 */ @Override public void run() { getDatas(url); } }