您的位置:首页 > 理论基础 > 计算机网络

Android 网络框架之 OkHttp 基础使用

2017-03-14 15:54 501 查看
以下代码基于 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<String, List<Cookie>> cookieStore = new HashMap<>();

@Override
public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
cookieStore.put(url.host(), cookies);
}

@Override
public List<Cookie> loadForRequest(HttpUrl url) {
List<Cookie> cookies = cookieStore.get(url.host());
return cookies != null ? cookies : new ArrayList<Cookie>();
}
}).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=ss  password = 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=Jack  password = 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<File> files;
private List<String> 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<File> getFiles() {
return files;
}

public void setFiles(List<File> files) {
this.files = files;
}

public List<String> getFilesFileName() {
return filesFileName;
}

public void setFilesFileName(List<String> filesFileName) {
this.filesFileName = filesFileName;
}

}


参考:

Android OkHttp完全解析 是时候来了解OkHttp了

Android网络框架-OkHttp使用
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息