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请求(源码初识)的主要内容,如果未能解决你的问题,请参考以下文章

Android编程入门--开源框架OKHttp

深度探索!Android之OkHttp网络架构源码解析

Android之OkHttp网络架构源码深入分析(揭开神秘的面纱)

Android网络请求框架—OKHttp 源码解析

Android网络请求框架之Okhttp3.0 详细使用

Android网络请求框架之Okhttp3.0 详细使用