OkHttp从使用到源代码分析(2)-请求的使用方法
之前说到OKHttp网络请求支持两种方式:同步请求和异步请求,同时又存在get和post请求,那么就是2*2,一共四种情况,接下来就分别介绍下这四种请求的使用和区别
在gradle中指定Java版本
compileOptions {sourceCompatibility JavaVersion.VERSION_1_8targetCompatibility 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
为了解决这个问题,常见的处理方式有三种:
- APP改用https请求
- targetSdkVersion 降到27(Android 8.1)以下
- 在 res 下新增一个 xml 目录,然后创建配置文件。再在application中添加进来
<?xml version="1.0" encoding="utf-8"?>
<network-security-config><base-config cleartextTrafficPermitted="true" />
</network-security-config>
android:networkSecurityConfig="@xml/network_security_config"
同步请求
- get同步请求
private void synGetRequest() {new Thread(new Runnable() {@Overridepublic 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的一长串内容):
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。
- post同步请求
private void synPostRequest() {new Thread(new Runnable() {@Overridepublic 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()
方法发送同步请求
异步请求
- get异步请求
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() {@Overridepublic void onFailure(Call call, IOException e) { //请求失败回调Log.e(TAG, "onFailure ===> " + e.getMessage());}@Overridepublic void onResponse(Call call, Response response) throws IOException { //请求成功回调Log.e(TAG, "onResponse ===> " + response.body().string());}});
}
使用注意:
回调接口位于子线程,即call.enqueue()
会将网络请求放在子线程中执行,说到这,就该知道UI更新什么的该怎么使用了吧。
- post异步请求
嗯~~~,这个和上面的get异步请求差不多,区别在于post请求需要构建RequestBody
对象传入post()
,从而完成Request
对象的构建,其他也就没啥区别了。
异步请求方法总结:
- 创建
OkHttpClient
和Request
对象 - 将
Request
封装成Call
对象 - 调用
Call
的enqueue()
方法进行异步请求
- post参数说明
查看post的方法
public Builder post(RequestBody body) {return method("POST", body);
}
可以看到传入的参数为:RequestBody
,那么这是个什么玩意儿呢?
其实就是请求体,再看看怎么构建,查看源代码会发现有五种静态构建方法
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并没有实现文件的下载功能,但我们可以拿到流,那么也就是说可以将流转化为文件保存,也就实现了文件下载功能。
热门开源项目源代码分析导航
OkHttp从使用到源代码分析(2)-请求的使用方法相关推荐
- struts2请求过程源代码分析
struts2请求过程源代码分析 Struts2是Struts社区和WebWork社区的共同成果.我们甚至能够说,Struts2是WebWork的升级版.他採用的正是WebWork的核心,所以.Str ...
- BT源代码学习心得(八):跟踪服务器(Tracker)的代码分析(用户请求的实际处理) - 转贴自 wolfenstein (NeverSayNever)
BT源代码学习心得(八):跟踪服务器(Tracker)的代码分析(用户请求的实际处理) author: wolfenstein 通过上一次的分析,我们已经知道了Tracker采用http协议和客户端通 ...
- Android应用程序进程启动过程的源代码分析(1)
Android应用程序框架层创建的应用程序进程具有两个特点,一是进程的入口函数是ActivityThread.main,二是进程天然支持Binder进程间通信机制:这两个特点都是在进程的初始化过程中实 ...
- AFNetworking 源代码分析
关于其他 AFNetworking 源代码分析的其他文章: AFNetworking 概述(一) AFNetworking 的核心 AFURLSessionManager(二) 处理请求和响应 AFU ...
- 区块链教程Fabric1.0源代码分析scc(系统链码)
区块链教程Fabric1.0源代码分析scc(系统链码),2018年下半年,区块链行业正逐渐褪去发展之初的浮躁.回归理性,表面上看相关人才需求与身价似乎正在回落.但事实上,正是初期泡沫的渐退,让人们更 ...
- HBase源代码分析之MemStore的flush发起时机、推断条件等详情(二)
在<HBase源代码分析之MemStore的flush发起时机.推断条件等详情>一文中,我们具体介绍了MemStore flush的发起时机.推断条件等详情.主要是两类操作.一是会引起Me ...
- CASSINI源代码分析
CASSINI源代码分析 2004-11-10 http://blog.csdn.net/shanhe/ 为什么要分析CASSINI? Cassini(卡西尼)是asp.net上的一个开源项目.主要给 ...
- KVM虚拟机源代码分析
1,KVM结构及工作原理 1.1 KVM结构 KVM基本结构有两部分组成.一个是KVM Driver ,已经成为Linux 内核的一个模块.负责虚拟机的创建,虚拟内存的分配,虚拟CPU寄存器的读写以 ...
- MyBatis架构设计及源代码分析系列(一):MyBatis架构
一.概述 MyBatis并不是一个完整的ORM框架,其官方首页是这么介绍自己 The MyBatis data mapper framework makes it easier to use a re ...
最新文章
- oracle pl/sql 程序设计 历史笔记整理
- html5游戏指尖跟随,图片跟随手指滑动
- 深度学习实践:计算机视觉_深度学习与传统计算机视觉技术:您应该选择哪个?
- drive es 软件兼容_某知名软件被完美修改!对不住了!
- OpenResty+Lua+redis+mysql实现高性能高可用限流缓存
- Linux下的iscsi(设备的共享服务)
- 深度神经网络基本问题的原理详细分析和推导
- scala type关键字用于起别名
- docker 容器中设置 mysql lampp php软链接
- flashcs3java_Flash CS3组件开发图文教程
- 技术人 | 浅谈如何成为技术一号位?
- MAX6299MTT在CPLD上的应用
- OpenGL ES EGL eglDestroyContext
- 英语笔记(计算机词汇,翻译/写作)
- C++对于文件的操作(4)——更改文件指定位置的数据(在新旧数据长度不一致的情况下)
- 9495 删除线性表中所有值为x的元素
- tensorflow与python交互系列,tf.py_function()、tf.py_func、tf.numpy_function()(一)
- 866数据结构重点内容
- 计算机联锁 组合布置图,车站计算机联锁论文
- Monty Python(蒙提·派森)的成员简介