文章目录

    • 0x01 Android 上的网络框架
        • 1、HttpClient
        • 2、Asynchronous HttpClient
        • 3、AFinal
        • 4、HttpURLConnection
        • 5、xUtils
        • 6、Volley
        • 7、OkHttp
        • 8、Retrofit
    • 0x02 Android 8.1 的网络框架
        • 1、构造对象
        • 2、建立连接
        • 3、关闭连接

0x01 Android 上的网络框架

Android一路走来的网络框架变更【网络协议:HTTP】

  • Android 2.2-:HttpClient
  • Android 2.3+:HttpURLConnection
  • Android 3.0:HttpURLConnection 增加了对 HTTPS 的支持
  • Android 4.0:HttpURLConnection 增加了对缓存的支持
  • Android 4.4+:HttpURLConnectio 底层引入OkHttp框架
  • Android 5.0+:OkHttp

在Android 2.2版本之前,HttpClient拥有较少的bug,因此使用它是最好的选择。而在Android 2.3版本及以后,HttpURLConnection则是最佳的选择。它的API简单,体积较小,因而非常适用于Android项目。压缩和缓存机制可以有效地减少网络访问的流量,在提升速度和省电方面也起到了较大的作用。对于新的应用程序应该更加偏向于使用HttpURLConnection,因为在以后的工作当中我们也会将更多的时间放在优化HttpURLConnection上面。

1、HttpClient

高效稳定、维护成本高

用法:

  1. 创建 DefaultHttpClient 实例(HttpClient 是接口类)
  2. 创建 HttpGet/HttpPost 对象 request,传入目标网络地址
  3. 如果是 HttpPost 对象,还需要通过 setEntity() 方法设置请求参数
  4. 调用 client 实例的 execute() 方法,传入 request 对象,返回 HttpResponse 对象
  5. 通过 response 对象的 getStatusLine() 方法再调用 getStatusCode() 获取状态码
  6. 通过 response 对象的 getEntity() 方法获得 HttpEntity 对象,再通过 EntityUtils.toString() 方法解析返回内容

2、Asynchronous HttpClient

基于 Apache HttpClient

请求在子线程中完成,在请求线程中返回回调

使用了线程池

使用 RequestParams 类封装了请求参数

持久化 cookies 到 SharedPreferences

支持文件上传

支持 json

支持 Http Basic Auth

用法:

  1. 实例化一个 AsynHttpClient 对象 client
  2. 编写一个静态 HttpClient 类,封装了 get 和 post 方法
  3. get/post 方法中分别调用了 client 的 get/post 方法,传入网址和参数,返回 AsyncHttpResponseHandler 回调
  4. 如果需要保存 cookie,实例化 PersistentCookieStore 类对象 cookieStore,并通过 client 的 setCookieStore() 方法设置
  5. 或者自己定义一个 BasicClientCookie 对象,通过 setXXX() 方法设置完属性,再通过 cookieStore 的 addCookie() 方法添加
  6. 最后在回调函数中做状态码和返回内容的处理

3、AFinal

基于 HttpClient(网络层实现为 finalHTTP,是 HttpClient 的再次封装)

开源的 ORM 和 IOC 应用开发框架

小巧灵活、代码入侵量少

4、HttpURLConnection

轻便灵活易扩展

支持 Https、缓存

用法:

  1. 实例化一个 URL 对象,传入目标网络地址
  2. 获取 HttpUrlConnection 实例,通过 URL 对象的 openConnection() 方法获取
  3. 设置 http 请求的连接属性,如 setRequestMethod() 设置请求方法,setConnectTimeout() 设置超时时间
  4. 通过 getResponseCode() 获取状态码
  5. 通过 getInputStream() 获取服务器返回的输入流
  6. 读取内容并解析
  7. 通过 disconnect() 方法关闭当前连接

注意添加网络访问权限

5、xUtils

3.0- 基于 HttpClient,3.0+ 基于 HttpURLConnection

基于 Afinal 开发的,目前功能比较完善的开源框架

6、Volley

Android 开发团队2013年推出的一个网络通信框架,Android 2.2- 使用 HttpClient, Android 2.3+ 使用 HttpURLConnection

通信封装,支持加载图片,性能优化,适合数据量不大、通信频繁的网络

优势在于处理小文件的 Http 请求

