OkHttp——请求流程源码剖析总结

T12 电烙铁简单程序逻辑 通过分析就可以知道,发热丝本身即是发热体,也是热偶的工作端。怎么可能是热电动势呢,确实,但不要忘了T12-OUTPUT端的电压是PWM波,理论上是有单片机PWM电压为0 v时的,而T12-OUTPUT端的电压此时也应为0。在T12烙铁头中,PID控制算法通过监测温度传感器的输出,计算当前温度与设定温度之间的差异,并根据这一差异调节加热功率,以实现烙铁头温度的精确控制。这段代码实现了一个简单的增量式PID控制器,通过计算当前温度与期望温度之间的差异以及历史偏差值,调节输出控制量,实现对温度的精确控制。 阅读详情


基本的用法不赘述了,在官网就能看到。
https://github.com/square/okhttp

OkHttp 整体的请求流程

1. 请求处理开始

当需要请求网络时,我们会调用。

 client.newCall(request).enqueue();
 client.newCall(request).execute();

他们都是从newCall()调用

  @Override public Call newCall(Request request) {
    return new RealCall(this, request);
  }

newCall()方法new了一个RealCall对象,他的enqueue请求源码

  void enqueue(Callback responseCallback, boolean forWebSocket) {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    client.dispatcher().enqueue(new AsyncCall(responseCallback, forWebSocket));
  }

execute请求源码:

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

能够看到他们的最终的请求是dispatcher来完成的。

2. Dispather任务调度

Dispatcher主要用于任务的调度。

他主要有如下变量


  private int maxRequests = 64;//最大请求数
  
  private int maxRequestsPerHost = 5;//最大请求主机数

  /** Executes calls. Created lazily. */
  private 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<>();//正在运行的同步任务队列

他的构造方法如下

  public Dispatcher(ExecutorService executorService) {
    this.executorService = executorService;
  }

  public Dispatcher() {
  }

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

Dispatcher可以接收用户自己设置的线程池,如果没有设置线程池则创建默认的线程池。

当调用enqueue方法实际上是调用了Dispatcher的enqueue方法

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

如果满足

  • 运行的异步线程小于最大请求数量
  • 运行主机数小于5

则会把他加入到线程池并执行

否则把他加入到等待队列。

后过头看一下Dispatcher当时传入的参数。

client.dispatcher().enqueue(new AsyncCall(responseCallback, forWebSocket));
Dispaer.enqueue(AsyncCall call) {}

最终给线程池传入的是一个AsyncCall对象,他实现了Runnable,run方法会调用AsyncCall的execute方法。

  boolean signalledCallback = false;
  try {
    Response response = getResponseWithInterceptorChain(forWebSocket);
    if (canceled) {
      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!
      logger.log(Level.INFO, "Callback failure for " + toLoggableString(), e);
    } else {
      responseCallback.onFailure(RealCall.this, e);
    }
  } finally {
    client.dispatcher().finished(this);
  }
}

暂且先看无论如何都会执行的client.dispatcher().finished(this);

  synchronized void finished(AsyncCall call) {
    if (!runningAsyncCalls.remove(call)) throw new AssertionError("AsyncCall wasn't running!");
    promoteCalls();
  }

他首先将这个请求从runningAsyncCalls中移除了,然后调用了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.
    }
  }

可以很清晰的看到这实际上就是取出下一个请求并执行。

让我们再回到AsyncCall的execute方法。

