写在前面
本文承接https://www.cnblogs.com/wushenjiang/p/12937857.html,接着上文继续学习。
前几篇博客我们主要学习了使用java原生网络编程来进行一些经典的操作。现在我们使用OKhttp将之前的操作再重新操作一遍。
OkHttp优点
- 1.请求同一主机的时候,一般是指同一域名,支持共享同一个socket.
- 2.通过连接池减少请求延时
- 3.传输通过GZIP压缩,减少下载内容的体积
- 4.用缓存的方式避免重复的请求
可以看到,OkHttp其实是对原生http请求的一个简单封装,并做了额外的一些工作让我们的请求访问更加简单通畅。
OkHttp入门
首先要导入依赖,我们可以使用AndroidStudio或者去github搜索然后copy依赖地址都可以。
首先我们就来用OkHttp来进行一个最基本的异步get请求:
public void getRequest(View v) {
//要有客户端,类似我们要有一个浏览器
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.connectTimeout(10000, TimeUnit.MILLISECONDS)
.build();
//创建请求内容
Request request = new Request.Builder()
.get()
.url(BASE_URL + "/get/text")
.build();
//用client去创建请求任务
Call task = okHttpClient.newCall(request);
//异步请求
task.enqueue(new Callback() {
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
Log.d(TAG, "onFailure... -->" + e);
}
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
int code = response.code();
if (code == HttpURLConnection.HTTP_OK) {
Log.d(TAG, "code -->" + code);
ResponseBody body = response.body();
Log.d(TAG, "body -->" + body.string());
}
}
});
}
可以看到,使用OkHttp的封装使得我们相比最基础的java原生网络编程要方便的多,我们只需要按照步骤来就可以很简单的使用网络请求了。当然我们还需要设置网络权限,详情可以看上上篇博客。
OkHttp的异步Post请求
上面学习了异步Get请求,那我们再学习一下异步Post请求吧:
//先有client
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.connectTimeout(10000, TimeUnit.MILLISECONDS)
.build();
//要提交的内容
CommentItem commentItem = new CommentItem("234134123", "我是评论内容...哈哈");
Gson gson = new Gson();
String jsonStr = gson.toJson(commentItem);
MediaType mediaType = MediaType.parse("application/json");
RequestBody requestBody = RequestBody.create(jsonStr, mediaType);
Request request = new Request.Builder()
.post(requestBody)
.url(BASE_URL + "/post/comment")
.build();
//用client去创建任务
Call task = okHttpClient.newCall(request);
//异步请求
task.enqueue(new Callback() {
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
Log.d(TAG, "onFailure -->" + e.toString());
}
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
int code = response.code();
Log.d(TAG, "code -->" + code);
if (code == HttpURLConnection.HTTP_OK) {
ResponseBody body = response.body();
if (body != null) {
Log.d(TAG, "result -->" + body.string());
}
}
}
});
比起Get请求,我们多了封装请求内容的部分代码。仔细观察我们可以看到,这部分代码很简单:首先我们new一个请求对象出来,然后通过Gson将其转换成json类型字符串,并标志为json类型即可放入请求头。其余的部分我们就发现基本是一致的。
至于带参数的get和post请求,我们只需要在url里拼接字符串即可,这里不再过多的叙述了。
Post方式文件上传
我们先来贴代码:
public void postFile(View v) {
//先有client
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.connectTimeout(10000, TimeUnit.MILLISECONDS)
.build();
File file = new File("/storage/emulated/0/Android/data/com.androidlearing.androidnetworkdemo/files/Pictures10.png");
MediaType fileType = MediaType.parse("image/png");
RequestBody fileBody = RequestBody.create(file, fileType);
RequestBody requestBody = new MultipartBody.Builder()
.addFormDataPart("file", file.getName(), fileBody)
.build();
//创建请求内容
Request request = new Request.Builder()
.url(BASE_URL + "/file/upload")
.post(requestBody)
.build();
//创建请求任务
Call task = okHttpClient.newCall(request);
//异步执行
task.enqueue(new Callback() {
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
Log.d(TAG, "onFailure -->" + e.toString());
}
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
int code = response.code();
Log.d(TAG, "code ==>" + code);
if (code == HttpURLConnection.HTTP_OK) {
ResponseBody body = response.body();
if (body != null) {
String string = body.string();
Log.d(TAG, "result -->" + string);
}
}
}
});
}
可以看到,我们的代码和上面的post请求并无太大差别,同样需要设置一个请求头。但这里我们由于是文件,就用到了RequestBody的实现类MultipartBody建造者模式下的addFormDataPart方法即可。
具体也很简单,构造一下即可。
多文件下载
上面我们注意到是addFormDataPart方法,一般add都是可以添加很多内容的,我们点进去方法看看注解:
我们可以看到,这里明显是把Part类进行了foreach循环,然后拼接起来了。说明我们的猜想没有错。我们可以直接在拼接请求头时添加多个文件:
public void postFiles(View v) {
//先有client
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.connectTimeout(10000, TimeUnit.MILLISECONDS)
.build();
File fileOne = new File("/storage/emulated/0/Android/data/com.androidlearing.androidnetworkdemo/files/Pictures10.png");
File fileTwo = new File("/storage/emulated/0/Download/rBsADV64HDWAI6i_AAhJfxL8eXE287.png");
File fileThree = new File("/storage/emulated/0/Download/rBsADV64ILeAfwQMAAdBpy-0H04021.png");
File fileFour = new File("/storage/emulated/0/Download/shop-ad.png");
MediaType fileType = MediaType.parse("image/png");
RequestBody fileOneBody = RequestBody.create(fileOne, fileType);
RequestBody fileTwoBody = RequestBody.create(fileTwo, fileType);
RequestBody fileThreeBody = RequestBody.create(fileThree, fileType);
RequestBody fileFourBody = RequestBody.create(fileFour, fileType);
RequestBody requestBody = new MultipartBody.Builder()
.addFormDataPart("files", fileOne.getName(), fileOneBody)
.addFormDataPart("files", fileTwo.getName(), fileTwoBody)
.addFormDataPart("files", fileThree.getName(), fileThreeBody)
.addFormDataPart("files", fileFour.getName(), fileFourBody)
.build();
//创建请求内容
Request request = new Request.Builder()
.url(BASE_URL + "/files/upload")
.post(requestBody)
.build();
//创建请求任务
Call task = okHttpClient.newCall(request);
//异步执行
task.enqueue(new Callback() {
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
Log.d(TAG, "onFailure -->" + e.toString());
}
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
int code = response.code();
Log.d(TAG, "code ==>" + code);
if (code == HttpURLConnection.HTTP_OK) {
ResponseBody body = response.body();
if (body != null) {
String string = body.string();
Log.d(TAG, "result -->" + string);
}
}
}
});
}
可以看到除了拼接头部部分其余部分基本一模一样。
这里拼接请求头,和上面基本一样,改个名字即可。
下载文件
我们先看核心部分:
private void downloadFile(InputStream inputStream, Headers headers) throws IOException {
for (int i = 0; i < headers.size(); i++) {
String key = headers.name(i);
String value = headers.value(i);
Log.d(TAG, key + " ==" + value);
}
String contentType = headers.get("Content-disposition");
String fileName = contentType.replace("attachment; filename=", "");
File outFile = new File(OkhttpActivity.this.getExternalFilesDir(Environment.DIRECTORY_PICTURES) + File.separator + fileName);
if (!outFile.getParentFile().exists()) {
outFile.mkdirs();
}
if (!outFile.exists()) {
outFile.createNewFile();
}
FileOutputStream fos = new FileOutputStream(outFile);
byte[] buffer = new byte[1024];
int len;
while ((len = inputStream.read(buffer, 0, buffer.length)) != -1) {
fos.write(buffer, 0, len);
}
fos.flush();
IOUtils.ioClose(fos);
IOUtils.ioClose(inputStream);
}
可以看到,下载文件其实就是获取filename和路径并且将二进制文件进行解析写入的过程。整体其实和我们在java原生网络编程的使用并无太大差别。
下面是全部文件下载的代码:
public void downFile(View v) {
//先有client
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.connectTimeout(10000, TimeUnit.MILLISECONDS)
.build();
Request request = new Request.Builder()
.get()
.url(BASE_URL + "/download/15")
.build();
final Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
Log.d(TAG, "onFailure -->" + e.toString());
}
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
int code = response.code();
Log.d(TAG, "code -->" + code);
if (code == HttpURLConnection.HTTP_OK) {
downloadFile(response.body().byteStream(),response.headers());
}
}
});
总结
可以看到,使用OkHttp极大的减少了我们的重复代码,但可以看到封装的还是不够彻底。接下来我们学习一个二次封装的框架:Retrofit.