2. OkHttp 초기 화

13559 단어
OkHttp 의 전체 초기 화 는 Builder 형식 으로 만 들 었 습 니 다. 디자인 모델 에 대해 잘 모 르 는 학생 들 은 보 러 갈 수 있 습 니 다.
https://github.com/mirsfang/ExamplesOfDesignPatterns
이제 본론 으로 들 어가 보도 록 하 겠 습 니 다.
OkHttpClient 초기 화
OkHttpClient 는 코드 에서 이 렇 습 니 다.
 OkHttpClient okHttpClient=new OkHttpClient.Builder().build();

OkHttpClient 는 Call 의 공장 으로 http 요청 을 보 내 고 그의 응답 을 읽 을 수 있 습 니 다. 보통 OkHttpClient 는 공식 적 으로 그의 공통성 을 제안 합 니 다. 개인 적 으로 는 전체 App 이나 client 가 하나 밖 에 없다 는 것 을 이해 합 니 다. 그렇지 않 으 면 모든 client 는 자신의 연결 풀 과 스 레 드 풀 을 가지 고 있 습 니 다. 모든 client 는 연결 풀 과 스 레 드 풀 을 만 드 는 것 입 니 다. 이런 자원 들 은 낭 비 될 것 입 니 다.
우 리 는 위의 코드 를 따라 OkHttpClient 를 분석 했다.
OkHttpClient 는 Builder 의 build 를 통 해 프레임 을 만 드 는 OkHttpClient 이기 때문에 Builder 류 와 Builder 의 build 방법 을 먼저 봅 니 다.
우선 Builder 의 build 방법:
public OkHttpClient build() {
      return new OkHttpClient(this);
}

어색 합 니 다. Builder 의 구축 방법 을 살 펴 보 겠 습 니 다.
 public Builder() {
      dispatcher = new Dispatcher();//                。
      protocols = DEFAULT_PROTOCOLS; //  ,  http1.1 http2
      connectionSpecs = DEFAULT_CONNECTION_SPECS;//     (HTTPS TSL  )
      eventListenerFactory = EventListener.factory(EventListener.NONE);
      proxySelector = ProxySelector.getDefault();//     
      cookieJar = CookieJar.NO_COOKIES;//cookie  
      socketFactory = SocketFactory.getDefault();//sokcet  
      hostnameVerifier = OkHostnameVerifier.INSTANCE;
      certificatePinner = CertificatePinner.DEFAULT;
      proxyAuthenticator = Authenticator.NONE;
      authenticator = Authenticator.NONE;
      connectionPool = new ConnectionPool();//   
      dns = Dns.SYSTEM;                 //Dns
      followSslRedirects = true;        
      followRedirects = true;
      retryOnConnectionFailure = true;
      connectTimeout = 10_000;
      readTimeout = 10_000;
      writeTimeout = 10_000;
      pingInterval = 0;
    }

위 에서 볼 수 있 듯 이 Builder 는 주로 기본 적 인 파 라 메 터 를 구축 한 것 입 니 다. 위 에 비교적 중요 한 파 라 메 터 를 주석 하 였 습 니 다. OkHttpClient 의 구조 함 수 를 살 펴 보 겠 습 니 다.
 OkHttpClient(Builder builder) {
    this.dispatcher = builder.dispatcher;
    this.proxy = builder.proxy;
    this.protocols = builder.protocols;
    this.connectionSpecs = builder.connectionSpecs;
    this.interceptors = Util.immutableList(builder.interceptors);
    this.networkInterceptors = Util.immutableList(builder.networkInterceptors);
    this.eventListenerFactory = builder.eventListenerFactory;
    this.proxySelector = builder.proxySelector;
    this.cookieJar = builder.cookieJar;
    this.cache = builder.cache;
    this.internalCache = builder.internalCache;
    this.socketFactory = builder.socketFactory;

    boolean isTLS = false;
    for (ConnectionSpec spec : connectionSpecs) {
      isTLS = isTLS || spec.isTls(); 
    }

    if (builder.sslSocketFactory != null || !isTLS) {
      this.sslSocketFactory = builder.sslSocketFactory;
      this.certificateChainCleaner = builder.certificateChainCleaner;
    } else {
      //       
      X509TrustManager trustManager = systemDefaultTrustManager();
      this.sslSocketFactory = systemDefaultSslSocketFactory(trustManager);
      this.certificateChainCleaner = CertificateChainCleaner.get(trustManager);
    }

    this.hostnameVerifier = builder.hostnameVerifier;
    this.certificatePinner = builder.certificatePinner.withCertificateChainCleaner(
        certificateChainCleaner);
    this.proxyAuthenticator = builder.proxyAuthenticator;
    this.authenticator = builder.authenticator;
    this.connectionPool = builder.connectionPool;
    this.dns = builder.dns;
    this.followSslRedirects = builder.followSslRedirects;
    this.followRedirects = builder.followRedirects;
    this.retryOnConnectionFailure = builder.retryOnConnectionFailure;
    this.connectTimeout = builder.connectTimeout;
    this.readTimeout = builder.readTimeout;
    this.writeTimeout = builder.writeTimeout;
    this.pingInterval = builder.pingInterval;
  }