支持处理高分辨率的图像压缩

支持使用 Okhttp 作为传输层

NetworkImageView 在 GC 的使用模式上更加保守,在请求清理上也更加积极

用法:

  1. 创建一个 RequestQueue 对象
  2. 创建一个 StringRequest 对象,传入网址,响应消息的监听 Listener 和 错误监听 ErrorListener
  3. 如果是 post 方法,还需要传入方法 Request.Method.POST,并重写 getParams() 方法
  4. 如果是加载图片,则使用 ImageRequest 来创建 request 对象,传入参数分别为网址、消息监听、图片最大宽度和高度、显示比例、颜色属性、错误监听
  5. 通过 add() 方法将 StringRequest 对象添加到 RequestQueue 对象中
  6. 在 onResponse(String response) 回调中处理响应消息或在 onErrorResponse(VolleyError error) 回调中处理错误

还可以使用 ImageLoader 来实现对图片的更多处理,如缓存、过滤,用法:

  1. 创建一个 RequestQueue 对象
  2. 创建一个 ImageLoader 对象 loader,传入请求队列、ImageCache 实例化对象
  3. ImageCache 是接口类,需要具体实现并重载 getBitmap() 和 putBitmap() 方法
  4. 调用 loader 的 get() 方法加载图片,传入网址和 ImageListener 对象
  5. ImageListener 对象通过 ImageLoader 的 getImageListener() 方法获取,传入参数为显示图片的 ImageView 对象,加载中和加载失败需要显示的图片资源
  6. 如果需要对图片大小进行限制,重载 get() 方法即可

自定义 Request,用法:

  1. 继承自 Volley 的 Request 类,实现构造方法,并重写 parseNetworkResponse 和 deliverResponse 方法

7、OkHttp

Java 的 Http + SPDY 客户端开发包,支持 Android 2.3+

高效,支持 SPDY、连接池、Gzip、Http 缓存

自动处理网络问题,如二次连接、SSL 握手

Android 4.4 开始 HttpURLConnection 底层实现采用 OkHttp

用法:

  1. 创建一个 OkHttpClient 对象 client
  2. 创建一个 Request 对象,通过 Request.Builder 的 url()、get()、post() 设置网址、请求方式和参数,通过 build() 方法返回对象
  3. 调用 client 对象的 newCall() 方法传入 request,然后调用 execute() 执行同步网络请求,或 enqueue() 执行异步请求,并在回调中处理结果

8、Retrofit

性能最好,处理最快

适用于 REST API

传输层默认使用 OkHttp

支持 NIO

默认使用 Gson

用法:

  1. 将请求地址转为接口,通过注解配置请求头部、方法、参数、返回值等信息
  2. 创建一个 Retrofit 对象,通过 Retrofit.Builder 的 baseUrl() 等方法设置地址等信息,再通过 build() 返回对象
  3. 调用 retrofit 对象的 create() 方法返回接口类实例化对象
  4. 通过接口类调用对应方法,获取 Call 实例,调用 Call 对象的 execute() 或 enqueue() 执行同步或异步网络请求

0x02 Android 8.1 的网络框架

Android 8.1 使用的网络框架为 HttpURLConnection,其底层实现依赖于 OkHttp

一个典型的 HttpURLConnection 网络请求过程为:

