Okhttp 3 소스 코드 분석
okhttp 의 특징 및 관련 기능 에 대한 소 개 는 홈 페이지 의 소 개 를 볼 수 있 습 니 다.
기본 사용
okhttp 를 사용 하여 네트워크 요청 을 시작 합 니 다. 다음 절차 만 있 으 면 됩 니 다.
public void request() {
// 1、 OkHttpClient
OkHttpClient okHttpClient = new OkHttpClient();
// 2、 Request
Request request = new Request.Builder()
.url("https://blog.csdn.net/kaifa1321")
.build();
// 3、 Call ,
Call call = okHttpClient.newCall(request);
// 4、
// 4.1、 , Response
Response response = call.execute();
// 4.2、 , Callback Response
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {}
@Override
public void onResponse(Call call, Response response) throws IOException {
//
String string = response.body().string();
}
});
}
소스 코드 분석
앞에서 언급 한 okhttp 의 사용 절 차 를 대상 으로 이 절차 의 소스 코드 를 하나씩 분석 할 것 입 니 다.
OkHttpClient 인 스 턴 스 생 성
OkHttpClient 대상 의 생 성, okhttp 에서 두 가지 방식 을 제공 합 니 다.
// 1、 OkHttpClient
OkHttpClient okHttpClient = new OkHttpClient();
// 2、 Builder OkHttpClient
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.addInterceptor(null)
.cache(null)
.build();
OkHttpClient 실례 화 과정
OkHttpClient 의 구조 방법 을 살 펴 보 겠 습 니 다.
public OkHttpClient() {
this(new OkHttpClient());
}
OkHttpClient(Builder builder) {
// ,
this.dispatcher = builder.dispatcher;
this.interceptors = Util.immutableList(builder.interceptors);
this.cache = builder.cache;
this.internalCache = builder.internalCache;
this.connectTimeout = builder.connectTimeout;
this.readTimeout = builder.readTimeout;
this.writeTimeout = builder.writeTimeout;
}
위의 소스 코드 를 통 해 알 수 있 듯 이 OkHttpClient 의 무 참 구조 방법 은 마지막 으로 Builder 대상 을 만 들 고 OkHttpClient 의 실례 화 를 완성 합 니 다.사실 무 참 구조 방법 은 OkHttpClient 를 실례 화 하 는 것 과 Builder. build () 방법 을 통 해 실례 화 하 는 원리 가 똑 같 습 니 다. 모두 Builder 대상 을 만들어 OkHttpClient 를 초기 화 하 는 것 입 니 다. 유일 하 게 다른 것 은 Builder 대상 파라미터 (시스템 기본 값 과 개발 자 자체 설정) 입 니 다.
Builder. build () 방법 을 살 펴 보 겠 습 니 다.
public OkHttpClient build() {
// this Builder
return new OkHttpClient(this);
}
OkHttpClient 는 작성 자 모드 를 통 해 실례 화 를 완 료 했 습 니 다. 이 생 성 모드 는 okhttp 에서 매우 많이 사 용 됩 니 다. 예 를 들 어 Request, Response 의 생 성 은 모두 이 모드 를 사 용 했 습 니 다.
맞 춤 형 OkHttpClient 인 스 턴 스
okhttp 홈 페이지 에서 개발 자 에 게 okhttp 을 사용 할 때 OkHttpClient 의 인 스 턴 스 는 단일 인 스 턴 스 를 통 해 okhttp 의 연결 풀 과 스 레 드 풀 을 잘 관리 하 는 것 이 좋 습 니 다.그러나 일반적으로 응용 개발 에서 차단 기 를 추가 하거나 캐 시 를 추가 하 는 등 특별한 요청 을 받 을 수 있 습 니 다.단일 인 스 턴 스 의 OkHttpClient 를 직접 수정 하면 다른 요청 에 영향 을 줄 수 있 습 니 다. okhttp 도 이러한 상황 을 고려 하여 OkHttpClient 인 스 턴 스 를 맞 춤 형 으로 만 드 는 방법 new Buidler 를 제공 합 니 다.
OkHttpClient client = new OkHttpClient();
client.newBuilder()
.readTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.build();
여기 뉴 빌 더 방법 을 보 겠 습 니 다.
public Builder newBuilder() {
// this OkHttpClient
return new Builder(this);
}
기 존의 OkHttpClient 인 스 턴 스 를 통 해 새로운 Builder 대상 을 만 듭 니 다. 이 Builder 대상 은 기본적으로 원래 OkHttpClient 인 스 턴 스 의 속성 을 가지 고 있 습 니 다. 그러면 존재 하 는 OkHttpClient 의 관련 설정 을 완전히 재 활용 할 수 있 습 니 다. 특정한 속성 만 수정 하면 됩 니 다.예 를 들 어 readTimeout, writeTimeout 등 이다.
요청 대상 만 들 기
Request 대상 의 주요 역할 은 Http 요청 의 주요 매개 변 수 를 봉인 하 는 것 입 니 다. 이 매개 변 수 는 다음 과 같 습 니 다.
요청 대상 생 성:
Request request = new Request.Builder()
.url("https://api.github.com/repos/square/okhttp/issues")
.header("User-Agent", "OkHttp Headers.java")
.addHeader("Accept", "application/json; q=0.5")
.addHeader("Accept", "application/vnd.github.v3+json")
.post(RequestBody.create(MediaType, File))
.build();
Request 의 생 성 절 차 는 상대 적 으로 간단 합 니 다. 위 에 서 는 기본적으로 한눈 에 알 수 있 습 니 다. 여 기 는 너무 많은 소 개 를 하지 않 습 니 다.
위의 코드 예제 에서 주의해 야 할 것 은 Request 에 Header 요청 헤드 를 추가 하 는 방법 header () 와 addHeader () 입 니 다.
Request 의 원본 코드 에서 도 두 가지 방법 에 대해 구체 적 으로 설명 했다.
RequestBody
Request 를 만 드 는 과정 에서 이 Request 가 POST 요청 이 라면 이 Request 에 요청 체 RequestBody 를 만들어 야 합 니 다. RequestBody 의 생 성 은 정적 방법 create () 를 호출 해 야 합 니 다. RequestBody 에는 여러 개의 create () 방법 이 있 습 니 다. 주로 요청 체 유형 에 따라 요청 체 인 스 턴 스 를 만 듭 니 다.
여기 서 RequestBody 류 의 구체 적 인 내용 을 살 펴 보 겠 습 니 다.
public abstract class RequestBody {
//
public abstract @Nullable MediaType contentType();
//
public long contentLength() throws IOException {
return -1;
}
// sink,sink okio ,
public abstract void writeTo(BufferedSink sink) throws IOException;
/**
* ,
*/
public static RequestBody create(@Nullable MediaType contentType, String content) {
// UTF-8
Charset charset = Util.UTF_8;
if (contentType != null) {
charset = contentType.charset();
if (charset == null) {
charset = Util.UTF_8;
contentType = MediaType.parse(contentType + "; charset=utf-8");
}
}
byte[] bytes = content.getBytes(charset);
return create(contentType, bytes);
}
/**
*
*/
public static RequestBody create(
final @Nullable MediaType contentType, final ByteString content) {
return new RequestBody() {
@Override public @Nullable MediaType contentType() {
return contentType;
}
@Override public long contentLength() throws IOException {
return content.size();
}
@Override public void writeTo(BufferedSink sink) throws IOException {
sink.write(content);
}
};
}
/**
*
*/
public static RequestBody create(final @Nullable MediaType contentType, final byte[] content) {
return create(contentType, content, 0, content.length);
}
/**
*
*/
public static RequestBody create(final @Nullable MediaType contentType, final byte[] content,
final int offset, final int byteCount) {
if (content == null) throw new NullPointerException("content == null");
Util.checkOffsetAndCount(content.length, offset, byteCount);
return new RequestBody() {
@Override public @Nullable MediaType contentType() {
return contentType;
}
@Override public long contentLength() {
return byteCount;
}
@Override public void writeTo(BufferedSink sink) throws IOException {
// sink
sink.write(content, offset, byteCount);
}
};
}
/**
* , file,
*/
public static RequestBody create(final @Nullable MediaType contentType, final File file) {
if (file == null) throw new NullPointerException("content == null");
return new RequestBody() {
@Override public @Nullable MediaType contentType() {
return contentType;
}
@Override public long contentLength() {
return file.length();
}
@Override public void writeTo(BufferedSink sink) throws IOException {
Source source = null;
try {
source = Okio.source(file);
sink.writeAll(source);
} finally {
Util.closeQuietly(source);
}
}
};
}
}
여기 핵심 코드 를 드 리 겠 습 니 다.
public static class Builder {
Headers.Builder headers;
public Builder header(String name, String value) {
headers.set(name, value);
return this;
}
public Builder addHeader(String name, String value) {
headers.add(name, value);
return this;
}
}
public final class Headers {
final List namesAndValues = new ArrayList<>(20);
//
public Builder add(String name, String value) {
checkNameAndValue(name, value);
return addLenient(name, value);
}
//
public Builder set(String name, String value) {
checkNameAndValue(name, value);
// ,
removeAll(name);
addLenient(name, value);
return this;
}
// list
Builder addLenient(String name, String value) {
namesAndValues.add(name);
namesAndValues.add(value.trim());
return this;
}
}
호출 대상 만 들 기
// 3、 Call
Call call = okHttpClient.newCall(request);
okHttpClient. newCall () 방법 에서 RealCall. newRealCall () 방법 으로 Call (RealCall) 인 스 턴 스 를 가 져 옵 니 다.
@Override
public Call newCall(Request request) {
// Call(RealCall)
return RealCall.newRealCall(this, request, false /* for web socket */);
}
네트워크 요청 시작
다음 절 참조: Call 인터페이스 및 구현 클래스 RealCall
Call 인터페이스 및 구현 클래스 RealCall
Call
Call 인터페이스, 주로 네트워크 요청 호출 을 실현 합 니 다.
콜 인터페이스 소스 코드
public interface Call extends Cloneable {
//
Request request();
//
Response execute() throws IOException;
//
void enqueue(Callback responseCallback);
//
void cancel();
//
boolean isExecuted();
//
boolean isCanceled();
Call clone();
interface Factory {
Call newCall(Request request);
}
}
RealCall
RealCall 은 Call 인터페이스의 유일한 실현 클래스 이기 때문에 okhttp 의 네트워크 요청 은 모두 RealCall 클래스 에서 이 루어 집 니 다.그래서 여기 서 리 얼 콜 에 대한 상세 한 분석 을 할 겁 니 다.
RealCall 인 스 턴 스 생 성
위의 글 에서 RealCall 대상 은 okHttpClient. newCall () 방법 에서 RealCall. newRealCall 방법 을 호출 하여 만 들 었 습 니 다.
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;
}
이 방법 에 서 는 주로 구조 적 방법 을 통 해 RealCall 대상 의 생 성 을 완성 하고 이 RealCall 대상 은 OkHttpClient 와 Requst 인 스 턴 스 를 가지 고 있 습 니 다.
RealCall 대상 이 생 성 되면 정식 네트워크 요청 을 시작 할 수 있 습 니 다. 여 기 는 동기 화 요청 과 비동기 요청 으로 나 뉜 다.
// , Response
try {
Response response = call.execute();
} catch (IOException e) {
e.printStackTrace();
}
// , Callback Response
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {}
@Override
public void onResponse(Call call, Response response) throws IOException {
//
String string = response.body().string();
}
});
여기 서 이 두 가지 요구 방식 에 대해 일일이 분석 하 다.
execute 동기 화 요청
RealCall.execute()
public Response execute() throws IOException {
// 1、 Call
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
try {
// 2、 OkHttpClient dispatcher executed
client.dispatcher().executed(this);
// 3、 http Response
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
eventListener.callFailed(this, e);
throw e;
} finally {
// 4、 , Call
client.dispatcher().finished(this);
}
}
execute () 방법 에서 주로 다음 과 같은 기능 을 완성 했다.
핵심 기능: Dispatcher 배포 기.이 는 후속 장 에서 구체 적 인 분석 을 할 것 입 니 다. 동기 화 비동기 요청 이 최종 적 으로 Dispatcher 요청 배포 기 에서 이 루어 졌 다 는 것 만 알 면 됩 니 다.
핵심 기능: Interceptor 차단기.이것 은 후속 장 에서 구체 적 인 분석 을 할 것 입 니 다. 여 기 는 Http 응답 을 얻 는 것 이 Intercepter 차단 기 를 통 해 이 루어 진 것 임 만 알 면 됩 니 다.
enqueue 비동기 요청
RealCall.enqueue(Callback)
@Override
public void enqueue(Callback responseCallback) {
// 1、 Call
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
// AsyncCall ,
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
enqueue () 방법 에서 주로 다음 과 같은 기능 을 완성 하 였 습 니 다.
위의 enqueue 방법 에서 마지막 으로 client. dispatcher (). enqueue (new AsyncCall (responseCallback) 를 실행 할 때 AsyncCall 대상 을 만 들 었 습 니 다.
이 AsyncCall 에 대한 추가 분석 이 필요 합 니 다.
AsyncCall
AsyncCall 은 RealCall 의 내부 클래스 로 AsyncCall 은 NamedRunnable 에서 계승 되 고 NamedRunnable 은 Runnable 인 터 페 이 스 를 실현 하기 때문에 AsyncCall 의 주요 역할 은 Runnable 인 터 페 이 스 를 실현 하 는 것 이다.
NamedRunnable
public abstract class NamedRunnable implements Runnable {
protected final String name;
public NamedRunnable(String format, Object... args) {
this.name = Util.format(format, args);
}
@Override public final void run() {
String oldName = Thread.currentThread().getName();
Thread.currentThread().setName(name);
try {
execute();
} finally {
Thread.currentThread().setName(oldName);
}
}
protected abstract void execute();
}
NamedRunnable 은 추상 적 인 유형 으로 Runnable 인 터 페 이 스 를 실현 했다. 이 유형 에서 추상 적 인 방법 execute () 를 정 의 했 는데 주로 구체 적 인 비동기 작업 을 실현 하 는 것 이다. 여기 서 이런 유형 에 대해 지나치게 설명 하지 않 는 다.
다음은 NamedRunnable 의 하위 클래스 AsyncCall 을 살 펴 보 겠 습 니 다.
AsyncCall
final class AsyncCall extends NamedRunnable {
private final Callback responseCallback;
AsyncCall(Callback responseCallback) {
super("OkHttp %s", redactedUrl());
this.responseCallback = responseCallback;
}
String host() {
return originalRequest.url().host();
}
Request request() {
return originalRequest;
}
RealCall get() {
return RealCall.this;
}
@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);
}
}
}
AsyncCall 의 execute 방법 에서 주요 역할 은 두 가지 입 니 다.
이 를 보면 알 수 있 을 것 입 니 다. 비동기 처리 프로 세 스 는 위의 RealCall. execute () 동기 화 요청 과 똑 같 습 니 다. 유일한 차이 점 은 메 인 스 레 드 에서 이 루어 진 것 입 니 다. 하 나 는 하위 스 레 드 (AsyncCall) 에서 이 루어 진 것 입 니 다.
Dispatcher 배포 기와 Interceptor 차단기 에 대해 서 는 다음 장 에서 구체 적 으로 소개 합 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
다양한 언어의 JSONJSON은 Javascript 표기법을 사용하여 데이터 구조를 레이아웃하는 데이터 형식입니다. 그러나 Javascript가 코드에서 이러한 구조를 나타낼 수 있는 유일한 언어는 아닙니다. 저는 일반적으로 '객체'{}...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.