Android网络编程学习(1)

作者: weiinter105 | 来源:发表于2019-02-27 22:49 被阅读0次

    前言

    在Android开发中,网络请求是非常常用的,因此我们需要对Android开发的网络请求有大致的了解;包括应用到的一些基础原理,以及一些常用的开源库

    基本原理

    在Android App的开发中,用到最多的通信方式是基于Http协议,如果涉及到消息推送,可能会使用到WebSocket等,不管是Http还是WebSocket,其底层实现都是基于Socket;Socket即套接字,是一个对TCP/IP协议进行封装的编程调用接口(API);对于一般的开发而言,有很成熟的开源库可以使用;但是我们还是要对其原理有基本的了解

    TCP

    网络是分层的,将网络节点所要完成的数据的发送或转发、打包或拆包、以及控制信息的加载或拆出等工作,分别由不同的硬件和软件模块来完成;常见的是TCP/IP五层分层模型

    TCP_IP五层协议.png

    传输层有TCP(传输控制协议)和UDP(用户数据报协议)两种协议;主要用于保证数据的传输

    TCP三次握手和四次挥手

    tcp标志位,有6种标示:SYN(synchronous建立联机) ACK(acknowledgement 确认) PSH(push传送) FIN(finish结束) RST(reset重置) URG(urgent紧急)

    三次握手

    三次握手.png

    第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;

    第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;

    第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。 完成三次握手,客户端与服务器开始传送数据.

    四次挥手

    四次挥手.png

    第一次挥手:客户端A发送一个FIN.用来关闭客户A到服务器B的数据传送

    第二次挥手:服务器B收到这个FIN. 它发回一个ACK,确认序号为收到的序号+1。和SYN一样,一个FIN将占用一个序号

    第三次挥手:服务器B关闭与客户端A的连接,发送一个FIN给客户端A

    第四次挥手:客户端A发回ACK报文确认,并将确认序号设置为序号加1

    拓展-四次挥手TIME_WAIT机制

    我们知道, 在tcp四次挥手中, B发FIN包(第三次挥手)后, A马上回应最后的ACK, 此时, A的socket让然不能立即进入CLOSED的状态, 为什么呢? 其实这就是在问TIME_WAIT状态存在的理由。

    理由之一:
    A不能保证最后的ACK能达到B, 所以, 还应该观望一段时间, 护送一段时间。 如果最后的ACK丢失, 那么B显然收不到, B于是发起了重传FIN的操作, 此时如果A处于CLOSED的状态, 就没办法给对端发ACK了(实际是发RST), 呜呼哀哉。 所以A应该等一段时间, 这段时间就是所谓的TIME_WAIT, 比如, 等待一个RTT的时间(实际上, 考虑到如下的理由之二就知道, RTT可能不够, 用2MSL更靠谱)。

    所以, TIME_WAIT存在的理由之一是尽可能护送最后的ACK达到对端。

    理由之二:

    假设tcp连接是: A(1.2.3.4:8888)------B(6.7.8.9:9999), 这就是一个tcp四元组。 当tcp连接关闭后, 四元组释放。 后面的新连接可能会重用到这个四元组(有这个可能性), 那么问题就来了: 新四元组和旧四元组完全一致, 他们的网络包会混乱吗? 所以, 可以考虑这样一个机制: 让旧四元组对应的所有网络包都消失后(等一段时间), 才允许新四元组建立, 颇有点锁的味道。 那么这个等一段时间究竟是多久呢? 多久才合适呢? 在前面的文章中, 我们讨论过, 采用2MSL比较合适, 我个人认为, 把TIME_WAIT定义为2MSL只是一个通用的经验方法而已, 无法从理论上百分之百论证。

    所以, TIME_WAIT存在的理由之二是新旧四元组互不干扰

    Socket

    Socket是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API),通过Socket,我们才能使用TCP/IP协议

    socket和http的区别

    Http协议:简单的对象访问协议,对应于应用层。Http协议是基于TCP链接的。

    tcp协议:对应于传输层

    ip协议:对应与网络层

    TCP/IP是传输层协议,主要解决数据如何在网络中传输;而Http是应用层协议,主要解决如何包装数据。而且前面说了socket不是一种协议,而是对传输层协议tcp/ip的包装;

    Http连接:http连接就是所谓的短连接,及客户端向服务器发送一次请求,服务器端相应后连接即会断掉。

    socket连接:socket连接及时所谓的长连接,理论上客户端和服务端一旦建立连接,则不会主动断掉;但是由于各种环境因素可能会是连接断开,比如说:服务器端或客户端主机down了,网络故障,或者两者之间长时间没有数据传输,网络防火墙可能会断开该链接已释放网络资源。所以当一个socket连接中没有数据的传输,那么为了位置连续的连接需要发送心跳消息,具体心跳消息格式是开发者自己定义的。

    http协议

    http协议是应用层协议,重点在数据的包装;TCP/IP重点在于数据的传输

    http协议的工作流程

    HTTP协议定义了客户端发送请求数据的格式以及服务端返回的数据格式,通信双方只需要按照规定的格式来解析数据即可解读数据。HTTP采用请求/响应模式,客户端向服务端发送一个请求报文,请求报文的内容包括请求的方法、URL、协议版本、请求头部和请求数据;服务端以一个状态行作为响应,响应内容包括协议的版本、请求状态响应码、服务器信息、响应头部和响应内容。这中间的数据传输依赖于TCP协议

    传输详细步骤如下:

    1、客户端连接到服务器

    客户端发起一个TCP连接请求,经过三次握手和服务器建立TCP连接;

    2、发送请求数据

    按照HTTP协议规定的格式组装请求报文,并通过TCP连接向服务端发送请求报文;

    3、服务端接收请求报文并处理

    服务端通过TCP连接收到客户端发送过来的请求报文后,按照规定格式解析数据,根据解读的数据生成对应的响应报文,生成的响应报文也要遵循HTTP协议;

    4、服务端发送响应报文给客户端

    服务端将生成的响应报文通过TCP连接发送给客户端

    5、关闭TCP连接

    服务端将数据发送给客户端后,如果connection模式为close,则服务端主动关闭TCP连接,客户端被动关闭连接,通信结束;如果connection模式为keepalive,则该连接会保持一段时间,则该时间段内可以继续通过连接传输数据;

    6、客户端处理响应数据

    客户端收到响应报文后,按照HTTP协议规定格式解析响应报文并处理。

    http请求报文

    HTTP协议的请求报文由请求行、请求头部、空行、请求数据四个部分组成

    请求报文范例

    
    POST /lotto/android/v1.0/order-group/queryOrderGroupPersonInfo HTTP/1.1
    
    cache-control: no-cache
    
    Postman-Token: 800ec750-6ee8-4b2b-a879-f5d854115862
    
    Content-Type: application/json
    
    User-Agent: PostmanRuntime/3.0.11-hotfix.2
    
    Accept: */*
    
    Host: sitapp.2ncai.com
    
    accept-encoding: gzip, deflate
    
    content-length: 38
    
    Connection: close
    
    {"seeType":1,"source":1,"userId":"30"}
    
    

    请求行

    在上面的例子中,请求行如下:

    POST /lotto/android/v1.0/order-group/queryOrderGroupPersonInfo HTTP/1.1

    http请求方法

    GET:请求获得Request-URL所标识的资源

    POST:在Request-URL所标识的资源后附加新的数据,即可以向服务端发送请求数据

    HEAD:请求获取Request-URL所标识的资源的响应消息报头

    PUT:请求服务器存储一个资源,并用Request-URL作为其标识

    DELETE:请求服务器删除Request-URL所标识的资源

    TRACE:请求服务器回送收到的请求信息,主要用于测试或者诊断

    CONNETC:HTTP1.1中预留的能够将连接改为管道方式的代理服务器

    OPTIONS:请求查询服务器性能,或者查询与资源相关的选项或需求

    对于我们平时的开发来说,用到最多的就是GET和POST

    get和post的区别
    • GET在浏览器回退时是无害的,而POST会再次提交请求。

    • GET产生的URL地址可以被Bookmark,而POST不可以。

    • GET请求会被浏览器主动cache,而POST不会,除非手动设置。

    • GET请求只能进行url编码,而POST支持多种编码方式。

    • GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。

    • GET请求在URL中传送的参数是有长度限制的,而POST没有。

    • 对参数的数据类型,GET只接受ASCII字符,而POST没有限制。

    • GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。

    • GET参数通过URL传递,POST放在Request body中

    参考GET和POST两种基本请求方法的区别

    请求报头

    在请求行之后会有0个或者多个请求报头,每个请求报头都包含一个名字和一个值,他们之间用英文冒号(:)分隔,例如上面的例子中请求报头如下

    
    cache-control: no-cache
    
    Postman-Token: 800ec750-6ee8-4b2b-a879-f5d854115862
    
    Content-Type: application/json
    
    User-Agent: PostmanRuntime/3.0.11-hotfix.2
    
    Accept: */*
    
    Host: sitapp.2ncai.com
    
    accept-encoding: gzip, deflate
    
    content-length: 38
    
    Connection: close
    
    

    关于请求报头我们后面说到消息报头的时候在统一说明

    请求数据

    请求数据不在GET方法中使用,而是在POST中使用,它表示向服务器附加的请求数据。POST方法使用于需要向服务器提交数据的请求,比如客户填写表单需要提交到服务器就可以使用POST方法发起请求

    响应报文

    HTTP的响应报文是指服务端返回给客户端的报文,其格式为状态行、响应报头、空行、响应正文

    范例如下:

    
    HTTP/1.1 200
    
    Date: Sat, 11 Aug 2018 04:24:25 GMT
    
    Content-Type: application/json;charset=UTF-8
    
    Transfer-Encoding: chunked
    
    Connection: close
    
    X-Application-Context: application:test:8160
    
    Server: my_server
    
    {"success":1,"errorCode":"10001","message":"正确","data":{"userName":"CCCC","userId":30,"headPic":"https://sitres.2ncai.com/_upload_images/user/head/1711181009252.png","winCount":32,"winAmount":2580477.84,"orderCount":249,"orderSucRate":0.36,"customizationCount":0,"winBwCount":0,"winSwCount":7,"winWCount":10,"winQCount":20,"winOtherCount":42,"orderGroupLotteryBOs":[{"lotteryCode":100,"lotteryName":"双色球","lotteryType":1,"grade":0,"orderCount":239,"orderSucRate":0.03,"winCount":3,"winAmount":324800.0}]},"serviceTime":1533961465544}
    
    

    状态行

    状态行的格式:HTTP-Version Status-Code Reason-Phrase CRLF

    其中HTTP-Version表示服务器HTTP协议的版本,Status-Code表示响应的状态码,Reason-Phrase表示状态码的文本描述。状态码由三位数字组成,其中首位数字定义了响应的类别,且有5种类别:

    100-199:指示信息,收到请求后,需要请求者继续执行操作

    200-299:请求成功,请求已被成功接收并处理

    300-399:重定向,要完成请求需要进行更进一步操作

    400-499:客户端错误,请求有语法错误或者请求无法实现

    500-599:服务端错误,服务器执行错误,无法正确处理请求

    常见响应状态码

    200:请求被正常处理

    204:请求被受理但没有资源可以返回

    206:客户端只是请求资源的一部分,服务器只对请求的部分资源执行GET方法,相应报文中通过Content-Range指定范围的资源。

    301:永久性重定向

    302:临时重定向

    303:与302状态码有相似功能,只是它希望客户端在请求一个URI的时候,能通过GET方法重定向到另一个URI上

    304:发送附带条件的请求时,条件不满足时返回,与重定向无关

    307:临时重定向,与302类似,只是强制要求使用POST方法

    400:请求报文语法有误,服务器无法识别

    401:请求需要认证

    403:请求的对应资源禁止被访问

    404:服务器无法找到对应资源

    500:服务器内部错误

    503:服务器正忙

    响应报头

    与消息报头一起解释

    响应正文

    服务端返回给客户端的正文数据

    消息报头

    a、通用首部字段(请求报文与响应报文都会使用的首部字段)

    Date:创建报文时间

    Connection:连接的管理

    Cache-Control:缓存的控制

    Transfer-Encoding:报文主体的传输编码方式

    b、请求首部字段(请求报文会使用的首部字段)

    Host:请求资源所在服务器

    Accept:可处理的媒体类型

    Accept-Charset:可接收的字符集

    Accept-Encoding:可接受的内容编码

    Accept-Language:可接受的自然语言

    c、响应首部字段(响应报文会使用的首部字段)

    Accept-Ranges:可接受的字节范围

    Location:令客户端重新定向到的URI

    Server:HTTP服务器的安装信息

    d、实体首部字段(请求报文与响应报文的的实体部分使用的首部字段)

    Allow:资源可支持的HTTP方法

    Content-Type:实体主类的类型

    Content-Encoding:实体主体适用的编码方式

    Content-Language:实体主体的自然语言

    Content-Length:实体主体的的字节数

    Content-Range:实体主体的位置范围,一般用于发出部分请求时使用

    http上层实践-开源库okhhtp

    HTTP是现代应用常用的一种交换数据和媒体的网络方式,高效地使用HTTP能让资源加载更快,节省带宽。OkHttp是一个高效的HTTP客户端,它有以下默认特性:

    • 支持HTTP/2,允许所有同一个主机地址的请求共享同一个socket连接
    • 连接池减少请求延时
    • 透明的GZIP压缩减少响应数据的大小
    • 缓存响应内容,避免一些完全重复的请求

    简单实例

    get

    String url = "https://www.baidu.com";
    OkHttpClient okHttpClient = new OkHttpClient();//创建OkHttpClient实例
    final Request request = new Request.Builder()
            .url(url)
            .get()//默认就是GET请求,可以不写
            .build();//创建Request对象
    Call call = okHttpClient.newCall(request);//将request对象封装成Call任务对象
    call.enqueue(new Callback() {//执行异步任务,call.excute为同步任务
        @Override
        public void onFailure(Call call, IOException e) {
            Log.d(TAG, "onFailure: ");
        }
    
        @Override
        public void onResponse(Call call, Response response) throws IOException {
            Log.d(TAG, "onResponse: " + response.body().string());
        }
    });
    
    @Override
    
    public void onResponse(Call call, Response response) throws IOException {
    
    Log.d(TAG, "onResponse: " + response.body().string());
    
    }
    
    });
    

    post

    MediaType mediaType = MediaType.parse("text/x-markdown; charset=utf-8");
    String requestBody = "I am Jdqm.";
    Request request = new Request.Builder()
            .url("https://api.github.com/markdown/raw")
            .post(RequestBody.create(mediaType, requestBody))
            .build();
    OkHttpClient okHttpClient = new OkHttpClient();
    okHttpClient.newCall(request).enqueue(new Callback() {
        @Override
        public void onFailure(Call call, IOException e) {
            Log.d(TAG, "onFailure: " + e.getMessage());
        }
        @Override
        public void onResponse(Call call, Response response) throws IOException {
            Log.d(TAG, response.protocol() + " " +response.code() + " " + response.message());
            Headers headers = response.headers();
            for (int i = 0; i < headers.size(); i++) {
                Log.d(TAG, headers.name(i) + ":" + headers.value(i));
            }
            Log.d(TAG, "onResponse: " + response.body().string());
        }
    });
    

    简单流程分析

    Okhttp初始化

    
    public OkHttpClient() {
    
    this(new Builder());
    
    }
    
    public Builder() {
    
    dispatcher = new Dispatcher(); // 由call代表的请求任务分发器
    
    protocols = DEFAULT_PROTOCOLS; // 默认的协议 http2 http1.1
    
    connectionSpecs = DEFAULT_CONNECTION_SPECS; // 设置连接时支持的tls层协议以及不进行数据加密
    
    eventListenerFactory = EventListener.factory(EventListener.NONE);
    
    proxySelector = ProxySelector.getDefault();
    
    cookieJar = CookieJar.NO_COOKIES;
    
    socketFactory = SocketFactory.getDefault(); // socket生产工厂
    
    hostnameVerifier = OkHostnameVerifier.INSTANCE;
    
    certificatePinner = CertificatePinner.DEFAULT;
    
    proxyAuthenticator = Authenticator.NONE;
    
    authenticator = Authenticator.NONE;
    
    connectionPool = new ConnectionPool(); //连接池 支持多路复用
    
    dns = Dns.SYSTEM;
    
    followSslRedirects = true;
    
    followRedirects = true;
    
    retryOnConnectionFailure = true;
    
    connectTimeout = 10_000;
    
    readTimeout = 10_000;
    
    writeTimeout = 10_000;
    
    pingInterval = 0;
    
    }
    
    

    然后根据请求报文的格式进行Request对象的构造;紧接着通过 OkHttpClient 和 Request 构造一个 Call对象,它的实现是RealCall,代表请求任务

    RealCall请求任务

    
    public Call newCall(Request request) {
    
    return RealCall.newRealCall(this, request, false /* for web socket */);
    
    }
    
    static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket){
    
    // Safely publish the Call instance to the EventListener.
    
    RealCall call = new RealCall(client, originalRequest, forWebSocket);
    
    call.eventListener = client.eventListenerFactory().create(call);
    
    return call;
    
    }
    
    private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    
    this.client = client;
    
    this.originalRequest = originalRequest;
    
    this.forWebSocket = forWebSocket;
    
    this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
    
    }
    
    

    可以看到在 RealCall 的构造方法中创建了一个RetryAndFollowUpInterceptor,用于处理请求错误和重定向等,这是 Okhttp 框架的精髓 interceptor chain 中的一环,默认情况下也是第一个拦截器,除非调用 OkHttpClient.Builder#addInterceptor(Interceptor) 来添加全局的拦截器。关于拦截器链的顺序参见 RealCall#getResponseWithInterceptorChain() 方法。

    RealCall#enqueue(Callback)

    
    public void enqueue(Callback responseCallback) {
    
    synchronized (this) {
    
    //每个请求只能之执行一次
    
    if (executed) throw new IllegalStateException("Already Executed");
    
    executed = true;
    
    }
    
    captureCallStackTrace();
    
    eventListener.callStart(this);
    
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
    
    }
    
    

    AsyncCall代表异步执行的请求任务,然后调用任务分发器Dispatcher的enqueue方法

    Dispatcher

    
    public final class Dispatcher {
    
    private int maxRequests = 64; //最大请求数量
    
    private int maxRequestsPerHost = 5; //每台主机最大的请求数量
    
    private @Nullable Runnable idleCallback;
    
    /** Executes calls. Created lazily. */
    
    private @Nullable ExecutorService executorService; //线程池
    
    /** Ready async calls in the order they'll be run. */
    
    private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
    
    /** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
    
    private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
    
    /** Running synchronous calls. Includes canceled calls that haven't finished yet. */
    
    private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
    
    /** 这个线程池没有核心线程,线程数量没有限制,空闲60s就会回收*/
    
    public synchronized ExecutorService executorService() {
    
    if (executorService == null) {
    
    executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
    
    new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
    
    }
    
    return executorService;
    
    }
    
    }
    
    

    任务分发器包括一个线程池,用于执行http请求;三个队列,正在执行的异步任务队列,准备就绪正在排队的异步任务队列,同步任务队列;

    Dispatcher#enqueue

    
    synchronized void enqueue(AsyncCall call) {
    
    //正在执行的任务数量小于最大值(64),并且此任务所属主机的正在执行任务小于最大值(5)
    
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
    
    runningAsyncCalls.add(call);
    
    executorService().execute(call); //线程池执行任务
    
    } else {
    
    readyAsyncCalls.add(call); //等待队列
    
    }
    
    }
    
    

    这里的执行就类似于java中Callable和FutureTask的使用,原理上是类似的;最终执行到AsyncCall的execute

    AsyncCall#execute

    
    final class AsyncCall extends NamedRunnable {
    
    //省略...
    
    @Override protected void execute() {
    
    boolean signalledCallback = false;
    
    try {
    
    //调用 getResponseWithInterceptorChain()获得响应内容
    
    Response response = getResponseWithInterceptorChain();
    
    if (retryAndFollowUpInterceptor.isCanceled()) {
    
    //这个标记为主要是避免异常时2次回调
    
    signalledCallback = true;
    
    //回调Callback告知失败
    
    responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
    
    } else {
    
    signalledCallback = true;
    
    //回调Callback,将响应内容传回去
    
    responseCallback.onResponse(RealCall.this, response);
    
    }
    
    } catch (IOException e) {
    
    if (signalledCallback) {
    
    // Do not signal the callback twice!
    
    Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
    
    } else {
    
    eventListener.callFailed(RealCall.this, e);
    
    responseCallback.onFailure(RealCall.this, e);
    
    }
    
    } finally {
    
    //不管请求成功与否,都进行finished()操作
    
    client.dispatcher().finished(this); //做队列清理操作,触发队列下面的任务
    
    }
    
    }
    
    }
    
    

    getResponseWithInterceptorChain

    
    Response getResponseWithInterceptorChain() throws IOException {
    
    // Build a full stack of interceptors.
    
    List<Interceptor> interceptors = new ArrayList<>(); //这是一个List,是有序的
    
    interceptors.addAll(client.interceptors());//首先添加的是用户添加的全局拦截器
    
    interceptors.add(retryAndFollowUpInterceptor); //错误、重定向拦截器
    
    //桥接拦截器,桥接应用层与网络层,添加必要的头、
    
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    
    //缓存处理,Last-Modified、ETag、DiskLruCache等
    
    interceptors.add(new CacheInterceptor(client.internalCache()));
    
    //连接拦截器
    
    interceptors.add(new ConnectInterceptor(client));
    
    //从这就知道,通过okHttpClient.Builder#addNetworkInterceptor()传进来的拦截器只对非网页的请求生效
    
    if (!forWebSocket) {
    
    interceptors.addAll(client.networkInterceptors());
    
    }
    
    //真正访问服务器的拦截器
    
    interceptors.add(new CallServerInterceptor(forWebSocket));
    
    Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
    
    originalRequest, this, eventListener, client.connectTimeoutMillis(),
    
    client.readTimeoutMillis(), client.writeTimeoutMillis());
    
    return chain.proceed(originalRequest);
    
    }
    
    

    RealInterceptorChain#proceed()

    
    public Response proceed(Request request) throws IOException {
    
    return proceed(request, streamAllocation, httpCodec, connection);
    
    }
    
    public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
    
    RealConnection connection) throws IOException {
    
    //省略异常处理...
    
    // Call the next interceptor in the chain.
    
    RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
    
    connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
    
    writeTimeout);
    
    Interceptor interceptor = interceptors.get(index);
    
    Response response = interceptor.intercept(next);
    
    //省略异常处理...
    
    return response;
    
    }
    
    

    okhttp对于网络请求采用用了一个类似AOP的的拦截器链,链式调用所有拦截器,最后执行请求返回response,而okhttp内置了5个拦截器,分别为:

    1.RetryAndFollowUpInterceptor

    在网络请求失败后进行重试

    当服务器返回当前请求需要进行重定向时直接发起新的请求,并在条件允许情况下复用当前连 接

    2.BridgeInteceptor

    设置内容长度,内容编码

    设置gzip压缩,并在接收到内容后进行解压。省去了应用层处理数据解压的麻烦

    添加cookie

    设置其他报头,如User-Agent,Host,Keep-alive等。其中Keep-Alive是实现多路复用的必要步骤

    3.CacheInterceptor

    当网络请求有符合要求的Cache时直接返回Cache

    当服务器返回内容有改变时更新当前cache

    如果当前cache失效,删除

    4.ConnectInterceptor

    为当前请求找到合适的连接,可能复用已有连接也可能是重新创建的连接,返回的连接由连接池负责决定。

    5.CallServerInterceptor

    负责向服务器发起真正的访问请求,并在接收到服务器返回后读取响应返回。

    示意图如下:

    okhttp拦截器.png

    5个拦截器的代码可以参考Okhttp3源码分析,这里不再详细叙述了

    后续

    本文学习了Android网络编程的一些基础知识点,并从一次简单的网络请求来跟踪源码,梳理的一个大致流程,其中对于每个拦截器只是说明作用,其实每个拦截器设计也很巧妙,后续有机会可以深入学习

    相关文章

      网友评论

        本文标题:Android网络编程学习(1)

        本文链接:https://www.haomeiwen.com/subject/dgoquqtx.html