위 에서 보면 기본적으로 전체 OkHttpClient 의 값 은 모두 Builder 에서 이 루어 진 것 이 고 구체 적 인 매개 변수 등 이 사 용 될 때 다시 이야기 합 니 다.
Request 요청 초기 화 과정
Request 빌 드 와 OkHttpClient 빌 드 의 기본 차 이 는 많 지 않 지만, Url 이 비어 있 지 않 으 면 IllegalState Exception 의 이상 을 던 집 니 다.
 public Request build() {
      if (url == null) throw new IllegalStateException("url == null");
      return new Request(this);
 }

같은 방식 으로 Builder 의 구축 함 수 를 봅 니 다.
 public Builder() {
      this.method = "GET";
      this.headers = new Headers.Builder();
 }

기본 값 은 get 요청 입 니 다. Headrs 를 초기 화 하 는 것 도 builder 형식 으로 초기 길이 가 20 인 List 입 니 다.Builder 의 선택 가능 한 매개 변 수 는 다음 과 같 습 니 다.
    HttpUrl url;    //url
    String method;  //  
    Headers.Builder headers;//header   
    RequestBody body;//   
    Object tag;//tag

조금 만 봐 도 큰 문제 가 없습니다. 모두 가 가 져 야 할 것 을 요구 하 는 것 입 니 다. 그러나 이 tag 는 무엇 을 하 는 것 입 니까? 그의 방법 설명 을 보 세 요.
 /**
     * Attaches {@code tag} to the request. It can be used later to cancel the request. If the tag
     * is unspecified or null, the request is canceled by using the request itself as the tag.
     *
     *  {@code tag}     。             。           ,                   。
     *
     */
    public Builder tag(Object tag) {
      this.tag = tag;
      return this;
    }

로 고 를 설정 하 는 것 입 니 다. 이 로 고 를 사용 하여 요청 을 취소 할 수 있 습 니 다. 지정 하지 않 아 도 괜 찮 습 니 다. 비어 있 으 면 자체 Reques 를 사용 하 십시오. 관련 코드 는 여기에 있 습 니 다.

  Request(Builder builder) {
    this.url = builder.url;
    this.method = builder.method;
    this.headers = builder.headers.build();
    this.body = builder.body;
    this.tag = builder.tag != null ? builder.tag : this;//    
  }

method
builder 에 다양한 요청 방법 (get post head delete 등) 이 있 습 니 다. 코드 를 보 니 Request. Builder 를 호출 한 method 방법 은 다음 과 같 습 니 다.
 public Builder head() {
      return method("HEAD", null);
 }

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

public Builder delete(RequestBody body) {
      return method("DELETE", body);
}

public Builder delete() {
      return delete(Util.EMPTY_REQUEST);
}

public Builder put(RequestBody body) {
      return method("PUT", body);
}

public Builder patch(RequestBody body) {
      return method("PATCH", body);
}

실현 방법 은 다음 과 같다.
 public Builder method(String method, RequestBody body) {
      if (method == null) throw new NullPointerException("method == null");
      if (method.length() == 0) throw new IllegalArgumentException("method.length() == 0");
      if (body != null && !HttpMethod.permitsRequestBody(method)) {
        throw new IllegalArgumentException("method " + method + " must not have a request body.");
      }
      if (body == null && HttpMethod.requiresRequestBody(method)) {
        throw new IllegalArgumentException("method " + method + " must have a request body.");
      }
      this.method = method;
      this.body = body;
      return this;
 }

여러 가지 요청 에 따 른 규칙 적 판단 을 판단 한 것 이기 도 합 니 다.
head
일반적인 요청 에서 header 도 자주 사용 되 며, okhttp 에서 header 의 구축 도 request 에서 관련 된 방법 입 니 다.
Builder header(String name, String value)
Builder addHeader(String name, String value) 
Builder removeHeader(String name)
Builder headers(Headers headers)

