OkHttp 학습
14011 단어 Android 오픈 소스 프레임 워 크
간단 한 소개
네트워크 요청 을 처리 하 는 오픈 소스 프로젝트 입 니 다. 안 드 로 이 드 의 가장 뜨 거 운 경량급 프레임 워 크 입 니 다. 모 바 일 결제 Square 회사 가 기 여 했 습 니 다.
현대 응용 프로그램 네트워크 처리 에서 HTTP 는 우리 가 데이터 와 미디어 를 교환 하 는 방식 이다.HTTP 를 효율적으로 실행 하면 데 이 터 를 더 빨리 불 러 오고 대역 폭 을 절약 할 수 있 습 니 다.
기본적으로 OKHTTP 는 효율 적 인 HTTP 클 라 이언 트 입 니 다. (1) HTTP/2 는 같은 호스트 의 모든 요청 에 소켓 을 공유 할 수 있 도록 지원 합 니 다.(2) 연결 풀 은 요청 지연 을 줄 였 습 니 다 (HTTP/2 를 사용 할 수 없 을 경우).(3) 투명 gzip 다운로드 크기 축소.(4) 응답 캐 시 는 중복 요청 네트워크 를 완전히 피한다.
네트워크 에 문제 가 생 겼 을 때 OKHTTP 는 견 뎌 냅 니 다. 일반적인 연결 문제 에서 자동 으로 복 구 됩 니 다.서비스 에 여러 개의 IP 주소 가 있 으 면 첫 번 째 연결 이 실 패 했 을 때 OKHTTP 는 예비 주 소 를 시도 합 니 다.이것 은 IPv 4 + IPv 6 와 불필요 한 데이터 센터 에 위탁 관리 하 는 서비스 에 필수 적 이다.OKHTTP 는 현대 TLS 기능 (TLS 1.3, ALPN, 인증서 고정) 을 지원 합 니 다.그것 은 광범 위 한 연결 을 실현 하기 위해 리 셋 으로 설정 할 수 있다.
OKHTTP 를 사용 하 는 것 은 매우 쉽다.그것 의 request/response API 는 유창 한 구축 자 모델 과 불변성 을 가지 도록 설계 되 었 다.그것 은 동기 화 차단 호출 과 리 턴 이 있 는 비동기 호출 을 지원 합 니 다.
OKHTTP 는 Android 5 + (API 레벨 21 +) 와 Java 8 + 를 지원 합 니 다.
취학 전 필수 지식
TCP 프로 토 콜 과 HTTP 프로 토 콜 을 알 아야 합 니 다.
홈 페이지
OkHttp
각 버 전의 차이: okhttp 3 와 이전 버 전의 okhttp 의 차이 분석
구 글 은 SDK 6.0 버 전에 서 HttpClient 클래스 를 취소 했다.
OkHttp 사용 하기
구체 적 인 사용 은 다음 과 같다.https://github.com/gong-shuang/MyOkHttp이 창고 의 코드 는 두 부분 을 포함 하고, 일 부 는 서버 엔 드 코드 (자바 구현) 이 며, 다른 일 부 는 안 드 로 이 드 구현 입 니 다.
HTTP 프로 토 콜 을 이해 한 후 '요청 패키지' 를 보 내 는 것 부터 서버 '응답 패키지' 를 받 아들 이 는 과정 까지 추상적으로 추정한다.
HTTP 요청 과정
순서
추상 적
구체 적
대응 okhttp
1
가방 보 낼 대상 만 들 기
구조 체 (new)
OkHttpClient 대상 만 들 기
2
패키지 (패키지 에 데 이 터 를 채 우기)
요청 헤더 와 데 이 터 를 채 웁 니 다.
패키지 구성, 콜 대상 가 져 오기
3
보내다
발송 대기 열 에 버 리 면 전문 스 레 드 에서 발송 합 니 다.
동기 화
4
기다리다
시스템 바 텀 인터페이스 에서 응답 패 키 지 를 받 아들 이 는 대기 열 에 버 립 니 다.
5
처리 응답 패키지
응답 패 키 지 를 받 은 후 응답 헤드 와 데 이 터 를 분석 합 니 다.
Callback () {} 리 셋 함수 에 성공 을 요청 하거나 실패 한 함 수 를 설정 합 니 다.
흔 한 종류
주 함수
아래 의 해석, 주요 참고: OkHttp - OkHttp 소스 코드 분석 및 OkHttp 의 디자인 사상 을 철저히 이해 합 니 다.
final class RealCall implements Call {
......
//TODO
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List interceptors = new ArrayList<>();
//TODO okhttpClient intercept
interceptors.addAll(client.interceptors());
//TODO
interceptors.add(new RetryAndFollowUpInterceptor(client));
//TODO 、
//TODO 。 , 。 。 , 。
interceptors.add(new BridgeInterceptor(client.cookieJar()));
//TODO ( )
//TODO (If-None-Match、If-Modified-Since ) 304( )
//TODO
interceptors.add(new CacheInterceptor(client.internalCache()));
//TODO
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
//TODO okhttpClient networkInterceptors
//TODO 。
interceptors.addAll(client.networkInterceptors());
}
//TODO ( 、 ) 、
//TODO http
interceptors.add(new CallServerInterceptor(forWebSocket));
//TODO
Interceptor.Chain chain = new RealInterceptorChain(interceptors, transmitter, null, 0,
originalRequest, this, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
boolean calledNoMoreExchanges = false;
try {
//TODO
Response response = chain.proceed(originalRequest);
if (transmitter.isCanceled()) {
closeQuietly(response);
throw new IOException("Canceled");
}
return response;
} catch (IOException e) {
calledNoMoreExchanges = true;
throw transmitter.noMoreExchanges(e);
} finally {
if (!calledNoMoreExchanges) {
transmitter.noMoreExchanges(null);
}
}
}
......
}
, Interceptor , okhttp , , Interceptor , , 。
,okhttp , 。
interceptors, RealInterceptorChain Interceptor.Chain, chain.proceed(originalRequest)。
chain.proceed() 。
public final class RealInterceptorChain implements Interceptor.Chain {
....
public Response proceed(Request request, Transmitter transmitter, @Nullable Exchange exchange)
throws IOException {
if (index >= interceptors.size()) throw new AssertionError();
calls++;
// If we already have a stream, confirm that the incoming request will use it.
if (this.exchange != null && !this.exchange.connection().supportsUrl(request.url())) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must retain the same host and port");
}
// If we already have a stream, confirm that this is the only call to chain.proceed().
if (this.exchange != null && calls > 1) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must call proceed() exactly once");
}
//TODO , index+1
// Call the next interceptor in the chain.
RealInterceptorChain next = new RealInterceptorChain(interceptors, transmitter, exchange,
index + 1, request, call, connectTimeout, readTimeout, writeTimeout);
//TODO - okhttpClient, intercept :retryAndFollowUpInterceptor
Interceptor interceptor = interceptors.get(index);
//TODO
Response response = interceptor.intercept(next);
// Confirm that the next interceptor made its required call to chain.proceed().
if (exchange != null && index + 1 < interceptors.size() && next.calls != 1) {
throw new IllegalStateException("network interceptor " + interceptor
+ " must call proceed() exactly once");
}
// Confirm that the intercepted response isn't null.
if (response == null) {
throw new NullPointerException("interceptor " + interceptor + " returned null");
}
if (response.body() == null) {
throw new IllegalStateException(
"interceptor " + interceptor + " returned a response with no body");
}
return response;
}
.......
}
, , RealInterceptorChain index+1, interceptors.get(index); Response。
, chain.proceed() , , chain.proceed() , chain.proceed() , 。
cache ,
public final class CacheInterceptor implements Interceptor {
....
@Override
public Response intercept(Chain chain) throws IOException {
//TODO request Response cacheCandidate == null
Response cacheCandidate = cache != null
? cache.get(chain.request())
: null;
//TODO
long now = System.currentTimeMillis();
CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
//TODO networkRequest == null
Request networkRequest = strategy.networkRequest;
//TODO (CacheStrategy) Response
Response cacheResponse = strategy.cacheResponse;
if (cache != null) {
cache.trackResponse(strategy);
}
//TODO
if (cacheCandidate != null && cacheResponse == null) {
closeQuietly(cacheCandidate.body()); // The cache candidate wasn't applicable. Close it.
}
//TODO networkRequest == null cacheResponse == null
// If we're forbidden from using the network and the cache is insufficient, fail.
if (networkRequest == null && cacheResponse == null) {
return new Response.Builder()
.request(chain.request())
.protocol(Protocol.HTTP_1_1)
.code(504)
.message("Unsatisfiable Request (only-if-cached)")
.body(Util.EMPTY_RESPONSE)
.sentRequestAtMillis(-1L)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
}
//TODO
// If we don't need the network, we're done.
if (networkRequest == null) {
return cacheResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.build();
}
// , 。
// , 。
//TODO
Response networkResponse = null;
try {
networkResponse = chain.proceed(networkRequest);
} finally {
// If we're crashing on I/O or otherwise, don't leak the cache body.
if (networkResponse == null && cacheCandidate != null) {
closeQuietly(cacheCandidate.body());
}
}
//TODO
// If we have a cache response too, then we're doing a conditional get.
if (cacheResponse != null) {
//TODO 304 ,
if (networkResponse.code() == HTTP_NOT_MODIFIED) {
Response response = cacheResponse.newBuilder()
.headers(combine(cacheResponse.headers(), networkResponse.headers()))
.sentRequestAtMillis(networkResponse.sentRequestAtMillis())
.receivedResponseAtMillis(networkResponse.receivedResponseAtMillis())
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();
networkResponse.body().close();
// Update the cache after combining headers but before stripping the
// Content-Encoding header (as performed by initContentStream()).
cache.trackConditionalCacheHit();
cache.update(cacheResponse, response);
return response;
} else {
closeQuietly(cacheResponse.body());
}
}
//TODO Response
Response response = networkResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();
if (cache != null) {
if (HttpHeaders.hasBody(response) && CacheStrategy.isCacheable(response, networkRequest)) {
// Offer this request to the cache.
CacheRequest cacheRequest = cache.put(response);
return cacheWritingResponse(cacheRequest, response);
}
if (HttpMethod.invalidatesCache(networkRequest.method())) {
try {
cache.remove(networkRequest);
} catch (IOException ignored) {
// The cache cannot be written.
}
}
}
return response;
}
....
}
, 。
(1) ,cacheCandidate = cache.Response Response, cacheCandidate = null; CacheStrategy cacheResponse networkRequest。( )
(2) cacheCandidate != null cacheResponse == null cacheCandidate 。( , , )
(3) networkRequest == null ,cacheResponse == null , , , 。
(4) networkRequest == null ,cacheResponse != null , , , 。
( ) :
(1) ,
(2) , , , 。
, , , , , 。
okhttp 。
:
https://www.jianshu.com/p/0b7cd90bec2f