Android网络框架OkHttp之get请求(源码初识)
Posted 星辰
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android网络框架OkHttp之get请求(源码初识)相关的知识,希望对你有一定的参考价值。
概括
OkHttp现在很火呀。于是上个星期就一直在学习OkHttp框架,虽然说起来已经有点晚上手了,貌似是2013年就推出了。但是现在它版本更加稳定了呀。这不,说着说着,OkHttp3.3版本在这几天又发布了。以下以OkHttp3.2版本为准,没办法,上个星期看的时候还是以3.2为最新版本的。首先,我们要先了解一些背景,OkHttp这个框架是有Square公司推出的,进入官网。如果想看API,点击进入API。大概了解了OkHttp之后,我们应该知道OkHttp是一个网络框架,想想以前在开发中,网络框架一般用的是什么?很快我们就会想到刚学习Android开发的时候接触的HttpURLConnection和Apache提供的HttpClient这两个类,然后就是后面推出的一些第三方网络框架,比如2013年google推出的Volley框架、android-async-http框架、2014年很火的Xutils、以及现在很多人用的Retrofit等等。这么多,到底选哪个?一开始我也晕。后来看了一些资料,似乎懂了一个概念:OkHttp是用来替换HttpURLConnection的,据说android4.4源码的HttpURLConnection就替换成了OkHttp。所以我们别拿OkHttp和这些网络框架比,这些网络框架也只是基于HttpURLConnection进行一些封装,使我们的代码更加简洁方便。懂了这点,我们应该就懂了为什么网上那么多OkHttp和Volley或者Retrofit等等这些框架结合使用了,其实是一个道理。那么我用的HttpUrlConnection或者HttpClient用的好好的,干嘛要用你的OkHttp?这里就来比较下HttpURLConnection和OkHttp。至于HttpClient嘛,android6.0已经把它的API废除了。用它还要引入org.apache.http.legacy.jar包,不值得,而且okhttp也已经提供了对应的okhttp-apache 模块。
HttpURLConnection和OkHttp的比较
- HttpURLConnection有的API,OkHttp基本上都有(你有我有全都有呀,哈哈哈)
- HttpURLConnection和OkHttp都支持Https,流的上传和下载,超时,IP6、连接池等等
-
OkHttp比HttpURLConnection具有更好的同步异步请求、缓存机制,支持HttpDNS、重定向、Gzip压缩,平台适应性、很好的服务器IP的转换、直接Socket通信,支持拦截器等等。
看到这么多机制,是不是觉得很强大,通过Socket直接通信,以及很好的缓存机制,Gzip对于Http头部的压缩传输。自然对于网络请求这块使应用更加省流量、请求的更快。OkHttp对于Https和HttpDNS的支持,使得应用的网络通信安全性更高。当然说了它的好,现在也来说说它的
不好之处 - OkHttp不支持优先级请求
- OkHttp不支持自签名证书
- OkHttp header中不能传中文
虽然是不好的地方,但是OkHttp已经比较成熟了,网上解决这几个问题的资料也很多了。所以这些都不是问题。
一个简单的Get请求例子
这里我们就以经典的官网提供的Get请求的例子来学习下,说大概的代码。
- 先在manifest加个网络权限,养成良好习惯
<uses-permission android:name="android.permission.INTERNET"/>
- 1
- 1
然后在build.gradle文件的dependencies添加库如下:
dependencies {
compile \'com.squareup.okhttp3:okhttp:3.2.0\'
compile \'com.squareup.okio:okio:1.7.0\'
}
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
同步Get请求:
final OkHttpClient okHttpClient = new OkHttpClient()
.newBuilder()
.build();
final Request request = new Request.Builder()
.url("https://www.publicobject.com/helloworld.txt")
.header("User-Agent","OkHttp Example")
.build();
new Thread(new Runnable() {
@Override
public void run() {
try {
Response response = okHttpClient.newCall(request).execute();
Log.d("zgx","response====="+response.body().string());
response.body().close();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
这个例子网上说烂了,没啥说的,来看下结果
很漂亮的样子。呵呵
异步Get请求:
修改上面部分代码,Call类调用enqueue方法。代码如下:
new Thread(new Runnable() {
@Override
public void run() {
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.d("zgx","response====="+e.getMessage());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
Log.d("zgx","response====="+response.body().string());
response.body().close();
}
});
}
}).start();
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
运行结果和上面一样。只是多了一个CallBack
其实还有Post请求,文件上传下载,图片加载,拦截器的使用,支持session的保持这里就先不说了。以后有时间再学习下。下面就是简单的来看下他的源码,只是以个人理解来分析下,看源码前先来了解一些基本的知识。
一些基本的知识
-
Http
Http是一种基于TCP/IP连接的一套网络通信协议,它是一种一应一答的请求,它分为Get和Post请求,Get请求获取得是静态页面,它可以把参数放在URL字符串后面。而Post请求就不同了,它是把参数放在Http请求的正文的。
Get请求我们会这样请求:
private void HttpURLConnection_Get(){
try{
//通过openConnection 连接
URL url = new java.net.URL(URL);
urlConn=(HttpURLConnection)url.openConnection();
//设置输入和输出流
urlConn.setDoOutput(true);
urlConn.setDoInput(true);
//关闭连接
urlConn.disconnect();
}catch(Exception e){
resultData = "连接超时";
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
然后把获取到的urlConn连接的数据通过IO流把读取出来:
InputStreamReader in = new InputStreamReader(urlConn.getInputStream());
BufferedReader buffer = new BufferedReader(in);
String inputLine = null;
while (((inputLine = buffer.readLine()) != null)){
resultData += inputLine + "\\n";
}
System.out.println(resultData);
in.close();
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
Post请求则会这样:
private void HttpURLConnection_Post(){
try{
//通过openConnection 连接
URL url = new java.net.URL(URL_Post);
urlConn=(HttpURLConnection)url.openConnection();
//设置输入和输出流
urlConn.setDoOutput(true);
urlConn.setDoInput(true);
urlConn.setRequestMethod("POST");
urlConn.setUseCaches(false);
// 配置本次连接的Content-type,配置为application/x-www-form-urlencoded的
urlConn.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
// 连接,从postUrl.openConnection()至此的配置必须要在connect之前完成,
// 要注意的是connection.getOutputStream会隐含的进行connect。
urlConn.connect();
//DataOutputStream流
DataOutputStream out = new DataOutputStream(urlConn.getOutputStream());
//要上传的参数
String content = "par=" + URLEncoder.encode("ylx_Post+中正", "UTF_8");
//将要上传的内容写入流中
out.writeBytes(content);
//刷新、关闭
out.flush();
out.close();
}catch(Exception e){
resultData = "连接超时";
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
然后同上把获取到的urlConn连接的数据通过IO流把读取出来,大概的代码就是这样。
- HTTPS
HTTP加入SSL即是HTTPS,它安全性更高,HTTP使用得端口号是80,而HTTPS使用的端口号是443。HTTP协议以明文方式发送内容,不提供任何方式的数据加密。HTTPS协议一般需要到CA申请证书,当然也可以自签名证书,和12306一样。这里就说下HTTPS的核心SSL和TLS。
首先我们来看一张简单的模型图
从这张图我们可以看出,最左边为经典的ISO7层模型图,右边我们可以看到有一个SSL层,它又叫做安全套捷字层,它分为SSL记录协议和SSL握手协议。SSL位于传输层和应用层之间,其中SSL记录 层协议位于传输层协议之上,而SSL握手协议又在SSL记录协议之上。SSL记录协议可以为高层协议进行加密,压缩,封装等功能,而SSL握手协议进行的是身份认证,协商加密算法、交换加密密钥等。其中TLS和SSL类似,它建立在SSL3.0协议之上。主要的不同在于他们的加密算法不同,其他功能作用类似。想要详情看他们的区别,请看这篇文章SSL与TLS的区别以及介绍。
基础基本上讲完了,现在就来说说OkHttp涉及到的一些知识了。支持SPDY协议和HTTP2.0协议,同步和异步请求,拦截机制,请求和响应的逻辑处理,缓存机制,重连和重定向机制,连接池,Gzip压缩,安全性,平台适应性等等。下面我们就来通过源码来一步步的学习。
源码分析:
- 支持哪些协议
既然是与服务器之间的通信协议,我们应该会想到Protocol这个类,这个类是干嘛的呢?它主要是配置与远程服务器的通信协议。既然这样,那我们去源码找下这个类。其实在OkHttpClient源码里面第一个属性我们就可以看到
private static final List<Protocol> DEFAULT_PROTOCOLS = Util.immutableList(
Protocol.HTTP_2, Protocol.SPDY_3, Protocol.HTTP_1_1);
- 1
- 2
- 1
- 2
再进去Protocol类
public enum Protocol {
HTTP_1_0("http/1.0"),
HTTP_1_1("http/1.1"),
SPDY_3("spdy/3.1"),
HTTP_2("h2");
//省略部分代码
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
- 4
- 5
- 6
- 7
进入这个类,我们发现,这是一个枚举类,它定义了一些和远程服务器通信的协议名称,如上面四种。
然后回到OkHttpClient这个类,跟踪protocols这个属性,我们会找到这个方法:
public Builder protocols(List<Protocol> protocols) {
protocols = Util.immutableList(protocols);
if (!protocols.contains(Protocol.HTTP_1_1)) {
throw new IllegalArgumentException("protocols doesn\'t contain http/1.1: " + protocols);
}
if (protocols.contains(Protocol.HTTP_1_0)) {
throw new IllegalArgumentException("protocols must not contain http/1.0: " + protocols);
}
if (protocols.contains(null)) {
throw new IllegalArgumentException("protocols must not contain null");
}
this.protocols = Util.immutableList(protocols);
return this;
}
public Builder connectionSpecs(List<ConnectionSpec> connectionSpecs) {
this.connectionSpecs = Util.immutableList(connectionSpecs);
return this;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
我们会发现,OkHttp是支持http/1.1版本的,但是不支持http/1.0版本的协议,支持h2协议,以及spdy/3.1协议。而且协议名称不能为null。既然支持h2,就说明服务端支持 ALPN的,它将可以协商到协商到 HTTP/2。这个很好呀,好在哪里呢,我们可以看下这篇文章,为什么我们应该尽快支持 ALPN?
- 同步和异步请求
这个其实上面已经说到过了,OkHttp可以通过调用execute()实现同步请求,然后通过enqueue()方法可以实现异步请求。从上面的请求代码我们可以知道,它是通过okHttpClient.newCall(request)得到一个Call对象来调用的。那现在我们就先来看下Call这个类。
public interface Call {
//初始化这个请求并且返回这个请求
Request request();
//同步方法
Response execute() throws IOException;
//异步方法
void enqueue(Callback responseCallback);
//取消请求,完成的请求则不能取消
void cancel();
//省略部分代码
interface Factory {
Call newCall(Request request);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
它是一个接口类,里面包含同步方法和异步方法,我们还可以看到定义了一个内部接口Factory ,实现这个接口,通过newCall方法返回Call对象,也就是上面我们调用的OkHttpClient.newCall(Request request),因为OkHttpClient对象已经实现这个接口。那么我们就回到OkHttpClient对象里面的newCall(Request request)方法里面。
@Override
public Call newCall(Request request) {
return new RealCall(this, request);
}
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
创建了一个RealCall对象,那么同步异步方法肯定就在这里面实现了,继续来看看,RealCall实现了Call接口,果然是在这里,接下来看同步和异步方法。
同步方法:
@Override
public Response execute() throws IOException {
//同步操作,如果这个请求已经请求完了,则直接抛异常返回
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
try {
//以上是关于Android网络框架OkHttp之get请求(源码初识)的主要内容,如果未能解决你的问题,请参考以下文章