Android网络框架OkHttp之get请求(源码初识)

一身转战三千里,一剑曾当百万师。这篇文章主要讲述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请求例子【Android网络框架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
这个例子网上说烂了,没啥说的,来看下结果 
Android网络框架OkHttp之get请求(源码初识)

文章图片

很漂亮的样子。呵呵
异步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 = "https://www.songbingjia.com/android/连接超时"; } }

  • 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 = "https://www.songbingjia.com/android/连接超时"; } }

  • 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。 
    首先我们来看一张简单的模型图
Android网络框架OkHttp之get请求(源码初识)

文章图片

从这张图我们可以看出,最左边为经典的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 { //

    推荐阅读