以下代码基于 OkHttp 3.6.0 版本且后台为 Java,基本使用 OkHttp 的场景如下:
先说下一般的请求编写的步骤:
1. 构造 Request 实例,其中 url 为必须的,其他选项在链式操作中添加。
2. 构造 RequestBody ( get 请求时则无)
3. 获得 OkHttpClient 实例,然后根据 Request 实例 构造出 Call 实例
4. 提交执行 Call 任务(可异步、同步),然后在回调中处理自己的逻辑
直接来看代码把,需要注意是这边为了方便,就使用了全局的 OkHttpClient 实例。另外服务端代码会在最后贴出。
public static final String BASE_URL = "http://xx.xx.xx.xx:8080/OkHttpServer/";
// cookieJar 等后面的操作是为了 session 保持
// 这边仅是保存在内存中,当然也可以持久化
private OkHttpClient okHttpClient = new OkHttpClient.Builder().cookieJar(new CookieJar() {
private final HashMap> cookieStore = new HashMap<>();
@Override
public void saveFromResponse(HttpUrl url, List cookies) {
cookieStore.put(url.host(), cookies);
}@Override
public List loadForRequest(HttpUrl url) {
List cookies = cookieStore.get(url.host());
return cookies != null ? cookies : new ArrayList();
}
}).build();
get 请求
客户端代码:
private void doGet() {
// 构造的请求
Request request = new Request.Builder()
.url(BASE_URL + "login?username=ss&password=123")
.build();
final Call call = okHttpClient.newCall(request);
// 异步请求方式
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {}@Override
public void onResponse(Call call, Response response) throws IOException {
System.out.println("response body = " + response.body().string());
}
});
// 同步请求
/*// 由于同步,将不可在主线程中调用,耗时操作将阻塞主线程
new Thread(new Runnable() {
@Override
public void run() {
try {
Response response = call.execute();
System.out.println("response body = " + response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
*/}
客户端接收的 Response
I/System.out: response body = login success!username=sspassword = 123
说明:
同步与异步请求的方式均已写出,但异步请求中的匿名类 CallBack 中的 onResponse 回调方法,仍然不是处于主线程中,这边为了偷懒而直接将信息打印在控制台中。因此在 onResponse 中若有操作 UI ,记得要切回 UI 线程!
post 请求
private void doPost() {
// 表单构造,携带参数
FormBody body = new FormBody.Builder()
.add("username", "Jack")
.add("password", "123")
.build();
Request request = new Request.Builder()
.url(BASE_URL + "login") // 对比 get 请求
.post(body)
.build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {}@Override
public void onResponse(Call call, Response response) throws IOException {
System.out.println("response body = " + response.body().string());
}
});
}
客户端接收的 Response
I/System.out: response body = login success!username=Jackpassword = 123
post 上传文件
private void doPostFile() {
File file = new File(Environment.getExternalStorageDirectory(), "testupload.apk");
RequestBody requestBody = RequestBody.create(MediaType.parse("application/octet-stream"), file);
Request request = new Request.Builder()
.url(BASE_URL + "postFile")
.post(requestBody)
.build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {}@Override
public void onResponse(Call call, Response response) throws IOException {
System.out.println("response body = " + response.body().string());
}
});
说明:这边的 RequestBody 的构造通过
RequestBody.create(MediaType.parse("application/octet-stream"), file);
由抽象类 RequestBody 中多个重载的 create 方法可知,比如想要仅仅上传给服务端一个字符串,可以写成 RequestBody requestBody = RequestBody.create(MediaType.parse("text/plain"), "server!can you see me?");
具体类型可查看 create 源码。表单上传,且获得进度反馈
经常性的需求是上传给服务端普通文件/图片也好,需要反馈给用户实时的上传进度。
private void postMultipart() {
// 表单提交包括用户名、密码和一个文件
MultipartBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM) // multipart/form-data 类型的表单提交
.addFormDataPart("username", "Jack")
.addFormDataPart("password", "123")
.addFormDataPart("file", "multipar.apk", RequestBody.create(MediaType.parse("application/octet-stream")
, new File(Environment.getExternalStorageDirectory(), "testupload.apk")))
.build();
// 包装 RequestBody
CountingRequestBody body = new CountingRequestBody(requestBody, new CountingRequestBody.Listener() {
@Override
public void onProcessUpload(long byteWritten, long contentLength) {
// 已写出的 byte 长度除以文件总的 byte 数
System.out.println(((float) byteWritten) / contentLength * 100 + "%");
}
});
Request request = new Request.Builder()
.url(BASE_URL + "postMultipart")
.post(body)
.build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {}@Override
public void onResponse(Call call, Response response) throws IOException {
System.out.println("response body = " + response.body().string());
}
});
}public class CountingRequestBody extends RequestBody {// 真正的包含数据的 RequestBody
private RequestBody delegate;
private Listener mListener;
private CountingSink countingSink;
public CountingRequestBody(RequestBody delegate, Listener mListener) {
this.delegate = delegate;
this.mListener = mListener;
}@Override
public MediaType contentType() {
return delegate.contentType();
}@Override
public long contentLength() throws IOException {
try {
return delegate.contentLength();
} catch (Exception e) {
return -1;
}
}@Override
public void writeTo(BufferedSink sink) throws IOException {countingSink = new CountingSink(sink);
BufferedSink bufferedSink = Okio.buffer(countingSink);
delegate.writeTo(bufferedSink);
bufferedSink.flush();
}interface Listener {
void onProcessUpload(long byteWritten, long contentLength);
}protected final class CountingSink extends ForwardingSink {private long byteWritten;
public CountingSink(Sink delegate) {
super(delegate);
}@Override
public void write(Buffer source, long byteCount) throws IOException {
super.write(source, byteCount);
byteWritten += byteCount;
mListener.onProcessUpload(byteWritten, contentLength());
}
}}
可参看 hongyang 大神的 Android网络框架-OkHttp使用
多文件上传
private void postMultiFile() {
MultipartBody.Builder builder = new MultipartBody.Builder();
builder.setType(MultipartBody.FORM);
builder.addFormDataPart("username", "Jack");
builder.addFormDataPart("password", "123");
// 虽然上传的同一个文件,但后台确实收到了上传了 3 次不同命名的 apk 文件
// 这里仅是为了方便起见,改了文件名 i + "multipart.apk"
for (int i = 0;
i < 3;
i++) {
builder.addFormDataPart("files", i + "multipart.apk", RequestBody.create(MediaType.parse("application/octet-stream")
, new File(Environment.getExternalStorageDirectory(), "testupload.apk")));
}
// 一个 xml
builder.addFormDataPart("files", "jay.xml", RequestBody.create(MediaType.parse("application/octet-stream")
, new File(Environment.getExternalStorageDirectory(), "jay.xml")));
MultipartBody body = builder.build();
Request request = new Request.Builder()
.url(BASE_URL + "postMultipleFile")
.post(body)
.build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {}@Override
public void onResponse(Call call, Response response) throws IOException {
System.out.println("response body = " + response.body().string());
}
});
}
可参看 Upload multiple files at once to a Struts2 @Action
下载文件
// 为了简单方便,文件直接给的是 url 服务端文件路径,而不涉及接口调用
private void downloadImage() {
Request request = new Request.Builder()
.url(BASE_URL + "files/author_avatar.png")
.build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {}@Override
public void onResponse(Call call, Response response) throws IOException {// 若对图片有其他的要求,再额外进行设置
final Bitmap bitmap = BitmapFactory.decodeStream(response.body().byteStream());
// 仅是展示出来
runOnUiThread(new Runnable() {
@Override
public void run() {
imageView.setImageBitmap(bitmap);
}
});
// 当然既然可以拿到 response.body().byteStream() 的输入流
// 所以就将文件可以保存到手机上
//FileOutputStream fos = new FileOutputStream(new File(
//Environment.getExternalStorageDirectory(), "author_avatar.png"));
//InputStream inputStream = response.body().byteStream();
//int len = 0;
//byte[] buffer = new byte[1024];
//while ((len = inputStream.read(buffer)) != -1) {
//fos.write(buffer, 0, len);
//}
//fos.flush();
//fos.close();
//inputStream.close();
}
});
}
说明:这边是以将输入流转为 Bitmap 展示图片为例而已,实际上拿到了 response.body().byteStream() 的输入流要很容易通过 IO 保存起来了。
服务端代码:
public class UserAction extends ActionSupport {private String username;
private String password;
private File mPhoto;
private String mPhotoFileName;
private List files;
private List filesFileName;
public void login() {
System.out.println("username = " + username + "password = "
+ password);
HttpServletResponse response = ServletActionContext.getResponse();
try {
PrintWriter writer = response.getWriter();
writer.write("login success!username=" + username + "password = "
+ password);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}}/**
* 获取客户端传来的字符串,再返回给客户端
*/
public void postString() {try {
System.out.println("postString is running");
HttpServletResponse response = ServletActionContext.getResponse();
HttpServletRequest request = ServletActionContext.getRequest();
InputStream is = request.getInputStream();
StringBuffer sb = new StringBuffer();
int len = 0;
byte[] buffer = new byte[1024];
while ((len = is.read(buffer)) != -1) {
sb.append(new String(buffer, 0, len));
}
PrintWriter writer = response.getWriter();
writer.write("your string is = " + sb.toString());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}/**
* 响应客户端上传的文件
*/
public void postFile() {
System.out.println("postFile is runnning");
HttpServletResponse response = ServletActionContext.getResponse();
try {
HttpServletRequest request = ServletActionContext.getRequest();
InputStream is = request.getInputStream();
String dir = ServletActionContext.getServletContext().getRealPath(
"files");
File path = new File(dir);
if (!path.exists()) {
path.mkdirs();
}
FileOutputStream fos = new FileOutputStream(new File(path,
"12306.apk"));
int len = 0;
byte[] buffer = new byte[1024];
while ((len = is.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
fos.flush();
fos.close();
is.close();
PrintWriter writer = response.getWriter();
writer.write("upload file success!");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}/**
* 相应客户端多文件上传
*/
public void postMultipleFile() {
// 检验是否上传的是多个文件,size 为正确
System.out.println("files size = " + files.size());
String dir = ServletActionContext.getServletContext().getRealPath(
"files");
File path = new File(dir);
if (!path.exists()) {
path.mkdirs();
}
try {
// 根据文件名保存文件
for (int i = 0;
i < files.size();
i++) {
File file = new File(path, filesFileName.get(i));
FileUtils.copyFile(files.get(i), file);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}}/**
* 响应客户端上传的用户名、密码和一个文件
*/
public void postMultipart() {
System.out.println("username = " + username + "password = "
+ password);
System.out.println("postMultipart is runnning");
HttpServletResponse response = ServletActionContext.getResponse();
try {
String dir = ServletActionContext.getServletContext().getRealPath(
"files");
File path = new File(dir);
if (!path.exists()) {
path.mkdirs();
}
File file = new File(path, mPhotoFileName);
FileUtils.copyFile(mPhoto, file);
PrintWriter writer = response.getWriter();
writer.write("your username = " + username + "password = "
+ password);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}public void downloadFile() {
System.out.println("invoke downloadfile");
HttpServletResponse response = ServletActionContext.getResponse();
File file = new File(ServletActionContext.getServletContext()
.getRealPath("files"), "12306.apk");
try {
FileInputStream fis = new FileInputStream(file);
OutputStream os = response.getOutputStream();
int len = 0;
byte[] buffer = new byte[1024];
while ((len = fis.read(buffer)) != -1) {
os.write(buffer, 0, len);
}
os.flush();
os.close();
fis.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}public String getUsername() {
return username;
}public void setUsername(String username) {
this.username = username;
}public String getPassword() {
return password;
}public void setPassword(String password) {
this.password = password;
}public File getmPhoto() {
return mPhoto;
}public void setmPhoto(File mPhoto) {
this.mPhoto = mPhoto;
}public String getmPhotoFileName() {
return mPhotoFileName;
}public void setmPhotoFileName(String mPhotoFileName) {
this.mPhotoFileName = mPhotoFileName;
}public List getFiles() {
return files;
}public void setFiles(List files) {
this.files = files;
}public List getFilesFileName() {
return filesFileName;
}public void setFilesFileName(List filesFileName) {
this.filesFileName = filesFileName;
}}
参考:
Android OkHttp完全解析 是时候来了解OkHttp了
【Android 网络框架之 OkHttp 基础使用】Android网络框架-OkHttp使用
推荐阅读
- 多渠道|Android 组件化在公用Module里实现多渠道打包配置
- Android基础|EventBus源码分析之订阅-发布模型
- Android基础|Android中Spinner下拉列表(使用ArrayAdapter和自定义Adapter实现)
- HandlerThread(子线程也可以有消息传递机制)
- android ViewId自动注解使用详解(ViewInject)
- Android|Android属性动画 Property animation
- JNI与底层调用-1
- #|FutureTask 使用场景介绍
- #|静态内部类创建单例的实现和优点
- #|View的三大流程是什么,加以简单说明