以前说到OKHttp网络请求支持两种方式:同步请求和异步请求,同时又存在get和post请求,那么就是2*2,一共四种状况,接下来就分别介绍下这四种请求的使用和区别html
在gradle中指定Java版本java
compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 }
说到这里,须要补一点,关于Android 9.0的http限制:默认使用加密链接,Android P禁止 App 使用全部未加密的链接,所以 Android P 系统不管是接收或者发送流量,都不能明码传输,须要使用下一代(Transport Layer Security)传输层安全协议,若是在Android P中使用http地址,将会出现以下错误(这里使用的url为http://www.baidu.com
)
W/System.err: java.net.UnknownServiceException: CLEARTEXT communication to www.baidu.com not permitted by network security policy
为了解决这个问题,常见的处理方式有三种:android
<?xml version="1.0" encoding="utf-8"?> <network-security-config> <base-config cleartextTrafficPermitted="true" /> </network-security-config>
android:networkSecurityConfig="@xml/network_security_config"
private void synGetRequest() { new Thread(new Runnable() { @Override public void run() { OkHttpClient mClient = new OkHttpClient.Builder() .callTimeout(5, TimeUnit.SECONDS) //设置超时时间 .build(); //构建OkHttpClient对象 Request request = new Request.Builder() .url("http://www.baidu.com") //请求url .get() //默认请求方式,写不写都是get .build(); //建立Request对象 Call call = mClient.newCall(request); //构建Call对象 try { Response response = call.execute(); //获得Response对象 if(response.isSuccessful()) { Log.e(TAG, "response code ==> " + response.code()); Log.e(TAG, "response message ==> " + response.message()); Log.e(TAG, "response res ==> " + response.body().string()); }else { Log.e(TAG, "response fail"); } } catch (IOException e) { e.printStackTrace(); } } }).start(); }
此时获得的log以下(省略了html的一长串内容):web
D/NetworkSecurityConfig: Using Network Security Config from resource network_security_config debugBuild: true E/MainActivity: response code ==> 200 E/MainActivity: response message ==> OK E/MainActivity: response res ==> <!DOCTYPE html><!--STATUS OK--><html> ··· </html>
使用注意:
因为同步请求是阻塞线程的(call.execute()
),因此在使用的时候须要在子线程中运行,同时response.body().string()
其本质上是对流的操做,也是须要在子线程中进行。另外,返回码中的code是在http中定义的,遵行http协议。因为response.body().string()
的本质是对流进行操做,因此,在调用的时候,只会获得一次的返回值,再次调用会返回null,其根本缘由是客户端发出请求,服务端作出响应,将数据写入到流中,客户端获得返回值,再次调用时服务端并无在流中写入数据,此时客户端就没有数据,获得的就为null。json
private void synPostRequest() { new Thread(new Runnable() { @Override public void run() { OkHttpClient mClient = new OkHttpClient.Builder() .callTimeout(5, TimeUnit.SECONDS) //设置超时时间 .build(); //构建OkHttpClient对象 MediaType JSON = MediaType.parse("application/json; charset=utf-8"); //数据类型为json格式, String json = "{\"username\":\"chen\"}"; //json数据 RequestBody body = RequestBody.create(JSON, json); //建立RequestBody对象 Request request = new Request.Builder() .url("http://www.baidu.com") //请求url .post(body) //post请求方式,须要传入RequestBody对象 .build(); //建立Request对象 Call call = mClient.newCall(request); //构建Call对象 try { Response response = call.execute(); //获得Response对象 if (response.isSuccessful()) { Log.e(TAG, "response code ==> " + response.code()); Log.e(TAG, "response message ==> " + response.message()); Log.e(TAG, "response res ==> " + response.body().string()); } else { Log.e(TAG, "response fail"); } } catch (IOException e) { e.printStackTrace(); } } }).start(); }
post与get同步请求的区别在与post是须要带入请求体的,这也和get与post请求的本质区别有关。
post须要构建出请求体,从而完成post请求。安全
同步请求方法总结:网络
OkHttpClient
和Request
对象Request
封装成Call
对象Call
的execute()
方法发送同步请求private void asyGetRequest() { OkHttpClient mClient = new OkHttpClient.Builder() .callTimeout(5, TimeUnit.SECONDS) //设置超时时间 .build(); //构建OkHttpClient对象 Request request = new Request.Builder() .url("https://www.baidu.com") .get() .build(); Call call = mClient.newCall(request); call.enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { //请求失败回调 Log.e(TAG, "onFailure ===> " + e.getMessage()); } @Override public void onResponse(Call call, Response response) throws IOException { //请求成功回调 Log.e(TAG, "onResponse ===> " + response.body().string()); } }); }
使用注意:
回调接口位于子线程,即call.enqueue()
会将网络请求放在子线程中执行,说到这,就该知道UI更新什么的该怎么使用了吧。app
RequestBody
对象传入post()
,从而完成Request
对象的构建,其余也就没啥区别了。异步请求方法总结:异步
OkHttpClient
和Request
对象Request
封装成Call
对象Call
的enqueue()
方法进行异步请求public Builder post(RequestBody body) { return method("POST", body); }
能够看到传入的参数为:RequestBody
,那么这是个什么玩意儿呢?
其实就是请求体,再看看怎么构建,查看源代码会发现有五种静态构建方法ide
RequestBody.create(MediaType, String) RequestBody.create(MediaType, ByteString) RequestBody.create(MediaType, byte[]) RequestBody.create(MediaType, byte[], int, int) RequestBody.create(MediaType, File)
其源代码以下
public abstract class RequestBody { ··· /** * Returns a new request body that transmits {@code content}. If {@code contentType} is non-null * and lacks a charset, this will use UTF-8. */ public static RequestBody create(@Nullable MediaType contentType, String content) { Charset charset = UTF_8; if (contentType != null) { charset = contentType.charset(); if (charset == null) { charset = UTF_8; contentType = MediaType.parse(contentType + "; charset=utf-8"); } } byte[] bytes = content.getBytes(charset); return create(contentType, bytes); } /** Returns a new request body that transmits {@code content}. */ public static RequestBody create( final @Nullable MediaType contentType, final ByteString content) { return new RequestBody() { @Override public @Nullable MediaType contentType() { return contentType; } @Override public long contentLength() throws IOException { return content.size(); } @Override public void writeTo(BufferedSink sink) throws IOException { sink.write(content); } }; } /** Returns a new request body that transmits {@code content}. */ public static RequestBody create(final @Nullable MediaType contentType, final byte[] content) { return create(contentType, content, 0, content.length); } /** Returns a new request body that transmits {@code content}. */ public static RequestBody create(final @Nullable MediaType contentType, final byte[] content, final int offset, final int byteCount) { if (content == null) throw new NullPointerException("content == null"); Util.checkOffsetAndCount(content.length, offset, byteCount); return new RequestBody() { @Override public @Nullable MediaType contentType() { return contentType; } @Override public long contentLength() { return byteCount; } @Override public void writeTo(BufferedSink sink) throws IOException { sink.write(content, offset, byteCount); } }; } /** Returns a new request body that transmits the content of {@code file}. */ public static RequestBody create(final @Nullable MediaType contentType, final File file) { if (file == null) throw new NullPointerException("file == null"); return new RequestBody() { @Override public @Nullable MediaType contentType() { return contentType; } @Override public long contentLength() { return file.length(); } @Override public void writeTo(BufferedSink sink) throws IOException { try (Source source = Okio.source(file)) { sink.writeAll(source); } } }; } }
经常使用的RequestBody
有json与file,那么具体怎么构建呢,如下举出两个例子
JSON格式上传
MediaType JSON = MediaType.parse("application/json; charset=utf-8"); //数据类型为json格式 String json = "{\"username\":\"chen\"}"; RequestBody body = RequestBody.create(JSON, json);
FILE格式上传
MediaType fileType = MediaType.parse("File/*"); //数据类型为file格式 File file = new File("/sdcard/test.txt"); RequestBody body = RequestBody.create(fileType, file);
同时查看源代码还发现,RequestBody
有两个实现类,FormBody
与MultipartBody
,这两个是OkHttp对RequestBody
的实现,前者用于传递键值对参数,后者用于传递复杂参数。
例如使用FormBody
传递键值对
RequestBody body = new FormBody.Builder() //建立表单请求 .add("username","chen") .add("from","PC") .build();
使用MultipartBody
的例子
MultipartBody multipartBody = new MultipartBody.Builder() .setType(MultipartBody.FORM) .addFormDataPart("title", "title") //键值对 .addFormDataPart("file", file.getName(), RequestBody.create(MediaType.parse("file/*"), file)) //文件 .build();
这里须要说明一点,OkHttp并无实现文件的下载功能,但咱们能够拿到流,那么也就是说能够将流转化为文件保存,也就实现了文件下载功能。