HttpURLConnection connection = null;InputStream inputStream = null;try {// 1. 实例化一个 URL 对象,传入目标网络地址    URL url = new URL(urlPath);    // 2. 通过 URL 对象的 openConnection() 方法获取 HttpUrlConnection 实例    connection = (HttpURLConnection) url.openConnection();// 3. 设置 http 请求的连接属性    connection.setRequestMethod("GET");    connection.setUseCaches(false);    connection.setConnectTimeout(3000);    connection.setReadTimeout(5000);    connection.setDoInput(true);    // 4. 通过 getResponseCode() 获取状态码    if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {        // 5. 通过 getInputStream() 获取服务器返回的输入流      inputStream = connection.getInputStream();            // 6. 读取内容并解析        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();        byte[] bytes = new byte[1024*512];        int length = 0;        while ((length = inputStream.read(bytes)) > -1) {        byteArrayOutputStream.write(bytes, 0, length);    }    return byteArrayOutputStream.toString();} catch (Exception e) {Log.e(TAG, "catch Exception = " + e.getMessage());e.printStackTrace();} finally {if (connection != null) {      // 7. 通过 disconnect() 方法关闭当前连接      connection.disconnect();      connection = null;    }if (inputStream != null) {      try {          inputStream.close();          inputStream = null;        } catch (IOException e) {          Log.e(TAG, "catch IOException = " + e.getMessage());          e.printStackTrace();        }    }}   

分析具体流程前,首先捋一下源码的路径和一些类继承关系

HttpURLConnection 相关源码对应不同版本的路径:

  • Android 7.0 - 路径为 /libcore/luni/src/main/java/java/net/
  • Android 7.0 + 路径为 /libcore/ojluni/src/main/java/java/net/
  • okhttp 的相关源码一直在 /external/okhttp/ 路径下

相关类的继承关系:

接下来具体分析代码调用流程:

1、构造对象

首先通过 URL 类的构造函数初始化 URL 类实例,然后调用这个实例的 openConnection() 方法,实际上调用了 openConnection(Proxy proxy)​,返回 URLConnection 类型对象。

URL 的 openConnection(Proxy proxy) 里面会调用 h a n d l e r . o p e n C o n n e c t i o n ( t h i s , p ) handler.openConnection(this, p) handler.openConnection(this,p), 其中的 handler 是 URLStreamHandler 类型的对象,通过 g e t U R L S t r e a m H a n d l e r ( S t r i n g p r o t o c o l ) getURLStreamHandler(String protocol) getURLStreamHandler(Stringprotocol) 设置,URLStreamHandler 是一个抽象类, o p e n C o n n e c t i o n ( U R L u , P r o x y p ) openConnection(URL u, Proxy p) openConnection(URLu,Proxyp) 的真正实现在其实现子类中,看一下 URLStreamHandler 是如何设置的

  1. 根据协议查找一个 URLStreamHandler 的哈希表;

  2. 通过 URLStreamHandlerFactory 对象的 createURLStreamHandler() 方法创建;

    【这个方法里面重写了 URLStreamHandler 类的 openConnection 方法,并且规定了 http/https 协议的默认端口 80/443】

  3. 拿到系统变量 java.protocol.handler.pkgs 的值通过 classloader 去查找对应类,然后去创建类实例;

  4. 根据不同的协议去创建不同的类实例

    if (protocol.equals("file")) {handler = new sun.net.www.protocol.file.Handler();} else if (protocol.equals("ftp")) {handler = new sun.net.www.protocol.ftp.Handler();} else if (protocol.equals("jar")) {handler = new sun.net.www.protocol.jar.Handler();} else if (protocol.equals("http")) {handler = (URLStreamHandler)Class.forName("com.android.okhttp.HttpHandler").newInstance();} else if (protocol.equals("https")) {handler = (URLStreamHandler)Class.forName("com.android.okhttp.HttpsHandler").newInstance();}

注意,这里 Android 源码才用了 jarjar-rules,即将以一个路径开头的包在编译时打包成另一个路径开头的包,所以 com.android.okhttp.HttpHandler 对应的包应该是 com.squareup.okhttp.HttpHandler,https 的同理,所以 URLStreamHandler 的子类实现分别在 HttpHandler 和 HttpsHandler 中

HttpsHandler 继承自 HttpHandler,openConnection(URL url, Proxy proxy) 的实现是调用了 newOkUrlFactory(proxy) 创建一个 OkUrlFactory 对象并调用它的 open(url) 方法。newOkUrlFactory() 里面首先调用 createHttpOkUrlFactory(proxy) 创建一个 OkUrlFactory 对象,然后通过调用这个对象的 client() 获取成员对象 OkHttpClient 的实例,接着通过调用 OkHttpClient 实例的 setConnectionPool() 去设置连接池。在 createHttpOkUrlFactory(proxy) 里面首先创建一个 OkHttpClient 对象,并且设置超时、重定向、流量加密这些属性,然后传入 client 参数创建一个 OkUrlFactory 对象并返回。

OkUrlFactory 的 open(url) 方法会调用 open(url, client.getProxy()),这里的 client 就是 OkHttpClient 对象。在 open(URL url, Proxy proxy) 中,首先 url.getProtocol() 获取协议,是 http 还是 https,然后设置 client 的 proxy,最后根据协议返回相应的实现类实例,HttpURLConnectionImpl 或 HttpsURLConnectionImpl。

URL#openConnection() 最终会返回 HttpURLConnectionImpl 或 HttpsURLConnectionImpl 对象

2、建立连接

HttpURLConnection 是一个抽象类,定义了许多配置连接属性方法,定义了获取相应消息码和响应消息的方法,定义了 disconnect()、usingProxy() 抽象方法,还定义了返回消息码,大致分为

2XX: generally "OK"3XX: relocation/redirect4XX: client error5XX: server error

在建立连接的过程,HttpURLConnection 没有显式调用 connect(),而是直接去调用 getResponseCode() 方法,而 getResponseCode() 里面会调用 getInputStream(),getInputStream() 方法中会间接实现了 connect()

看下 getInputStream() 这个方法,首次在 URLConnection 中定义,在 HttpURLConnectionImpl 重写实现。这个方法里面首先调用 getResponse() 返回类型为响应消息的 HttpEngine 对象,然后通过此对象的 getResponse() 获取返回消息,然后调用 body() 获取消息体,最后调用 byteStream() 返回字节流格式。

HttpEngine 的 getResponse() 方法返回的是一个 Response 对象 userResponse(还有一个 cacheResponse 作用不详),Response 的 body() 方法会返回 ResponseBody 对象。

重点看下 getResponse() 如何返回 HttpEngine 对象的。这个方法里面首先调用 initHttpEngine() 初始化 HttpEngine 对象,接着调用 hasResponse() 判断这个 HttpEngine 对象是否有响应消息,若有则直接将此对象返回,否则会循环调用 execute(true),然后分别通过 httpEngine 的 getResponse() 方法获取 Response 对象和 followUpRequest() 方法获取 Request 对象,并调用 newHttpEngine(method, streamAllocation, (RetryableSink) requestBody, response)。

initHttpEngine() 里面会设置连接为 true,然后调用 newHttpEngine(method, null, null, null) 创建 HttpEngine 对象并返回;而 newHttpEngine(String method, …) 方法里面会进一步构造一系列对象如 Request、OkHttpClient 等然后调用 HttpEngine() 构造函数。

execute(true) 里面则会开始真正的建立连接过程:httpEngine 会调用 sendRequest() 发送请求,然后调用 getConnection() 获取连接,最后调用 readResponse() 读取相应消息。其中,route 和 handshake 都是通过 connection 的 getRoute() 和 getHandshake() 获取。另外,此函数中会捕获异常,然后通过 httpEngine 的 recover(e) 获取 retryEngine 实现重试功能。


再看下 getOutputStream() 这个方法,首先调用 connect(), 然后通过 httpEngine 的 getBufferedRequestBody() 获取 BufferedSink 对象,最后调用 BufferedSink 对象的 outputStream() 方法返回 OutputStream 对象。

connect() 里面做的也是调用 initHttpEngine() 初始化 HttpEngine 和无限重复执行 execute()。

getBufferedRequestBody() 方法中会通过 getRequestBody() 获取请求消息体,然后封装到 BufferedSink 对象中返回。

3、关闭连接

disconnect() 方法定义在 HttpURLConnection 中实现在 HttpURLConnectionImpl 中,实际上调用了 HttpEngine 对象的 cancel() 方法

更多相关文章

  1. 【Android基础】Activity的启动模式(android:launchMode) .
  2. Android(安卓)初体验
  3. 学习Android闹钟源代码(三)-AlarmClock类分析(part2)
  4. 如何设定Activity间切换时的动画
  5. android tabHost使用
  6. Android(安卓)2.3新增API - DownloadManager
  7. android客户端连接人人网之二----获取朋友信息
  8. Android(安卓)stutdio2.2 启动模拟器出现“/dev/kvm is not foun
  9. 【OkHttp3源代码分析】(一)Request的execute

随机推荐

  1. Android UI开发专题(四) View自绘控件
  2. 关于移动开发,再推荐给你5本
  3. android路径动画学习笔记
  4. Android开发
  5. 【Android】@+id与@id的区别
  6. Android 动画具体解释View动画
  7. [Android]直接使用代码建立Android视图元
  8. Android基本命令
  9. Android 自定义组合控件小结
  10. Text To Speech 总结