근원 을 추적 하 는 것 은 모두 Headers. Builder headers 이다.의 조작, 그 는 위의 것 과 마찬가지 로 모두 Builder 를 통 해 구 조 를 만 들 었 기 때문에 우 리 는 먼저 그의 Builder 를 직접 보 았 다.
 public static final class Builder {
    final List namesAndValues = new ArrayList<>(20);

    /**
     * Add a header line without any validation. Only appropriate for headers from the remote peer
     * or cache.
     *             。                。
     */
    Builder addLenient(String line) {
      int index = line.indexOf(":", 1);
      if (index != -1) {
        return addLenient(line.substring(0, index), line.substring(index + 1));
      } else if (line.startsWith(":")) {
        // Work around empty header names and header names that start with a
        // colon (created by old broken SPDY versions of the response cache).
        return addLenient("", line.substring(1)); // Empty header name.
      } else {
        return addLenient("", line); // No header name.
      }
    }

    /** 
    *   Add an header line containing a field name, a literal colon, and a value.
    *             ,          。
    */
    public Builder add(String line) {
      int index = line.indexOf(":");
      if (index == -1) {
        throw new IllegalArgumentException("Unexpected header: " + line);
      }
      return add(line.substring(0, index).trim(), line.substring(index + 1));
    }

    /** Add a field with the specified value.
    *             
    */
    public Builder add(String name, String value) {
      checkNameAndValue(name, value);
      return addLenient(name, value);
    }

    /**
     * Add a field with the specified value without any validation. Only appropriate for headers
     * from the remote peer or cache.
     *
     *           ,        。                。
     */
    Builder addLenient(String name, String value) {
      namesAndValues.add(name);
      namesAndValues.add(value.trim());
      return this;
    }

    public Builder removeAll(String name) {
      for (int i = 0; i < namesAndValues.size(); i += 2) {
        if (name.equalsIgnoreCase(namesAndValues.get(i))) {
          namesAndValues.remove(i); // name
          namesAndValues.remove(i); // value
          i -= 2;
        }
      }
      return this;
    }

    /**
     * Set a field with the specified value. If the field is not found, it is added. If the field 
     * is found, the existing values are replaced.
     *             。        ,      。       ,      。
     */
    public Builder set(String name, String value) {
      checkNameAndValue(name, value);
      removeAll(name);
      addLenient(name, value);
      return this;
    }

    private void checkNameAndValue(String name, String value) {
      if (name == null) throw new NullPointerException("name == null");
      if (name.isEmpty()) throw new IllegalArgumentException("name is empty");
      for (int i = 0, length = name.length(); i < length; i++) {
        char c = name.charAt(i);
        if (c <= '\u0020' || c >= '\u007f') {
          throw new IllegalArgumentException(Util.format(
              "Unexpected char %#04x at %d in header name: %s", (int) c, i, name));
        }
      }
      if (value == null) throw new NullPointerException("value == null");
      for (int i = 0, length = value.length(); i < length; i++) {
        char c = value.charAt(i);
        if ((c <= '\u001f' && c != '\t') || c >= '\u007f') {
          throw new IllegalArgumentException(Util.format(
              "Unexpected char %#04x at %d in %s value: %s", (int) c, i, name, value));
        }
      }
    }

    /** Equivalent to {@code build().get(name)}, but potentially faster. */
    public String get(String name) {
      for (int i = namesAndValues.size() - 2; i >= 0; i -= 2) {
        if (name.equalsIgnoreCase(namesAndValues.get(i))) {
          return namesAndValues.get(i + 1);
        }
      }
      return null;
    }

    public Headers build() {
      return new Headers(this);
    }
  }

설명 을 통 해 우 리 는 Builder 가 올 바른 동작 을 하 는 일부 행위 의 패 키 징 을 볼 수 있 습 니 다. 이 어 Headers 안에 전체 변수 private final String[] namesAndValues; 만 있 습 니 다. 우 리 는 우리 가 자주 사용 하 는 public static Headers of(Map headers) 방법 으로 볼 수 있 습 니 다.
 public static Headers of(Map headers) {
    if (headers == null) throw new NullPointerException("headers == null");

    // Make a defensive copy and clean it up.
    //             。
    String[] namesAndValues = new String[headers.size() * 2];
    int i = 0;
    for (Map.Entry header : headers.entrySet()) {
      if (header.getKey() == null || header.getValue() == null) {
        throw new IllegalArgumentException("Headers cannot be null");
      }
      String name = header.getKey().trim();
      String value = header.getValue().trim();
      if (name.length() == 0 || name.indexOf('\0') != -1 || value.indexOf('\0') != -1) {
        throw new IllegalArgumentException("Unexpected header: " + name + ": " + value);
      }
      namesAndValues[i] = name;
      namesAndValues[i + 1] = value;
      i += 2;
    }

    return new Headers(namesAndValues);
  }

이 안에서 다음 과 같은 몇 가지 일 을 했다.
  • 공백 여 부 를 판단 한다
  • 새 임시 변수 namesAndValues 길 이 는 map * 2
  • 맵 키 와 value 를 옮 겨 다 니 면 비어 있 거나 비어 있 을 수 없습니다
  • names AndValues 하 나 는 name 이 고 두 번 째 는 value 짝수 는 name 홀수 가 value 이다. 예 를 들 어 User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; Trident/5.0; SLCC2; 그러면 그 가 이렇게 처리 한 결 과 는 namesAndValues[0]="User-Agent" namesAndValues[1]="Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; Trident/5.0; SLCC2"
  • 이다.
  • 그리고 return new Headers(namesAndValues); 전역 변수 namesAndValues
  • 에 값 을 부여 합 니 다.

    좋은 웹페이지 즐겨찾기