```java
  boolean signalledCallback = false;
  try {
    Response response = getResponseWithInterceptorChain(forWebSocket);
    if (canceled) {
      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!
      logger.log(Level.INFO, "Callback failure for " + toLoggableString(), e);
    } else {
      responseCallback.onFailure(RealCall.this, e);
    }
  } finally {
    client.dispatcher().finished(this);
  }
}

注意这句代码

Response response = getResponseWithInterceptorChain(forWebSocket);

他返回了一个Response 对象,这就是在请求网络。

3. Interceptor拦截器

跟进到getResponseWithInterceptorChain()方法。

  private Response getResponseWithInterceptorChain(boolean forWebSocket) throws IOException {
    Interceptor.Chain chain = new ApplicationInterceptorChain(0, originalRequest, forWebSocket);
    return chain.proceed(originalRequest);
  }

getResponseWithInterceptorChain()方法创建了ApplicationInterceptorChain对象,他是RealCall的内部类,是一个拦截器。

然后他执行了proceed方法。

@Override public Response proceed(Request request) throws IOException {
   // If there's another interceptor in the chain, call that.
   if (index < client.interceptors().size()) {
     Interceptor.Chain chain = new ApplicationInterceptorChain(index + 1, request, forWebSocket);
     //从拦截器列表中取出拦截器
     Interceptor interceptor = client.interceptors().get(index);
     Response interceptedResponse = interceptor.intercept(chain);

     if (interceptedResponse == null) {
       throw new NullPointerException("application interceptor " + interceptor
           + " returned null");
     }

     return interceptedResponse;
   }

   // No more interceptors. Do HTTP.
   return getResponse(request, forWebSocket);
 }

proceed方法每次从拦截器列表取出拦截器。当存在多个拦截器时会在Response interceptedResponse = interceptor.intercept(chain);阻塞,并等待下一个拦截器的调用返回。

拦截器是一种能够监控、重写、重试调用的机制。通常情况下,拦截器用来添加、移除、转换请求和相应头部信息。比如将域名替换为IP地址,在请求头中添加host属性等等。

后头看最后一行

return getResponse(request, forWebSocket);

如果没有更多拦截器的话就会执行网络请求。

  /**
   * Performs the request and returns the response. May return null if this call was canceled.
   */
  Response getResponse(Request request, boolean forWebSocket) throws IOException {
    // Copy body metadata to the appropriate request headers.
    RequestBody body = request.body();
    if (body != null) {
      Request.Builder requestBuilder = request.newBuilder();

      MediaType contentType = body.contentType();
      if (contentType != null) {
        requestBuilder.header("Content-Type", contentType.toString());
      }

      long contentLength = body.contentLength();
      if (contentLength != -1) {
        requestBuilder.header("Content-Length", Long.toString(contentLength));
        requestBuilder.removeHeader("Transfer-Encoding");
      } else {
        requestBuilder.header("Transfer-Encoding", "chunked");
        requestBuilder.removeHeader("Content-Length");
      }

      request = requestBuilder.build();
    }

    // Create the initial HTTP engine. Retries and redirects need new engine for each attempt.
    engine = new HttpEngine(client, request, false, false, forWebSocket, null, null, null);

    int followUpCount = 0;
    while (true) {
      if (canceled) {
        engine.releaseStreamAllocation();
        throw new IOException("Canceled");
      }

      boolean releaseConnection = true;
      try {
        engine.sendRequest();
        engine.readResponse();
        releaseConnection = false;
      } catch (RequestException e) {
        // The attempt to interpret the request failed. Give up.
        throw e.getCause();
      } catch (RouteException e) {
        // The attempt to connect via a route failed. The request will not have been sent.
        HttpEngine retryEngine = engine.recover(e.getLastConnectException(), null);
        if (retryEngine != null) {
          releaseConnection = false;
          engine = retryEngine;
          continue;
        }
        // Give up; recovery is not possible.
        throw e.getLastConnectException();
      } catch (IOException e) {
        // An attempt to communicate with a server failed. The request may have been sent.
        HttpEngine retryEngine = engine.recover(e, null);
        if (retryEngine != null) {
          releaseConnection = false;
          engine = retryEngine;
          continue;
        }

        // Give up; recovery is not possible.
        throw e;
      } finally {
        // We're throwing an unchecked exception. Release any resources.
        if (releaseConnection) {
          StreamAllocation streamAllocation = engine.close();
          streamAllocation.release();
        }
      }

      Response response = engine.getResponse();
      Request followUp = engine.followUpRequest();

      if (followUp == null) {
        if (!forWebSocket) {
          engine.releaseStreamAllocation();
        }
        return response;
      }

      StreamAllocation streamAllocation = engine.close();

      if (++followUpCount > MAX_FOLLOW_UPS) {
        streamAllocation.release();
        throw new ProtocolException("Too many follow-up requests: " + followUpCount);
      }

      if (!engine.sameConnection(followUp.url())) {
        streamAllocation.release();
        streamAllocation = null;
      }

      request = followUp;
      engine = new HttpEngine(client, request, false, false, forWebSocket, streamAllocation, null,
          response);
    }
  }

有点长,其他的先放下。

注意到其中的这样一段代码

 try {
        engine.sendRequest();
        engine.readResponse();
        releaseConnection = false;
      } 

看一看到创建了HTTPEngine类并调用了sendRequest和readResponse方法。

4. 缓存策略

  public void sendRequest() throws RequestException, RouteException, IOException {
    if (cacheStrategy != null) return; // Already sent.
    if (httpStream != null) throw new IllegalStateException();

    Request request = networkRequest(userRequest);

	//获取client中的Cache,同时Cache在初始化时会读取缓存目录中曾经请求过的所有信息。
    InternalCache responseCache = Internal.instance.internalCache(client);
    Response cacheCandidate = responseCache != null
        ? responseCache.get(request)
        : null;

    long now = System.currentTimeMillis();
    cacheStrategy = new CacheStrategy.Factory(now, request, cacheCandidate).get();
  	//网络
    networkRequest = cacheStrategy.networkRequest;
    //缓存
    cacheResponse = cacheStrategy.cacheResponse;

    if (responseCache != null) {
      responseCache.trackResponse(cacheStrategy);
    }

	//缓存不可用
    if (cacheCandidate != null && cacheResponse == null) {
      closeQuietly(cacheCandidate.body()); // The cache candidate wasn't applicable. Close it.
    }

	//	不进行网络请求,也不存在缓存。返回504
    // If we're forbidden from using the network and the cache is insufficient, fail.
    if (networkRequest == null && cacheResponse == null) {
      userResponse = new Response.Builder()
          .request(userRequest)
          .priorResponse(stripBody(priorResponse))
          .protocol(Protocol.HTTP_1_1)
          .code(504)
          .message("Unsatisfiable Request (only-if-cached)")
          .body(EMPTY_BODY)
          .build();
      return;
    }

	//不进行网络请求,但是缓存有效,返回缓存。
    // If we don't need the network, we're done.
    if (networkRequest == null) {
      userResponse = cacheResponse.newBuilder()
          .request(userRequest)
          .priorResponse(stripBody(priorResponse))
          .cacheResponse(stripBody(cacheResponse))
          .build();
      userResponse = unzip(userResponse);
      return;
    }

    // We need the network to satisfy this request. Possibly for validating a conditional GET.
	//    访问网络
    boolean success = false;
    try {
      httpStream = connect();
      httpStream.setHttpEngine(this);

      if (writeRequestHeadersEagerly()) {
        long contentLength = OkHeaders.contentLength(request);
        if (bufferRequestBody) {
          if (contentLength > Integer.MAX_VALUE) {
            throw new IllegalStateException("Use setFixedLengthStreamingMode() or "
                + "setChunkedStreamingMode() for requests larger than 2 GiB.");
          }

          if (contentLength != -1) {
            // Buffer a request body of a known length.
            httpStream.writeRequestHeaders(networkRequest);
            requestBodyOut = new RetryableSink((int) contentLength);
          } else {
            // Buffer a request body of an unknown length. Don't write request headers until the
            // entire body is ready; otherwise we can't set the Content-Length header correctly.
            requestBodyOut = new RetryableSink();
          }
        } else {
          httpStream.writeRequestHeaders(networkRequest);
          requestBodyOut = httpStream.createRequestBody(networkRequest, contentLength);
        }
      }
      success = true;
    } finally {
      // If we're crashing on I/O or otherwise, don't leak the cache body.
      if (!success && cacheCandidate != null) {
        closeQuietly(cacheCandidate.body());
      }
    }
  }

上面的代码显然实在进行网络请求,首先是对缓存做了处理。

cacheCandidate是上次与服务器交互缓存的Response,缓存基于Map,key是请求中url的md5,value是在文件中查询到的缓存,页面置换基于LRU算法,我们只需要知道cacheCandidate是一个可以读取缓存Header的Response即可。

然后根据cacheStrategy得到了networkRequest和cacheResponse这两个值,根据这两个值得数据是否为null,来进一步的处理,在networkRequest和CacheResponse都为null的情况下,也就是不进行网络请求并且缓存不存在或者过期,这是返回504,。当networkRequest为null时,也就是不进行网络请求,如果缓存可以使用则直接返回缓存,其他情况为请求网络。

然后就是readResponse()方法:

  public void readResponse() throws IOException {
    if (userResponse != null) {
      return; // Already ready.
    }
    if (networkRequest == null && cacheResponse == null) {
      throw new IllegalStateException("call sendRequest() first!");
    }
    if (networkRequest == null) {
      return; // No network response to read.
    }

    Response networkResponse;

    if (forWebSocket) {
      httpStream.writeRequestHeaders(networkRequest);
      networkResponse = readNetworkResponse();
    } else if (!callerWritesRequestBody) {
      networkResponse = new NetworkInterceptorChain(0, networkRequest).proceed(networkRequest);
    } else {
      // Emit the request body's buffer so that everything is in requestBodyOut.
      if (bufferedRequestBody != null && bufferedRequestBody.buffer().size() > 0) {
        bufferedRequestBody.emit();
      }

      // Emit the request headers if we haven't yet. We might have just learned the Content-Length.
      if (sentRequestMillis == -1) {
        if (OkHeaders.contentLength(networkRequest) == -1
            && requestBodyOut instanceof RetryableSink) {
          long contentLength = ((RetryableSink) requestBodyOut).contentLength();
          networkRequest = networkRequest.newBuilder()
              .header("Content-Length", Long.toString(contentLength))
              .build();
        }
        httpStream.writeRequestHeaders(networkRequest);
      }

      // Write the request body to the socket.
      if (requestBodyOut != null) {
        if (bufferedRequestBody != null) {
          // This also closes the wrapped requestBodyOut.
          bufferedRequestBody.close();
        } else {
          requestBodyOut.close();
        }
        if (requestBodyOut instanceof RetryableSink) {
          httpStream.writeRequestBody((RetryableSink) requestBodyOut);
        }
      }

      networkResponse = readNetworkResponse();
    }

    receiveHeaders(networkResponse.headers());

    // If we have a cache response too, then we're doing a conditional get.
    if (cacheResponse != null) {
      if (validate(cacheResponse, networkResponse)) {
        userResponse = cacheResponse.newBuilder()
            .request(userRequest)
            .priorResponse(stripBody(priorResponse))
            .headers(combine(cacheResponse.headers(), networkResponse.headers()))
            .cacheResponse(stripBody(cacheResponse))
            .networkResponse(stripBody(networkResponse))
            .build();
        networkResponse.body().close();
        releaseStreamAllocation();

        // Update the cache after combining headers but before stripping the
        // Content-Encoding header (as performed by initContentStream()).
        InternalCache responseCache = Internal.instance.internalCache(client);
        responseCache.trackConditionalCacheHit();
        responseCache.update(cacheResponse, stripBody(userResponse));
        userResponse = unzip(userResponse);
        return;
      } else {
        closeQuietly(cacheResponse.body());
      }
    }

    userResponse = networkResponse.newBuilder()
        .request(userRequest)
        .priorResponse(stripBody(priorResponse))
        .cacheResponse(stripBody(cacheResponse))
        .networkResponse(stripBody(networkResponse))
        .build();

    if (hasBody(userResponse)) {
      maybeCache();
      userResponse = unzip(cacheWritingResponse(storeRequest, userResponse));
    }
  }

这个方法主要是解析响应报头,如果有缓存就用缓存的数据,则用缓存的数据并更新缓存,否则就用网络请求返回的数据。

注意这句

  if (validate(cacheResponse, networkResponse)) 

这是检查缓存有效的方法。

  private static boolean validate(Response cached, Response network) {
    if (network.code() == HTTP_NOT_MODIFIED) {
      return true;
    }

    // The HTTP spec says that if the network's response is older than our
    // cached response, we may return the cache's response. Like Chrome (but
    // unlike Firefox), this client prefers to return the newer response.
    Date lastModified = cached.headers().getDate("Last-Modified");
    if (lastModified != null) {
      Date networkLastModified = network.headers().getDate("Last-Modified");
      if (networkLastModified != null
          && networkLastModified.getTime() < lastModified.getTime()) {
        return true;
      }
    }

    return false;
  }

如果服务器返回的状态码是304,那么缓存有效。

如果缓存过期或者强制放弃缓存,则缓存策略全部交给服务器判断,客户端只需求发送GET请求即可。

条件GET请求有两种方式:一种是Last-Modified-Date,一种是ETag。这里使用Last-Modified-Date,通过缓存网络请求相应中的Last-Modified来计算是否是最新数据。

5. 失败重连

回到RealCall的getResponse方法

  /**
   * Performs the request and returns the response. May return null if this call was canceled.
   */
  Response getResponse(Request request, boolean forWebSocket) throws IOException {
      ······
      boolean releaseConnection = true;
      try {
        engine.sendRequest();
        engine.readResponse();
        releaseConnection = false;
      } catch (RequestException e) {
        // The attempt to interpret the request failed. Give up.
        throw e.getCause();
      } catch (RouteException e) {
        // The attempt to connect via a route failed. The request will not have been sent.
        //1. 失败重连
        HttpEngine retryEngine = engine.recover(e.getLastConnectException(), null);
        if (retryEngine != null) {
          releaseConnection = false;
          engine = retryEngine;
          continue;
        }
        // Give up; recovery is not possible.
        throw e.getLastConnectException();
      } catch (IOException e) {
        // An attempt to communicate with a server failed. The request may have been sent.
        //2. 失败重连
        HttpEngine retryEngine = engine.recover(e, null);
        if (retryEngine != null) {
          releaseConnection = false;
          engine = retryEngine;
          continue;
        }

        // Give up; recovery is not possible.
        throw e;
      } finally {
        // We're throwing an unchecked exception. Release any resources.
        if (releaseConnection) {
          StreamAllocation streamAllocation = engine.close();
          streamAllocation.release();
        }
      }

      Response response = engine.getResponse();
      Request followUp = engine.followUpRequest();

      if (followUp == null) {
        if (!forWebSocket) {
          engine.releaseStreamAllocation();
        }
        return response;
      }

      StreamAllocation streamAllocation = engine.close();

      if (++followUpCount > MAX_FOLLOW_UPS) {
        streamAllocation.release();
        throw new ProtocolException("Too many follow-up requests: " + followUpCount);
      }

      if (!engine.sameConnection(followUp.url())) {
        streamAllocation.release();
        streamAllocation = null;
      }

      request = followUp;
      engine = new HttpEngine(client, request, false, false, forWebSocket, streamAllocation, null,
          response);
    }
  }

在上述代码的1、2处当发生IOException或RouteException时会执行

HttpEngine retryEngine = engine.recover(e.getLastConnectException(), null);
  public HttpEngine recover(IOException e, Sink requestBodyOut) {
    if (!streamAllocation.recover(e, requestBodyOut)) {
      return null;
    }

    if (!client.retryOnConnectionFailure()) {
      return null;
    }

    StreamAllocation streamAllocation = close();

    // For failure recovery, use the same route selector with a new connection.
    return new HttpEngine(client, userRequest, bufferRequestBody, callerWritesRequestBody,
        forWebSocket, streamAllocation, (RetryableSink) requestBodyOut, priorResponse);
  }

实际上就是重新创建了HttpEngine并返回来完成重建。

这就是OKHttp整体的请求流程。流程图如下:

在这里插入图片描述

aistudio高效解压zip文件的实用技巧 本文详细介绍了在百度AI Studio平台上高效解压ZIP文件的实用技巧。针对深度学习项目数据准备中的常见痛点,重点讲解了使用Python zipfile库和Linux unzip命令两种核心方法,并提供了路径设置、大文件处理、中文乱码等关键问题的解决方案,帮助用户提升数据处理效率。 阅读详情

相关推荐

IDEA系列(四):IDEA导入Maven工程项目并配置Tomcat启动

本片文章主要介绍如何导入一个存在的maven项目并配置Tomcat启动项目,方便自己使用,也分享出来,希望可以帮助到其他人。

低调D树苗的博客 1243

OkHttp源码解析(一)同步请求和异步请求

接下来几篇文章,将会进行OkHttp的源码解析。还记得去年花了半天时间去进行Volley的源码解析,其实去年那篇文章我也说过,Volley已经算是一个过时的网络请求框架。因此,接下来,我会选择主流的网络请求框架OkHttp进行源码解析。今天,主要从OkHttp的两种请求方式开始说起,也就是同步请求和异步请求。 一、前言 作为一名优秀的程序员,一定要有分析源码的经...

zhaojun的博客 1万+

【算法】—前缀和与差分详解

前缀和指一个数组的某下标之前的所有数组元素的和(即数列的前n项求和),前缀和是一种重要的预处理,能够降低算法的时间复杂度,可以快速地求出某一段的和,对于处理区间之间的问题是往往十分高效相比较其他算法而言,前缀和更像是一种解题的技巧,一种优化方式输入一个长度为 n 的整数序列。接下来再输入 m 个询问,每个询问输入一对 l,r 对于每个询问,输出原序列中从第 l 个数到第 r 个数的和5 32 1 3 6 41 21 32 4123453610。

DAY I 的成长之路 6022

OkHttp架构—异步请求enqueue(不完整篇)

在分发器Dispatcher类中,有一个这样的方法,这个方法内部就是创建线程池的}}线程池和拦截器懒得写了,lay了。。整体流程:创建RealCall对象>>重写RealCall类enqueue()>>调用Dispatcher类enqueue()来调配任务(队列分配)>>线程池执行running队列>>线程执行完毕队列移动>>线程池执行>>队列移动。。。。。

https://blog.youkuaiyun.com/qFAFAF 2463

速腾聚创激光雷达复现FAST-LIO

记录复现FAST-LIO算法的过程和,代码梳理和理解。FAST-LIO的简化版、注释版。速腾激光雷达

abanchao的博客 2928

CDD数据库文件制作(二)——DTC配置

1.DTC库 2.新建DTC DTC相关介绍:https://blog.csdn.net/LOVE135149/article/details/122565443 ① 选择DTC数据库 ② 点击新建DTC ③ DTC参数编辑 3.将DTC库中的DTC加载到Variant中 找到Fault Memory的DTC Table,鼠标放在DTC Table区域,右键点击选择Copy … Reference from Available DTCs…,即可将DTC库中的故障码加载至Variant中。 虽然选择Co

LOVE135149的博客 1万+

OkHttp深入学习(二)——网络

在上一篇《OkHttp深入学习(一)——初探》中对OkHttpClient中的使用和Dispatcher进行了学习,无论我们是发送同步请求还是异步请求,最终都会通过调用RealCall的getResponseWithInterceptorChain()方法来获取request对应的response。本篇就从该方法开始,来分析OkHttp是如何实现网络通信的。 RealCall.class

evan_man的专栏 5039

uniapp - 实现手机端H5网页直播功能效果,拿到后端推流地址或播放资源后进行前端的流畅播放和画面展示功能,uniapp实时推流/拉流直播系统详细教程(提供完整示例代码,一键复制开箱即用!)

uniapp,h5网页,手机端H5网页,uniapp h5直播功能页面,uniapp直播实时推送显示,uniapp h5直播怎么做,怎么使用uniapp去实现移动端手机H5直播,h5 vue+uniapp怎么做直播,uniapp中使用videojs构建H5直播播放器,uniappH5端怎么实现直播推流,uniapp如何实现直播,直播聊天功能,h5平台端直播用什么插件,uniapp手机网页直播用什么组件插件播放器,uniapp手机H5直播播放器详细教程,直播项目,怎么使用vue+uniapp怎么做H5实时直播

✅ 精选推荐文章 8792

OKHttp源码解析(二)

上一篇文章里我们大致分析了OkHttp整个请求的流程,重点分析了具体发送请求前都做了哪些操作,这篇文章我们将继续上篇的内容,看看在发送请求过程中做了什么,看了上篇文章的应该都知道,我们将从HttpEngine的sendRequest入手看是如何操作的public void sendRequest() throws RequestException, RouteException, IOExcepti

chenzujie的博客 9630

10|Git Diff 与增量代码识别:本次到底改了哪些代码?

本文介绍 Git Diff 和增量代码识别,帮助你理解精准测试如何先找到本次真正变化的代码。测试开发工程师。Java 后端开发工程师。质量平台 / DevOps 平台建设者。想了解精准测试落地方法的技术负责人。

专注 AI 精准测试平台实战落地,分享从代码 Diff 到回归决策的完整工程闭环。主张“先建数据链路,再上 AI 智能”,让测试从“靠经验”走向“基于风险”。 160

ABAP RAP 行为建模深入学习:从 Behavior Definition 到 Behavior Implementation 的完整实践指南

ABAP RAP开发指南:业务行为定义与实现 本文介绍SAP RAP框架中业务对象行为的开发方法: 分层设计 Behavior Definition声明业务能力(增删改查、校验、动作等) Behavior Implementation在ABAP类中编写具体逻辑 核心组件 使用BDL定义业务行为 通过FOR BEHAVIOR OF绑定行为池类(ABP) 在本地类实现校验/确定/动作逻辑 关键特性 支持managed/unmanaged两种实现模式 提供草稿表、并发控制等企业级功能 通过EML以别名访问实体 开

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 411

okHttp3的原理剖析

okHttp3的原理剖析 1.基本使用 先来一个okHttp3最基本的使用实例 Request request = new Request.Builder() .url("https://github.com/") .addHeader("content-type","text/html") .build(); OkHttpClient okHttpClient = new OkHttpClient.Builder().build(); okHttpClien

qq_39431405的博客 1411

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

你想要的系列:网络请求框架OkHttp3全解系列 - (二)OkHttp的工作流程分析

在本系列的上一篇文章中,我们学习了OkHttp的基本用法,体验了这个网络加载框架的强大功能,以及它非常简便的API。还没有看过上一篇文章的朋友,建议先去阅读 网络请求框架OkHttp3全解系列 - (一)OkHttp的基本使用 。 如果我们想要进行get请求,那么使用少量的代码就能实现,如下所示: OkHttpClient httpClient = new OkHttpClient(); String url = "https://www.baidu.com/";

胡飞洋的技术积累 1867

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

浅析Okhttp3框架源码

浅析Okhttp3框架源码 本文主要通过对Okhttp框架的源码进行浅析,到达了解使用流程和原理的目的。主要内容包括HTTP协议简述,请求流程,Okhttp的简单使用,以及使用流程中的源码剖析。

number_cmd9的博客 1144

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

Android OkHttp3源码分析

title: Android OkHttp3源码分析 date: 2019-06-03 21:16:50 tags: Android第三方框架 文章目录title: Android OkHttp3源码分析 date: 2019-06-03 21:16:50 tags: Android第三方框架Android OkHttp3源码分析介绍源码分析简单使用OkHttpClient的创建Request的...

hesheng1024的博客 2662

OkHttp3源码分析(一)-整体执行流程

对OkHttp介绍,我们分为使用篇和源码分析篇两个系列进行介绍。 源码分析篇目录如下: (一)-整体执行流程 在OkHttp使用篇当中,我们分几篇文章介绍了OkHttp框架的使用,但并没有去分析整个框架的源码和实现。这里单独开一个系列来捋一捋OkHttp3的源码,整个过程也是我自己学习的过程,所以有不好的地方还请见谅了。 下面就进入到本篇文章主题,从源码角度分析下整体的执行流程。在OkHttp3使...

VictorCatFish的博客 485

OkHttp3源码解析(整体流程)

点击上方蓝字关注????作者:秦子帅来源:程序员进阶圈整理:code小生前言今天主要讲一下OkHttp3源码解析(整体流程),主要分为四大块:okhttp的基本用法OkHtt...

weixin_38754349的博客 243

Okhttp 请求流程梳理

最近在看 Okhttp 的源码。不得不说源码设计的很巧妙,从中能学到很多。其实网上关于 Okhttp 的文章已经很多了,自己也看了很多。但是俗话说得好,好记性不如烂笔头,当你动手的时候,你会发现你在看的时候没有注意到的很多细节。 本次要分析的 Okhttp 版本是 3.8.1,在 gradle 中引用如下: implementation 'com.squareup.okhttp3:okh...

good575654643的博客 324

【源码解析】OkHttp的工作原理

引言 OkHttp作为优秀的网络请求框架,已经得到了广大Android开发者的认可。对于它的使用方法,大家也是非常的熟悉。例如同步同步请求、异步请求等,都可以使用很简洁的逻辑来实现。由于OkHttp已经封装了繁琐复杂的请求逻辑,开发者只需要使用其提供的API就能轻松的实现网络请求,这使得开发者能将更多的精力放到业务开发上,提高了工作效率。 但是,作为一位有追求的Android开发者,不能一味的衣来...

Running&H的博客 321

OkHttp源码剖析(一) 初识okhttp

使用示例 本篇主要基于okhttp-4.9.0进行分析。下面是OkHttp主要的使用示例。 OkHttpClient client = new OkHttpClient.Builder().build(); Request request = new Request .Builder() .url("https://www.yanfriends.com") .build();

许进进的博客 2413

Retrofit框架源码深度剖析【Android热门框架分析第二弹】

网络请求框架Retrofit(Android中的Retrofit) 是一款强大的 Android 和 Java 网络请求库,通过简单而优雅的方式实现了对 RESTful API 的调用和数据解析。其基于注解的 API 定义、可扩展的转换器和适配器模式使得网络请求变得高效、灵活且易于维护。本文将深入探讨 Retrofit 的工作原理、核心功能如何支持同步、异步和 RxJava 方式的请求处理,并分析其与 OkHttp 的关系及优势,帮助开发者理解和利用 Retrofit 构建稳健的网络应用。

wei的博客 1575

OkHttp3源码分析

OKHTTP介绍 在前一篇文章中已经初步介绍了OKHTTP框架以及基本的使用,不熟悉的读者可以看我的上一篇文章OKHTTP基本使用,下面从OKHTTP的源码出发,分析整个请求的过程。笔者这里分析的OKHTTP版本是3.8.0 官方请求案例 OkHttpClient client = new OkHttpClient(); String run(String url) throws IOException { Request request = new Request.Builder() .

李林杰的博客 892

okhttp3源码初探

okhttp3源码初探简介GET请求使用源码阅读发起请求eventListener的由来真正的网络请求 简介 okhttp3 是目前android平台最流行的网络访问框架,而目前大火的retrofit底层封装的也是OKHttp。所以可以这么说,okhttp是我们做android 开发的过程中绕不过的一座大山。 但是由于okhttp实在太易用了,几句代码就可以完成网络请求的发起,导致我们很少去了解o...

蓝冷然的博客 666

Android okhttp源码调用流程分析

okhttp 是一套专门为 JVM、Android 设计的 HTTP 客户端,支持同步阻塞调用和带有回调的异步调用,可以非常简单的在 Android 项目中实现网络请求。那么 okhttp 到底是如何做到异步请求?如何实现 HTTP 协议的?本文将通过简单案例对 okhttp 的主要源码进行解读。

孙先森的博客 612

aop方式向okhttp完成网络的监听拦截_简洁易懂的 OkHttp 请求的流程源码分析

本文为 OkHttp 的第一篇文章,主要是对整个请求的流程的梳理,对 OkHttp 整体有一个感性的认识。 本文基于 OkHttp 最新的 4.8.1版本进行源分析的,源码是 Kotlin 写的,做好准备。依赖implementation 'com.squareup.okhttp3:okhttp:4.8.1'整体流程先写一个 Demo 作为源码分析的入口,首先分析异步请求的整体流程,然后再看同步请...

weixin_36307713的博客 525

Android OkHttp 源码解析 - 整体流程分析

一、前言 注:本文 OkHttp 源码解析基于 v3.14.2 ,下文中涉及到展示 OkHttp 源码的地方,都采用在 AS 里打开源码并以截图的方式展现出来,这样更加直观。 二、简单使用 请求分为同步和异步两种,同步请求通过 调用 Call.exectute() 方法直接返回当前请求的 Response,异步请求调用 Call.enqueue() 方法将请求(AsyncCall)添加...

lerendan的博客 462
上一篇: HTTP——一次完整的请求过程(详细)
下一篇: OkHttp——复用连接池源码剖析总结
PdKingLiu
博客等级 码龄9年 89粉丝 92原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值