OKHttp3源码解析(一)同步请求的源码分析

OKHTTP是什么?

百度一搜:”一个处理网络请求的开源项目,是安卓端最火热的轻量级框架,由移动支付Square公司贡献(该公司还贡献了Picasso) 用于替代HttpUrlConnection和Apache HttpClient(android API23 6.0里已移除HttpClient,现在已经打不出来)”

正常的Http协议

OKHTTP的开发

同步请求

OkHttpClient okHttpClient = new OkHttpClient.Builder().build();//创建单例
Request request = new Request.Builder()//创建请求
        .url("http://www.baidu.com")
        .build();
Call call = okHttpClient.newCall(request);
try {
     Response response = call.execute();//执行请求
} catch (IOException e) {
     e.printStackTrace();
}

 

异步请求

​OkHttpClient okHttpClient = new OkHttpClient.Builder().build();//创建单例
Request request = new Request.Builder()//创建请求
        .url("http://www.baidu.com")
        .build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
     Log.d(TAG,"失败");
}

@Override
public void onResponse(Call call, Response response) throws IOException {
      Log.d(TAG,"成功");               
}
});

​

同步异步的区别:同步就好比食堂窗口排队吃饭,一个窗口一个一个的排队,只有前一个人排队完之后,后一个人才能打着饭,

而异步就好比多了一个个人窗口,你就不必去等待前一个人,那个窗口专门为你开了个小灶。

接下来分析一下OKHTTP同步

第一步创建了OKHttpClient

这个类其实是OKHttp请求的一个客户端类,很多的功能都需要通过这个客户端类来进行转发,或者直接由这个客户端类来进行实现,创建的方式有两种,第一种是可以直接new OKHttpClient(),第二种就是像例子中这种以builder(建造者设计模式)这种形式创建的,并且可以设计一些参数来做一些事情比如readTimeout

OkHttpClient okHttpClient = new OkHttpClient.Builder().readTimeout(10,TimeUnit.SECONDS).build();

设置写入超时的时间,默认10s,timeout超时时间,unit时间单位,一般是TimeUnit.SECOND或者TimeUnit.MILLIMETER。

connectTimeout(long timeout, TimeUnit unit) 

连接超时的时间

 writeTimeout(long timeout, TimeUnit unit)

写入超时的时间,等等

进入Builder的构造方法中来看

public Builder() {
            this.dispatcher = new Dispatcher();   //OKHttp核心类,分发器(同步异步请求的分发,后续会详细分析)
            this.protocols = OkHttpClient.DEFAULT_PROTOCOLS;
            this.connectionSpecs = OkHttpClient.DEFAULT_CONNECTION_SPECS;
            this.proxySelector = ProxySelector.getDefault();
            this.cookieJar = CookieJar.NO_COOKIES;
            this.socketFactory = SocketFactory.getDefault();
            this.hostnameVerifier = OkHostnameVerifier.INSTANCE;
            this.certificatePinner = CertificatePinner.DEFAULT;
            this.proxyAuthenticator = Authenticator.NONE;
            this.authenticator = Authenticator.NONE;
            this.connectionPool = new ConnectionPool(); //连接池(可以理解为客户端与服务端一个连接叫connection,而每一个connection都放在这个ConnectionPool中,由它来管理)
            this.dns = Dns.SYSTEM;
            this.followSslRedirects = true;
            this.followRedirects = true;
            this.retryOnConnectionFailure = true;
            this.connectTimeout = 10000;
            this.readTimeout = 10000;
            this.writeTimeout = 10000;
        }

通过OHKttp的build()方法就可以来完成整个的OKHttpClient的对象创建。

第二步创建Request

它其实代表的是我们请求的报文信息,也是根据建造者模式来创建的,例子中通过建造者模式设置了url,然后调用build()方法来完成整个的Request的创建

第三步需要创建一个Call对象

什么是Call对象,其实它代表实际的一个http请求,通过将request传入call中连接request 和response的桥梁,这里是同步异步请求的分水岭,前三步都是一样的。

第四步同步请求代码

通过执行execute方法来进行同步操作

OKHttp同步需要注意的是:发送请求之后,就会进入阻塞状态,直到收到响应。

接下来分析一下OKHTTP异步

跟同步的前三步骤一样

第四步通过enqueue(new Callback(){})请求,开启一个新的工作线程,然后它会让Okhttp请求都在这个工作线程里工作,通过两个回调onResponse 和 onFailure来告知异步请求的成功与失败

之前查看了OKHttpClient中Builder的构造方法,接来下看一下Request中Builder的构造方法

