精华内容
下载资源
问答
  • OkHttpClient同步请求的执行流程和源码分析 同步请求示例 OkHttpClient okHttpClient = new OkHttpClient.Builder().readTimeout(5, TimeUnit.SECONDS).build(); Request request = new Request.Builder()...

    OkHttpClient同步请求的执行流程和源码分析

    同步请求示例

    OkHttpClient okHttpClient = new OkHttpClient.Builder().readTimeout(5, TimeUnit.SECONDS).build();
            Request request = new Request.Builder().url("https://www.baidu.com").get().build();
            Call call = okHttpClient.newCall(request);
            try {
                Response response = call.execute();
                Log.e(TAG,"response: " + response.body().string());
            } catch (IOException e) {
                e.printStackTrace();
            }
    

    同步请求的步骤

    1.创建OkHttpClient对象和Request对象,均是采用Builder模式创建,构建者(Builder)设计模式(又叫生成器设计模式)

    2.将Request封装成Call对象

    3.调用Call的execute()方法发送同步请求,发送请求后,就会进入阻塞状态,直到收到响应。

    一、(1)OkHttpClient Builder对象分析

    public Builder() {
          dispatcher = new Dispatcher();
          protocols = DEFAULT_PROTOCOLS;
          connectionSpecs = DEFAULT_CONNECTION_SPECS;
          eventListenerFactory = EventListener.factory(EventListener.NONE);
          proxySelector = ProxySelector.getDefault();
          cookieJar = CookieJar.NO_COOKIES;
          socketFactory = SocketFactory.getDefault();
          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;
    }
    

      OkHttpClient Builder的构造函数,主要是对一些参数赋值默认值,对一些对象进行初始化,Dispatcher是OkHttpClient中http请求的分发器,由它来决定异步请求是直接处理还是进行缓存等待,对于同步请求,它并没有做太多操作,只是把同步请求放到队列当中去执行。ConnectionPool是一个连接池对象,用于管理连接对象,当存在同样的Url请求时,可以复用,从连接池中找到对应缓存的连接对象。

    (2)Request 对象分析

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

    Request Builder的构造函数,默认请求方法为GET,同时初始化一个Header对象。

    public Request build() {
          if (url == null) throw new IllegalStateException("url == null");
          return new Request(this);
    }
    

    build()方法是创建Request对象,将当前的builder对象传入,接下来看Request的构造函数:

    Request(Builder builder) {
        this.url = builder.url;
        this.method = builder.method;
        this.headers = builder.headers.build();
        this.body = builder.body;
        this.tags = Util.immutableMap(builder.tags);
      }
    

      可以看到,是将传入的Builder对象中的属性赋值给Request的相关属性,这样就创建好了Request对象。

    二、创建Call 对象分析

    **`js
    /**

    • Prepares the {@code request} to be executed at some point in the future.
      */

    @Override public Call newCall(Request request) {

    return RealCall.newRealCall(this, request, false /* for web socket */);

    }
    *`


      OkHttpClient 对象中的newCall()方法,返回值是一个Call对象(接口),在这里可以看到实际上调用的RealCall.newRealCall()方法创建,RealCall是Call接口的一个实现类,接着查看RealCall类中newRealCall()方法:

    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;
     }
    

      可以看到newRealCall()方法中创建了Call接口的实现类RealCall对象并返回该对象,到此Call对象的创建便完成了。

    三、Call 对象exexcute()方法分析

      上面有提及到Call对象是一个接口,我们点击查看exexcute()方法时,需要点击查看该方法的实现,实际上是进入到RealCall对象的exexcute()方法:

    @Override public Response execute() throws IOException {
        synchronized (this) {
          if (executed) throw new IllegalStateException("Already Executed");
          executed = true;
        }
        captureCallStackTrace();
        eventListener.callStart(this);
        try {
          client.dispatcher().executed(this);
          Response result = getResponseWithInterceptorChain();
          if (result == null) throw new IOException("Canceled");
          return result;
        } catch (IOException e) {
          eventListener.callFailed(this, e);
          throw e;
        } finally {
          client.dispatcher().finished(this);
        }
      }
    

      在同步代码中,先通过判断executed标识,如果当前已经有在执行,则会抛出"Already Executed"信息的异常,如果没有执行过,则更改executed标识为true。

      接着调用captureCallStackTrace()方法,这个方法主要用于捕捉一些http请求的异常堆栈信息。

      eventListener.callStart(this)开启事件监听,通过查看该方法:

    /**
       * Invoked as soon as a call is enqueued or executed by a client. In case of thread or stream
       * limits, this call may be executed well before processing the request is able to begin.
       *
       * <p>This will be invoked only once for a single {@link Call}. Retries of different routes
       * or redirects will be handled within the boundaries of a single callStart and {@link
       * #callEnd}/{@link #callFailed} pair.
       */
      public void callStart(Call call) {
      }
    

      通过阅读该方法的注释,可以知道该方法会在调用Call对象的enqueue()或execute()方法的时候,就会开启这个listener。

    接下来分析一下这个方法中的核心代码:

    client.dispatcher().executed(this);
    

    首先调用OkHttpClient的dispatcher()方法

    public Dispatcher dispatcher() {
        return dispatcher;
    }
    

    该方法返回一个Dispatcher对象,紧接着调用该对象的executed()方法:

    /** Used by {@code Call#execute} to signal it is in-flight. */
     synchronized void executed(RealCall call) {
       runningSyncCalls.add(call);
     }
    

      该方法中,runningSyncCalls是一个存放同步请求的队列,这里仅仅只是将RealCall加入到同步请求的队列中,Dispatcher对象中相关的队列有:

    /** 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<>();
    
    • readyAsyncCalls 是异步请求的就绪队列
    • runningAsyncCalls 是异步请求的执行队列
    • runningSyncCalls 是同步请求的执行队列

      调用完Dispatcher的executed()方法后,紧接着调用getResponseWithInterceptorChain()方法获取Response对象,这个其实是一个拦截器链的方法,该方法内部会依次调用拦截器来进行相应的操作。

    最后看一下finally中:

    finally {
          client.dispatcher().finished(this);
    }
    

      通过调用Dispatcher的finished()方法,传入当前的RealCall对象,查看该方法的代码可以发现:

    /** Used by {@code Call#execute} to signal completion. */
     void finished(RealCall call) {
       finished(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) promoteCalls();
         runningCallsCount = runningCallsCount();
         idleCallback = this.idleCallback;
       }
    
       if (runningCallsCount == 0 && idleCallback != null) {
         idleCallback.run();
       }
     }
    

      该方法继续调用了其他一个同名的的方法,将正在执行的同步请求队列传了进来,在同步代码块中,移除掉同步请求队列中的call对象,并进行了判断,如果移除出错,则会抛出异常。接着判断promoteCalls,由于这里传入的promoteCalls为false,所以不会走promoteCalls()方法。

      接着,对runningCallsCount重新赋值,runningCallsCount用于记录当前正在执行的请求数,查看该方法的代码:

    public synchronized int runningCallsCount() {
        return runningAsyncCalls.size() + runningSyncCalls.size();
    }
    

    该方法很简单,即返回正在执行的异步请求数和正在执行的同步请求数的总和。

    if (runningCallsCount == 0 && idleCallback != null) {
          idleCallback.run();
    }
    

      最后通过判断当前正在执行的请求数,如果当前没有正在执行的请求数并且有设置闲置时的回调,则会回调其run()方法。

    总结

      到此,同步请求的执行流程就已经分析完了,由上述的分析可以知道,在同步请求中,Dispatcher分发器做的工作非常简单,就两个操作,保存同步请求和移除同步请求

    OkHttpClient异步请求的执行流程和源码分析

    异步请求示例

    OkHttpClient okHttpClient = new OkHttpClient.Builder().readTimeout(5, TimeUnit.SECONDS).build();
           Request request = new Request.Builder().url("https://www.baidu.com").get().build();
           Call call = okHttpClient.newCall(request);
           call.enqueue(new Callback() {
               @Override
               public void onFailure(Call call, IOException e) {
    
               }
    
               @Override
               public void onResponse(Call call, Response response) throws IOException {
    
               }
           });
    

    异步请求的步骤

    创建OkHttpClient对象和Request对象,均是采用Builder模式创建,构建者(Builder)设计模式(又叫生成器设计模式)

    将Request封装成Call对象

    调用Call的enqueue()方法进行异步请求

    同步和异步的区别

    发起请求的方法调用

    阻塞线程与否

    源码分析

      异步请求的前两步,和同步请求的一致,都是一些准备工作,并没有发起请求,这里不再重复说明,最主要的是第三步,调用Call对象的enqueue()方法,具体的实现还是在RealCall类中,查看该方法代码:

    @Override 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));
    }
    

      前面的操作和同步请求的execute()方法相似,主要是 client.dispatcher().enqueue(new AsyncCall(responseCallback)) 这行代码,调用Dispatcher的enqueue()方法,将Callback回调封装成AsyncCall对象作为参数传入,通过查看代码,了解到AsyncCall对象继承自NamedRunnable对象,而NamedRunnable对象实现了Runnable接口,接着继续查看Dispatcher的enqueue()方法源码:

    synchronized void enqueue(AsyncCall call) {
       if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
         runningAsyncCalls.add(call);
         executorService().execute(call);
       } else {
         readyAsyncCalls.add(call);
       }
     }
    

      该方法前加了synchronized修饰符,是一个同步方法,根据判断当前执行的异步请求数是否小于maxRequests(最大请求数,默认为64) 且当前执行的异步请求队列中相同主机的请求数小于maxRequestsPerHost(每个主机最大请求数,默认为5) 来进行处理,如果二者都小于设置的值,则将该请求添加到runningAsyncCalls(异步请求执行队列)中,否则则添加到readyAsyncCalls(异步请求准备队列)中。

    runningCallsForHost()方法的代码:

    /** Returns the number of running calls that share a host with {@code call}. */
      private int runningCallsForHost(AsyncCall call) {
        int result = 0;
        for (AsyncCall c : runningAsyncCalls) {
          if (c.get().forWebSocket) continue;
          if (c.host().equals(call.host())) result++;
        }
        return result;
      }
    

      通过注释可以知道,该方法返回同一个主机的请求数目,通过遍历执行中的异步请求队列,和传入的AsyncCall对象的主机对比,如果相同则记录数递增,以此获得和传入AsyncCall对象相同主机的请求数。

    enqueue()方法中,主要的代码:

    executorService().execute(call);
    

    这里是进行异步请求操作的代码,先看下executorService()方法的代码:

    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;
    }
    

      该方法也是一个同步方法,主要用于返回 ExecutorService 对象,在这里仅一次创建了线程池对象 ThreadPoolExecutor,第二个参数传入了Integer的最大值,即线程池所能容纳的最大线程数为Integer.MAX_VALUE,虽然这里设置了很大的值,但是实际情况下并非会达到最大值,因为上面enqueue()方法中有做了判断,主要的还是maxRequests这个值决定异步请求线程池的最大数量。

      executorService()方法返回了线程池对象,接着调用它的execute()方法,传入实现Runnable接口的AsyncCall对象,上面提及到AsyncCall继承NamedRunnable,而NamedRunnable对象实现了Runnable接口,所以我们想知道该线程池执行这个任务做了什么,就得看下NamedRunnable对象的 run() 方法:

    @Override public final void run() {
        String oldName = Thread.currentThread().getName();
        Thread.currentThread().setName(name);
        try {
          execute();
        } finally {
          Thread.currentThread().setName(oldName);
        }
    }
    

    该方法中,真正的处理逻辑是在execute()方法中:

    protected abstract void execute();
    

    而execute()方法是一个抽象方法,所以要回到继承NamedRunnable对象的AsyncCall类中:

    @Override protected void execute() {
          boolean signalledCallback = false;
          try {
            Response response = getResponseWithInterceptorChain();
            if (retryAndFollowUpInterceptor.isCanceled()) {
              signalledCallback = true;
              responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
            } else {
              signalledCallback = true;
              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 {
            client.dispatcher().finished(this);
          }
    }
    

      这里才是真正进行异步请求操作的逻辑,同样也是通过getResponseWithInterceptorChain()方法得到Response对象,关于getResponseWithInterceptorChain()方法的分析在下面的文章里将会介绍,接着通过判断retryAndFollowUpInterceptor是否取消回调CallBack接口的onFailure()或onResponse()方法,最后finally中,和同步请求的处理一样,调用了Dispatcher对象的finished()方法:

    /** Used by {@code AsyncCall#run} to signal completion. */
      void finished(AsyncCall call) {
        finished(runningAsyncCalls, call, true);
      }
    

    也是调用了带三个参数的finished()方法,传入了runningAsyncCalls,call,第三个参数传入了true。

    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) promoteCalls();
          runningCallsCount = runningCallsCount();
          idleCallback = this.idleCallback;
        }
    
        if (runningCallsCount == 0 && idleCallback != null) {
          idleCallback.run();
        }
    }
    

    这里的处理和同步请求结束后的处理多了一个promoteCalls()方法的调用,因为这里promoteCalls传入了true,所以会走promoteCalls()方法:

    private void promoteCalls() {
        if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
        if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.
    
        for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
          AsyncCall call = i.next();
    
          if (runningCallsForHost(call) < maxRequestsPerHost) {
            i.remove();
            runningAsyncCalls.add(call);
            executorService().execute(call);
          }
    
          if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
        }
      }
    

      看完这个方法,会有一种恍然大悟的感觉,因为上面调用enqueue()方法的时候,会根据情况将请求添加到runningAsyncCalls(异步请求执行队列)或readyAsyncCalls(异步请求准备队列)中,而readyAsyncCalls队列中的请求什么时候执行呢,相信在看enqueue()方法的时候会有这个疑问,看了promoteCalls()后疑问将会被解答,为了方便阅读再次贴上enqueue()方法:

    synchronized void enqueue(AsyncCall call) {
        if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
          runningAsyncCalls.add(call);
          executorService().execute(call);
        } else {
          readyAsyncCalls.add(call);
        }
      }
    

      promoteCalls()方法中,首先做了一些判断,当runningAsyncCalls(异步请求执行队列)已经达到设置的最大的请求数或当前readyAsyncCalls(异步请求准备队列)中没有请求的时候,则直接返回不做处理,如果满足条件,则会遍历readyAsyncCalls队列,将该请求添加到runningAsyncCalls队列中,并调用 executorService().execute(call) 对该请求进行处理。

    总结

      如果异步请求数超过最大请求数或同个主机最大请求数超过设置的值的时候,该请求就会添加到readyAsyncCalls(异步请求准备队列)中,当执行完runningAsyncCalls(异步请求执行队列)的请求后,将会调用Dispatcher的finished()三个参数的方法,第三个参数传入true,会调用promoteCalls()方法,遍历准备队列readyAsyncCalls,将该队列的中的请求添加到执行队列runningAsyncCalls中,调用 executorService().execute(call)进行处理。

    原文发布时间为:2018-07-26
    本文作者:安卓巴士Android开发者门户
    本文来自云栖社区合作伙伴“安卓巴士Android开发者门户”, 了解相关信息可以关注“安卓巴士Android开发者门户”。

    展开全文
  • OkHttpClient同步下载文件与访问http

    千次阅读 2018-12-10 17:16:59
    笔记,使用ok http下载文件版本...public class DownUtil { private Logger logger = LoggerFactory.getLogger(this.getClass()); private OkHttpClient okHttpClient; private boolean canDownLoad = true; ...

    笔记,使用ok http下载文件版本为3.1

    public class DownUtil {
    
        private Logger logger = LoggerFactory.getLogger(this.getClass());
    
        private OkHttpClient okHttpClient;
    
        private boolean canDownLoad = true;
    
        public boolean downFile(String downUrl, String downPath){
            try{
                long timeStart = System.currentTimeMillis()/1000;
                okHttpClient=new OkHttpClient();
                Request request = new Request.Builder().url(downUrl).build();
                Response response = okHttpClient.newCall(request).execute();
                InputStream is;
                is = response.body().byteStream();
                FileOutputStream fos=null;
                fos = new FileOutputStream(downPath);
                int len;
                byte[] bytes = new byte[4096];
                while ((len = is.read(bytes)) != -1&&canDownLoad) {
                    fos.write(bytes, 0, len);
                }
                fos.flush();
                is.close();
                fos.close();
                long timeEnd = System.currentTimeMillis()/1000;
            }catch (Exception ex){
                return  false;
            }
            if(canDownLoad)
                return true;
            return  false;
        }
    
        //超时关闭下载
        public void closeDownLoadStream(){
            canDownLoad = false;
        }
       //http下载数据
        public String getHttp(String url){
            try{
                OkHttpClient client = new OkHttpClient();
                Request request = new Request.Builder()
                        .url(url)
                        .get()
                        .removeHeader("User-Agent")
                        .addHeader("User-Agent","Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.118 Safari/537.36 ApiMaxJia/1.0")
                        .addHeader("Referer","http://api.maxjia.com/")
                        .header("Cookie","phone_num=0101010101010101010101")
                        .build();
                client.setConnectTimeout(10, TimeUnit.SECONDS);
                Response response = client.newCall(request).execute();
                if (response.isSuccessful()){
                   return response.body().string();
                }
            }catch (Exception ex){
    
            }
            return null;
        }	
    
    }
    
    展开全文
  • OKhttp中请求任务的管理是由dispatcher来负责的,负责的请求的分发的发起,实际执行请求的是ConnectionPool同步请求:同一时刻只能有一个任务发起,synchronized关键字锁住了整个代码,那么如果dangqianOKhttpClient...

    OKhttp中请求任务的管理是由dispatcher来负责的,负责的请求的分发的发起,实际执行请求的是ConnectionPool

    同步请求:同一时刻只能有一个任务发起,synchronized关键字锁住了整个代码,那么如果dangqianOKhttpClient已经执行了一个同步任务,如果这个任务没有释放锁,那么新发起的请求将被阻塞,直到当前任务释放锁,如下图源码:



    @Override public Response execute() throws IOException {

    //同一时刻只能有一个任务执行 因为是阻塞式的 由synchronized关键字锁住
        synchronized (this) {
          if (executed) throw new IllegalStateException("Already Executed");
          executed = true;
        }
        captureCallStackTrace();
        try {
          client.dispatcher().executed(this);
          Response result = getResponseWithInterceptorChain();
          if (result == null) throw new IOException("Canceled");
          return result;
        } finally {
          client.dispatcher().finished(this);
        }

      }


    异步请求:

    同一时刻可以发起多个请求,以为异步请求每一个都是一个独立的线程,由两个队列管理,并且synchronized只锁住了代码校验是否执行的部分

    @Override public void enqueue(Callback responseCallback)
      synchronized ( this) {
      if (executed) throw new IllegalStateException( "Already Executed"); 
     executed = true; } //异步请求同一时刻可以有多个任务执行,由两个队列管理 
     captureCallStackTrace(); 
     client.dispatcher().enqueue( new AsyncCall(responseCallback)); 
     }
            




     同步方式:发送请求后,就会进入阻塞状态,直到收到响应
        OkHttpClient httpClient = new OkHttpClient.builder().readTimeout(5,TimeUnit.SECONDS).build();
                        Request request= new Request.Builder()
                                                    .url(www.baidu.com)
                                                    .get()
                                                    .build();

        

                 Response  response=  httpClient.newCall(request).execute();

                  String strBody =     response.body().string();

        上面的代码创建OkhttpClient 和Request对象,两者均使用了builder模式,然后将Request封装成Call对象,然后调用Call的

        execute()同步发送请求

    异步方式:是在回调中处理响应的,

        OkHtrtpClient httpClient=new OkhttpClient.Builder().readTimeout(5,TimeUnit.SENCONDS).build();

        Request  request = new Request.Builder()

                                    

                                                     .url(www.baidu.com)
                                                    .get()
                                                    .build();

                Call call=    httpClient.newCall(request);

                call.enqueue(new Callback(){

                        @Override

                    public void onFailure(Call call, IOException e) {

                    System.out.println("Fail");

                }

                    @Override

                    public void onResponse(Call call, Response response) throws IOException {

                    System.out.println(response.body().string());

        

                })

    同样是创建OkhttpClient,Request和Call,只是调用了enqueue方法,并在回调中处理响应。



    展开全文
  • OkHttpClient同步请求的执行流程和源码分析 同步请求示例 OkHttpClient okHttpClient = new OkHttpClient.Builder().readTimeout(5, TimeUnit.SECONDS).build(); Request request = new Request....

    OkHttpClient同步请求的执行流程和源码分析

    同步请求示例

    OkHttpClient okHttpClient = new OkHttpClient.Builder().readTimeout(5, TimeUnit.SECONDS).build();
            Request request = new Request.Builder().url("https://www.baidu.com").get().build();
            Call call = okHttpClient.newCall(request);
            try {
                Response response = call.execute();
                Log.e(TAG,"response: " + response.body().string());
            } catch (IOException e) {
                e.printStackTrace();
            }
    

    同步请求的步骤

    1. 创建OkHttpClient对象和Request对象,均是采用Builder模式创建,构建者(Builder)设计模式(又叫生成器设计模式)

    2. 将Request封装成Call对象

    3. 调用Call的execute()方法发送同步请求,发送请求后,就会进入阻塞状态,直到收到响应。

    一、(1)OkHttpClient Builder对象分析

    public Builder() {
          dispatcher = new Dispatcher();
          protocols = DEFAULT_PROTOCOLS;
          connectionSpecs = DEFAULT_CONNECTION_SPECS;
          eventListenerFactory = EventListener.factory(EventListener.NONE);
          proxySelector = ProxySelector.getDefault();
          cookieJar = CookieJar.NO_COOKIES;
          socketFactory = SocketFactory.getDefault();
          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;
    }
    

      OkHttpClient Builder的构造函数,主要是对一些参数赋值默认值,对一些对象进行初始化,Dispatcher是OkHttpClient中http请求的分发器,由它来决定异步请求是直接处理还是进行缓存等待,对于同步请求,它并没有做太多操作,只是把同步请求放到队列当中去执行。ConnectionPool是一个连接池对象,用于管理连接对象,当存在同样的Url请求时,可以复用,从连接池中找到对应缓存的连接对象。

    (2)Request 对象分析

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

    Request Builder的构造函数,默认请求方法为GET,同时初始化一个Header对象。

     public Request build() {
          if (url == null) throw new IllegalStateException("url == null");
          return new Request(this);
    }
    

    build()方法是创建Request对象,将当前的builder对象传入,接下来看Request的构造函数:

    Request(Builder builder) {
        this.url = builder.url;
        this.method = builder.method;
        this.headers = builder.headers.build();
        this.body = builder.body;
        this.tags = Util.immutableMap(builder.tags);
      }
    

      可以看到,是将传入的Builder对象中的属性赋值给Request的相关属性,这样就创建好了Request对象。

    二、创建Call 对象分析

    /**
       * Prepares the {@code request} to be executed at some point in the future.
       */
      @Override public Call newCall(Request request) {
        return RealCall.newRealCall(this, request, false /* for web socket */);
      }
    

      OkHttpClient 对象中的newCall()方法,返回值是一个Call对象(接口),在这里可以看到实际上调用的RealCall.newRealCall()方法创建,RealCall是Call接口的一个实现类,接着查看RealCall类中newRealCall()方法:

      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;
      }
    

      可以看到newRealCall()方法中创建了Call接口的实现类RealCall对象并返回该对象,到此Call对象的创建便完成了。

    三、Call 对象exexcute()方法分析

      上面有提及到Call对象是一个接口,我们点击查看exexcute()方法时,需要点击查看该方法的实现,实际上是进入到RealCall对象的exexcute()方法:

    @Override public Response execute() throws IOException {
        synchronized (this) {
          if (executed) throw new IllegalStateException("Already Executed");
          executed = true;
        }
        captureCallStackTrace();
        eventListener.callStart(this);
        try {
          client.dispatcher().executed(this);
          Response result = getResponseWithInterceptorChain();
          if (result == null) throw new IOException("Canceled");
          return result;
        } catch (IOException e) {
          eventListener.callFailed(this, e);
          throw e;
        } finally {
          client.dispatcher().finished(this);
        }
      }
    

      在同步代码中,先通过判断executed标识,如果当前已经有在执行,则会抛出"Already Executed"信息的异常,如果没有执行过,则更改executed标识为true。

      接着调用captureCallStackTrace()方法,这个方法主要用于捕捉一些http请求的异常堆栈信息。

      eventListener.callStart(this)开启事件监听,通过查看该方法:

     /**
       * Invoked as soon as a call is enqueued or executed by a client. In case of thread or stream
       * limits, this call may be executed well before processing the request is able to begin.
       *
       * <p>This will be invoked only once for a single {@link Call}. Retries of different routes
       * or redirects will be handled within the boundaries of a single callStart and {@link
       * #callEnd}/{@link #callFailed} pair.
       */
      public void callStart(Call call) {
      }
    

      通过阅读该方法的注释,可以知道该方法会在调用Call对象的enqueue()或execute()方法的时候,就会开启这个listener。

    接下来分析一下这个方法中的核心代码:

    client.dispatcher().executed(this);
    

    首先调用OkHttpClient的dispatcher()方法

    public Dispatcher dispatcher() {
        return dispatcher;
    }
    

    该方法返回一个Dispatcher对象,紧接着调用该对象的executed()方法:

    
      /** Used by {@code Call#execute} to signal it is in-flight. */
      synchronized void executed(RealCall call) {
        runningSyncCalls.add(call);
      }
    

      该方法中,runningSyncCalls是一个存放同步请求的队列,这里仅仅只是将RealCall加入到同步请求的队列中,Dispatcher对象中相关的队列有:

     /** 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<>();
    
    • readyAsyncCalls 是异步请求的就绪队列
    • runningAsyncCalls 是异步请求的执行队列
    • runningSyncCalls 是同步请求的执行队列

      调用完Dispatcher的executed()方法后,紧接着调用getResponseWithInterceptorChain()方法获取Response对象,这个其实是一个拦截器链的方法,该方法内部会依次调用拦截器来进行相应的操作。

    最后看一下finally中:

    finally {
          client.dispatcher().finished(this);
    }
    

      通过调用Dispatcher的finished()方法,传入当前的RealCall对象,查看该方法的代码可以发现:

      /** Used by {@code Call#execute} to signal completion. */
      void finished(RealCall call) {
        finished(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) promoteCalls();
          runningCallsCount = runningCallsCount();
          idleCallback = this.idleCallback;
        }
    
        if (runningCallsCount == 0 && idleCallback != null) {
          idleCallback.run();
        }
      }
    

      该方法继续调用了其他一个同名的的方法,将正在执行的同步请求队列传了进来,在同步代码块中,移除掉同步请求队列中的call对象,并进行了判断,如果移除出错,则会抛出异常。接着判断promoteCalls,由于这里传入的promoteCalls为false,所以不会走promoteCalls()方法。

      接着,对runningCallsCount重新赋值,runningCallsCount用于记录当前正在执行的请求数,查看该方法的代码:

    public synchronized int runningCallsCount() {
        return runningAsyncCalls.size() + runningSyncCalls.size();
    }
    

    该方法很简单,即返回正在执行的异步请求数和正在执行的同步请求数的总和。

    if (runningCallsCount == 0 && idleCallback != null) {
          idleCallback.run();
    }
    

      最后通过判断当前正在执行的请求数,如果当前没有正在执行的请求数并且有设置闲置时的回调,则会回调其run()方法。

    总结

      到此,同步请求的执行流程就已经分析完了,由上述的分析可以知道,在同步请求中,Dispatcher分发器做的工作非常简单,就两个操作,保存同步请求和移除同步请求

    OkHttpClient异步请求的执行流程和源码分析

    异步请求示例

     OkHttpClient okHttpClient = new OkHttpClient.Builder().readTimeout(5, TimeUnit.SECONDS).build();
            Request request = new Request.Builder().url("https://www.baidu.com").get().build();
            Call call = okHttpClient.newCall(request);
            call.enqueue(new Callback() {
                @Override
                public void onFailure(Call call, IOException e) {
                    
                }
    
                @Override
                public void onResponse(Call call, Response response) throws IOException {
    
                }
            });
    

    异步请求的步骤

    1. 创建OkHttpClient对象和Request对象,均是采用Builder模式创建,构建者(Builder)设计模式(又叫生成器设计模式)

    2. 将Request封装成Call对象

    3. 调用Call的enqueue()方法进行异步请求

    同步和异步的区别

    1. 发起请求的方法调用

    2. 阻塞线程与否

    源码分析

      异步请求的前两步,和同步请求的一致,都是一些准备工作,并没有发起请求,这里不再重复说明,最主要的是第三步,调用Call对象的enqueue()方法,具体的实现还是在RealCall类中,查看该方法代码:

      @Override 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));
      }
    

      前面的操作和同步请求的execute()方法相似,主要是 client.dispatcher().enqueue(new AsyncCall(responseCallback)) 这行代码,调用Dispatcher的enqueue()方法,将Callback回调封装成AsyncCall对象作为参数传入,通过查看代码,了解到AsyncCall对象继承自NamedRunnable对象,而NamedRunnable对象实现了Runnable接口,接着继续查看Dispatcher的enqueue()方法源码:

      synchronized void enqueue(AsyncCall call) {
        if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
          runningAsyncCalls.add(call);
          executorService().execute(call);
        } else {
          readyAsyncCalls.add(call);
        }
      }
    

      该方法前加了synchronized修饰符,是一个同步方法,根据判断当前执行的异步请求数是否小于maxRequests(最大请求数,默认为64) 且当前执行的异步请求队列中相同主机的请求数小于maxRequestsPerHost(每个主机最大请求数,默认为5) 来进行处理,如果二者都小于设置的值,则将该请求添加到runningAsyncCalls(异步请求执行队列)中,否则则添加到readyAsyncCalls(异步请求准备队列)中。

    runningCallsForHost()方法的代码:

     /** Returns the number of running calls that share a host with {@code call}. */
      private int runningCallsForHost(AsyncCall call) {
        int result = 0;
        for (AsyncCall c : runningAsyncCalls) {
          if (c.get().forWebSocket) continue;
          if (c.host().equals(call.host())) result++;
        }
        return result;
      }
    

      通过注释可以知道,该方法返回同一个主机的请求数目,通过遍历执行中的异步请求队列,和传入的AsyncCall对象的主机对比,如果相同则记录数递增,以此获得和传入AsyncCall对象相同主机的请求数。

    enqueue()方法中,主要的代码:

    executorService().execute(call);
    

    这里是进行异步请求操作的代码,先看下executorService()方法的代码:

    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;
    }
    

      该方法也是一个同步方法,主要用于返回 ExecutorService 对象,在这里仅一次创建了线程池对象 ThreadPoolExecutor,第二个参数传入了Integer的最大值,即线程池所能容纳的最大线程数为Integer.MAX_VALUE,虽然这里设置了很大的值,但是实际情况下并非会达到最大值,因为上面enqueue()方法中有做了判断,主要的还是maxRequests这个值决定异步请求线程池的最大数量。

      executorService()方法返回了线程池对象,接着调用它的execute()方法,传入实现Runnable接口的AsyncCall对象,上面提及到AsyncCall继承NamedRunnable,而NamedRunnable对象实现了Runnable接口,所以我们想知道该线程池执行这个任务做了什么,就得看下NamedRunnable对象的 run() 方法:

    @Override public final void run() {
        String oldName = Thread.currentThread().getName();
        Thread.currentThread().setName(name);
        try {
          execute();
        } finally {
          Thread.currentThread().setName(oldName);
        }
    }
    
    

    该方法中,真正的处理逻辑是在execute()方法中:

     protected abstract void execute();
    

    而execute()方法是一个抽象方法,所以要回到继承NamedRunnable对象的AsyncCall类中:

     @Override protected void execute() {
          boolean signalledCallback = false;
          try {
            Response response = getResponseWithInterceptorChain();
            if (retryAndFollowUpInterceptor.isCanceled()) {
              signalledCallback = true;
              responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
            } else {
              signalledCallback = true;
              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 {
            client.dispatcher().finished(this);
          }
    }
    

      这里才是真正进行异步请求操作的逻辑,同样也是通过getResponseWithInterceptorChain()方法得到Response对象,关于getResponseWithInterceptorChain()方法的分析在下面的文章里将会介绍,接着通过判断retryAndFollowUpInterceptor是否取消回调CallBack接口的onFailure()或onResponse()方法,最后finally中,和同步请求的处理一样,调用了Dispatcher对象的finished()方法:

     /** Used by {@code AsyncCall#run} to signal completion. */
      void finished(AsyncCall call) {
        finished(runningAsyncCalls, call, true);
      }
    

    也是调用了带三个参数的finished()方法,传入了runningAsyncCalls,call,第三个参数传入了true。

    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) promoteCalls();
          runningCallsCount = runningCallsCount();
          idleCallback = this.idleCallback;
        }
    
        if (runningCallsCount == 0 && idleCallback != null) {
          idleCallback.run();
        }
    }
    

    这里的处理和同步请求结束后的处理多了一个promoteCalls()方法的调用,因为这里promoteCalls传入了true,所以会走promoteCalls()方法:

      private void promoteCalls() {
        if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
        if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.
    
        for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
          AsyncCall call = i.next();
    
          if (runningCallsForHost(call) < maxRequestsPerHost) {
            i.remove();
            runningAsyncCalls.add(call);
            executorService().execute(call);
          }
    
          if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
        }
      }
    

      看完这个方法,会有一种恍然大悟的感觉,因为上面调用enqueue()方法的时候,会根据情况将请求添加到runningAsyncCalls(异步请求执行队列)或readyAsyncCalls(异步请求准备队列)中,而readyAsyncCalls队列中的请求什么时候执行呢,相信在看enqueue()方法的时候会有这个疑问,看了promoteCalls()后疑问将会被解答,为了方便阅读再次贴上enqueue()方法:

      synchronized void enqueue(AsyncCall call) {
        if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
          runningAsyncCalls.add(call);
          executorService().execute(call);
        } else {
          readyAsyncCalls.add(call);
        }
      }
    

      promoteCalls()方法中,首先做了一些判断,当runningAsyncCalls(异步请求执行队列)已经达到设置的最大的请求数或当前readyAsyncCalls(异步请求准备队列)中没有请求的时候,则直接返回不做处理,如果满足条件,则会遍历readyAsyncCalls队列,将该请求添加到runningAsyncCalls队列中,并调用 executorService().execute(call) 对该请求进行处理。

    总结

      如果异步请求数超过最大请求数或同个主机最大请求数超过设置的值的时候,该请求就会添加到readyAsyncCalls(异步请求准备队列)中,当执行完runningAsyncCalls(异步请求执行队列)的请求后,将会调用Dispatcher的finished()三个参数的方法,第三个参数传入true,会调用promoteCalls()方法,遍历准备队列readyAsyncCalls,将该队列的中的请求添加到执行队列runningAsyncCalls中,调用 executorService().execute(call)进行处理。

    Dispatcher的作用

    维护请求的状态,并维护一个线程池,用于执行请求。

    异步请求为什么需要两个队列

    异步请求的设计可以将其理解成生产者消费者模式,其中各个角色分别为:

    • Dispatcher 生产者
    • ExecutorService 消费者池
    • Deque readyAsyncCalls 缓存
    • Deque runningAsyncCalls 正在运行的任务

    当同步和异步请求结束后,会调用dispatcher的finished方法,将当前的请求从队列中移除。

    下一篇文章中,将为大家讲解一下OkHttp的拦截器链,感兴趣的朋友可以继续阅读:

    OkHttpClient源码分析(二) —— RetryAndFollowUpInterceptor和BridgeInterceptor

    展开全文
  • OkHttpClient client = new OkHttpClient().newBuilder() .connectTimeout(10000,TimeUnit.MILLISECONDS) .readTimeout(30000,TimeUnit.MILLISECONDS) .build(); URIBuilder ub = new URIBuilder(buildUrl(path...
  • okhttp3.OkHttpClient ; import okhttp3.Request ; import okhttp3.RequestBody ; import okhttp3.Response ; import okio.Timeout ; public class MainActivity extends AppCompatActivity { @...
  • OkHttpClient访问https请求 同步和异步

    千次阅读 2019-06-28 16:50:05
    再来一版同步的: public static void main(String[] args) throws Exception{ new Main().requestHttps(); } public void requestHttps(){ StringBuffer content = new StringBuffer(); try { ...
  • OkHttpClient

    千次阅读 2015-12-29 20:43:10
    OkHttpClient  OkHttpClient client = new OkHttpClient();  Request request = new Request.Builder().url("http://m2.qiushibaike.com/article/list/suggest?page=").get().build();  call = client....
  • OkHttpClient的依赖 implementation 'com.squareup.okhttp3:okhttp-ws:3.4.2' 日志拦截器的依赖 implementation 'com.squareup.okhttp3:logging-interceptor:3.12.0' //同步处理get方式 必须放在子线程里 private ...
  • 今天试着像鸿洋_一样想封装一下OkHttpClient,但是鸿洋_在封装的源码中却没有开启新线程,难道所有都是在主线程中直接操作,让我好迷惑。于是尝试去Log一下线程,发现execute()直接报错,不能在UI线程执行网络操作。...
  • okHttpClient test sample

    2018-05-11 21:59:56
    之前此工程是用于测试webView的,所以工程名不是叫okHttpClient. Server之前文章已经有提到如何搭建了,见 [Windows上Apache服务器的下载和安装](https://blog.csdn.net/kris_fei/article/details/80229735) 流程是...
  • OkHttpClient用例

    千次阅读 2018-11-15 10:33:17
    OkHttpClient okHttpClient = new OkHttpClient(); // 创建OkHttpClient对象 Request request = new Request.Builder().url(urlBaidu).build(); // 创建一个请求 Response response = okHttpClient.newCall...
  • OkHttpClient封装

    2018-07-18 15:40:39
    import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; import okhttp3.ResponseBody; //网络请求工具 public class OkHttpUtils { //初始化OkHttpClient private static ...
  • OkhttpClient工具类

    2018-11-08 13:59:08
    public class OkHttpUtils {  private String baseUrl = "...;  private static OkHttpClient mOkHttpClient;  private Request.Builder requestBuilder;  private String...
  • OkHttpClient使用示例

    千次阅读 2016-09-18 09:40:55
    OKHttpClient是一个类似HttpClient的工具,用于模拟Http,https的请求。语法使用方面要比HttpClient更简洁 一. 1、Jar包下载 maven引入: dependency> groupId>com.squareup.okhttpgroupId> artifactId>...
  • OkhttpClient的使用详解

    万次阅读 多人点赞 2019-06-12 23:34:52
    OkHttpClient是一个高效的HTTP客户端,它有以下默认特性: 支持HTTP/2,允许所有同一个主机地址的请求共享同一个socket连接 连接池减少请求延时 透明的GZIP压缩减少响应数据的大小 缓存响应内容,避免一些完全重复的...
  • OkHttpClient使用和封装

    千次阅读 2018-09-29 10:56:38
    OkHttpClient使用和封装 最近的项目用到了底部弹窗的效果,网上百度了很多类似写好的控件,其中也不乏好的实现。但是为了方便以后扩展,总结了一下前人的经验自己写了实现了一个类: 文章目录 OkHttpClient...
  • okHttpClient的用法

    2017-11-29 20:48:54
    OkHttpClient okHttpClient = new OkHttpClient(); // 创建客户端对象 Request builder = new Request.Builder() .url( path ) // 指定访问路径 .build(); // 提交请求 Call call = okHttpClient....
  • OKhttpClient 简单使用总结

    千次阅读 2017-12-21 15:07:33
    http优化,由httpClient改为OKHttpClient,研究了一下,网上资料不多大部分是安卓的,就着httpClient的入参简单写了一个公共方法,因为上一层使用了hystrix,就没有使用异步调用。后期看业务需要增加OKHttp的拦截和...
  • } } /** * json参数的同步get请求 返回对象 * * @param url 请求URL * @param header 请求头 * @param clazz 返回对象类型 * @return 返回对象 */ public static E doGetReturnJson(String url, Map header, Class ...
  • 之前此工程是用于测试webView的,所以工程名不是叫okHttpClient. Server之前文章已经有提到如何搭建了,见 Windows上Apache服务器的下载和安装 流程是从apache server读取一个文件json文件,然后解析显示到屏幕上...
  • 安卓使用OkHttpClient进行网络请求

    千次阅读 2016-05-13 00:02:40
    一般的安卓开发中,我们进行网络请求使用的都是HttpUrlClient。但使用起来有点繁琐。可以通过OkHttpClient减少代码量,源码为...同步(Synchronous Get)首先引入Okhttpclient的依赖包compile 'com.squareup.okht
  • 1:创建一个OkHttpClient(发起请求的客户端) OkHttpClient.Builder builder = new OkHttpClient.Builder();//首先拿到创建者 builder.connectTimeout(30, TimeUnit.SECONDS); builder.readTimeout(30, TimeUni....

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 5,695
精华内容 2,278
关键字:

okhttpclient同步