public Builder() {
       this.method = "GET";
       this.headers = new okhttp3.Headers.Builder();
}

从代码中能够看出,默认的方法是get请求,当然你也可以设置post请求,创建了Headers.Builder的内部类对象.来进行保存它的Head头部信息

public Request.Builder post(RequestBody body) {
     return this.method("POST", body);
}

然后在继续查看一下.build()做了什么?

public Request build() {
       if (this.url == null) {
          throw new IllegalStateException("url == null");
       } else {
          return new Request(this);  //直接创建了,把当前的build对象传递过去(就是把之前配置好的请求方式,url 头部都赋值给Request这个对象)
       }
}

继续看一下Request的构造方法

  private Request(Request.Builder builder) {
		//该方法为其指定了所需要的请求方式
        this.url = builder.url; //url
        this.method = builder.method; //请求方式
        this.headers = builder.headers.build();//头部信息
        this.body = builder.body;  
        this.tag = builder.tag != null ? builder.tag : this;
  }

从代码上可知:构建了携带请求信息的Request对象

准备工作已经完成,接下来看一下是如何通过call.execute()进行同步请求的,查看代码

Call call = okHttpClient.newCall(request);  进入newCall方法
 public Call newCall(Request request) {
     return new RealCall(this, request);
 }

也就是说它的实现操作都是在RealCall里操作的,查看RealCall

protected RealCall(OkHttpClient client, Request originalRequest) {
      this.client = client;  //初始化好的OKHttpClient 对象
      this.originalRequest = originalRequest;//初始化好的Request 对象
      this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client);//赋值了一个重定向拦截器,后续会讲解拦截器
}

最后一步同步请求的执行方法execute() 进入查看代码

public Response execute() throws IOException {
        synchronized(this) { 
            if (this.executed) {//这个判断的所要表达的是,这个OKHttp请求只能执行一次,如果false那就会报出异常
                throw new IllegalStateException("Already Executed");
            }

            this.executed = true;  //如果么有执行过那么就设置成true
        }

        Response var2;
        try {
            this.client.dispatcher().executed(this);//①通过OKHttpClient调用分发器执行
            Response result = this.getResponseWithInterceptorChain();//②获取Response,这个是拦截器链的方法,一次调用拦截器来进行相应的操作
            if (result == null) {
                throw new IOException("Canceled");
            }

            var2 = result;
        } finally {
            this.client.dispatcher().finished(this); //③主动的回收同步的请求
        }

        return var2;
    }

①执行的方法如下

private final Deque<RealCall> runningSyncCalls = new ArrayDeque();//它是在dispather类中定义的
	synchronized void executed(RealCall call) {
        this.runningSyncCalls.add(call);  //其实就是把它添加到同步请求的队列当中
    }

关于dispatcher主要干什么呢?

      先有个大概的概念,其实dispather的作用就是维持Call请求发给它的一次状态,同时它也维护了一个线程池,用于执行网络请求,而call这个请求在执行任务的时候通过这个dispatcher分发器类,把它的任务推到我们执行的队列当中。然后来进行操作,如果操作完成再执行我们的等待队列的任务。

③执行的方法如下

void finished(RealCall call) {
        this.finished(this.runningSyncCalls, call, false);
    }

    private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
        int runningCallsCount;
        Runnable idleCallback;
        synchronized(this) {
            if (!calls.remove(call)) {//当前的同步队列中移除这个请求 ,如果不能移除就会抛出异常
                throw new AssertionError("Call wasn't in-flight!");
            }

            if (promoteCalls) {  //同步的时候是走不到这个方法里面,但是异步的时候会进入该方法
                this.promoteCalls();
            }

            runningCallsCount = this.runningCallsCount();//④返回了正在执行的异步请求。和返回了正在执行的同步请求的数量总和
            idleCallback = this.idleCallback;
        }

        if (runningCallsCount == 0 && idleCallback != null) {//如果正在执行的请求数为0的时候,并且它的回调不为空的时候,执行run方法
            idleCallback.run();
        }

    }
	
	public synchronized int runningCallsCount() {
        return this.runningAsyncCalls.size() + this.runningSyncCalls.size();//正在执行的异步请求数+正在执行的同步请求数
    }

在同步请求其实dispatcher只做了两件事,添加同步请求,移除同步请求。

同步总结:第一点,创建了一个OKHttpClient对象

                  第二点,构建了一个Request对象,通过OKHttpClient和Request对象,构建出Call对象

                  第三点,执行call的execute方法

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

万子开发

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值