[Android] Retrofit2, OkHttpClient Method 정리

(이 Post는 Retrofit 2.2.0 기준으로 작성되었음)
Retrofit을 적용하면서, 지원되는 기능들을 한번 정리를 해 둬야, 어떤 것을 쓸수 있는지 확실히 할 수 있을 것 같아서, 아래와 같이 정리한다.

1. Retrofit.Builder

   baseUrl(String baseUrl) 을 제외하면, 대부분 DI의 형태를 띄고 있다. 각각 Injection 되는 Class들의 용도는 각 항목에서 설명한다. 분석을 하고 보니, Retrofit Class는 이 Builder를 통해 Injection 된 Instance들을 들고있는 Holder 역할을 하는 것으로 보인다.

  • baseUrl(String baseUrl): Base가 되는 URL을 설정한다. 많은 서비스들에서 사용하는 API들은 앞부분의 Host 정도는 동일하고, 뒷부분의 API Name들로 구분을 하는 경우가 많다. 그때 고정이 되는 앞부분을 여기서는 BaseURL이라고 이야기하고 있다.
    만약 BaseUrl을 "https://apis.github.com"이라고 설정한 다음
    실제 Retrofit Service Interface에서 @GET("/user/repos")라고 선언해서 사용한다면 실제 요청은 "https://apis.github.com/user/repos"로 나가게 된다. 중요한 부분이 있어서 Retrofit의 Document에서 일부를 가져온다.

    올바른 사용:
    Base URL: http://example.com/api/
    Endpoint: foo/bar/
    Result: http://example.com/api/foo/bar/

    잘못된 사용:
    Base URL: http://example.com/api
    Endpoint: foo/bar/
    Result: http://example.com/foo/bar/

    만약 Base URL을 설정하지 않으면 IllegalStateException이 발생하게 된다.

  • baseUrl(HttpUrl httpUrl)
    : HttpUrl Class는 Builder 패턴으로 되어있다. HttpUrl.Builder를 들여다보면 꽤 많은 것들을 설정할 수 있는데, 위에서 봤던 BaseUrl뿐 아니라, username, password, scheme, port 등 꽤나 디테일한 설정을 할 수 있다.

  • client(OkHttpClient client): 실제 Connection에 가장 큰 영향을 주는 녀석이다. OkHttpClient는 거의 가장 중요하다고 해도 무방한 녀석이라 아래쪽에서 자세하게 살펴보겠지만 이 녀석의 역할을 간단하게 살펴보면 아래와 같다.   - read/write/connect Timeout 설정
      - ping 인터벌 설정
      - proxy와 proxy Selector설정
      - 기본 / SSL용 Socket Factory 설정
      - Authenticator 설정
      - Dispatcher 설정
      - Interceptor 설정
      - 네트워크 Interceptor 설정
      - 캐시설정
      - Cookie Jar 설정
    나열된 항목들을 보면 알 수 있겠지만, 대부분 Connection에 밀접한 영향을 주는 항목들임을 알 수 있다. 중요한 Class인 만큼 아래서 자세하게 살펴본다.
  • callFactory(okhttp3.Call.Factory factory)  : okhttp3.Call.Factory는 아래와 같이 하나의 method를 갖는 Interface이다.
    이 Call.Factory를 implements한 대표적인 사례는 바로 위에서 봤던 OkHttpClient Class이다. 이 Factory의 용도는 Request를 받아서 Call이라는 Instance를 return하는 것이다. (Call의 Factory니 당연한...)
    그렇다면 Call interface를 살펴보면, 아래와 같다.

    현재 상태를 확인하는 is~~ Method들과 clone()을 제외한 메인 기능은 4가지
    request(), execute(), enqueue(), cancel()이다.

    그러면 이 메인 Method들의 실제 구현부분을 살펴봐야하는데, 앞서 봤던 Call.Factory를 구현한 OkHttpClient의 newCall()을 따라가보면 아래와 같은 모양이다.

  •  이 안에서는 단순하게, Parameter로 넘겨받은 request를 RealCall 이라는 Class로 넘겨서 Instance를 생성하고 있다. 그럼 RealCall의 Constructor를 가보면 아래와 같다.
    RealCall의 Constructor와 앞에서 본 OkHttpClient의 newCall을 함께 보면, RealCall을 생성하기 위해, OkHttpClient의 Instance를 넘겨주고, Request Instance를 로컬에 들고 있으며, webSocket은 false로 설정한다. 그리고 맨 마지막에 Retry를 위한 Interceptor를 생성하는 것으로 보인다.(이것은 추후 확인해보도록 한다.)
    자 여기서 RealCall은 Call의 메인 4개 Method를 구현했는데, 하나씩 살펴보자
    - request()   request() Method의 역할은 Constructor로 넘겨받은 Request Instance를 되돌려주는 것... 실제로 Retrofit 내부에서는 어떻게 쓰이는지는 아래서 확인해본다.
    - execute()
     실제 실행하는 부분인데, 이 부분은 Sync하게 처리한다. 아래에서 볼 enqueue()와의 차이는 Sync하게 처리하는가? Async하게 처리한 후 callback으로 그 결과를 받는가의 차이이다. (많은 library에서 위와 같은 방식으로 동기/비동기 기능을 제공한다.)


    - enqueue()
      앞서 말한바와 같이, enqueue()는 작업을 queue에 넣겠다 라는 말이다. 위의 코드에서 볼 수 있듯, client의 dispatcher에 있는 queue에 넣는다.
    다만 여기서 AsyncCall이라는 Class는 Call Interface를 상속받은 것이 아니라, Runnable을 상속받은 것이다. 그리고 AsyncCall은 RealCall Class의 inner class로써 RealCall의 멤버변수들에 접근할 수 있다.
    AsyncCall내부의 핵심 Method인 execute()는 다른 Thread에서 처리한 후 Callback으로 넘겨줄 뿐, RealCall의 execute()와 다를것이 없다.
     
    - cancel()

     말 그대로 Cancel하는 부분. 다만 저 retryAndFollowUpInterceptor에 cancel하는 것을 통해 cancel동작이 수행되는데, 저 내부 코드는 시간이 될 때 분석하도록 하겠다.
     
      
  • addConverterFactory(Converter.Factory factory)
    : 이 부분은 Response로 내려온 것을 Convert해주는 Converter의 Factory를 넣어주는 곳이다. 다만 set이 아니라 add라는 부분에 집중해야 한다. 결국 여러차례 Convert가 가능하다는 이야기인데, 어떤경우 쓸 수 있는지는 잘 확인이 되지 않는다. 다만 Realm에 아래와 같은 글이 있어서 참고할 필요가 있다.
    (출처 : https://realm.io/kr/news/droidcon-jake-wharton-simple-http-retrofit-2/)


        
  • addCallAdapterFactory(CallAdapter.Factory factory)
     :

      
  • callbackExecutor(Executor executor): 여기서의 Executor는 java.util.concurrent.Executor이다. 기본적으로 Executor는 Runnable을 받아서 실행을 시킨다 라는 목적을 구현하도록 정의된 Interface이다. Retrofit에서는 Callback을 되돌려줄때, Callback이 항상 MainThread임을 보장해주기 위해 Default로 MainThreadExecutor를 사용한다.

    만약 별도로 Executor를 정의해주지 않는다면 Callback은 항상 MainThread라고 생각해도 된다. 하지만, 굳이 Callback을 MainThread에서 처리할 필요가 없다면, 아래와 같이 Executor를 작성해서 사용할 수도 있다.

    또는 Callback을 되돌려주기 전에 로그를 쌓는다거나, 기타 추가작업등을 하고싶을 경우 활용할 수 있다.
     
  • validateEagerly(boolean validateEagerly)
    : Request를 하기 전 Retrofit Instance를 통해 Request를 위한 Service Instance를 생성하기 위해, Retrofit.create()를 호출할 때, 여러 파라미터들에 대한 검증을 좀 더 타이트하게 할 것인지를 설정하는 곳이다. 기본적으로는 false이다. true로 설정하게 되면, 검증은 확실하게 되지만, 그만큼 생성하는 속도가 느려지게 되는 점이 있다.
     
       
  • build()
    : 위에 설정된 정보를 바탕으로 드디어 Retrofit Instance를 생성하게 된다. 이 시점에서 설정이 안된 정보들에 대해서 Default Instance들을 생성하게 된다. 
   

2. OkHttpClient.Builder

  • public Builder connectTimeout(long timeout, TimeUnit unit) 
  • public Builder readTimeout(long timeout, TimeUnit unit) 
  • public Builder writeTimeout(long timeout, TimeUnit unit)
    : 위의 3개 Method는 각종 Timeout에 관한 설정이다. 여기서 TimeUnit은 java.util.concurrent.TimeUtil 이다.
       
  • public Builder pingInterval(long interval, TimeUnit unit)
    : 웹소켓을 사용할 때 사용되는 Ping Interval에 대한 값.
      
  • public Builder proxy(Proxy proxy) 
    : Proxy 정보를 직접 설정할 수 있다.
    int proxyPort = 8080;
    String proxyHost = "proxyHost";
    final String username = "username";
    final String password = "password";
    
    Authenticator proxyAuthenticator = new Authenticator() {
      @Override public Request authenticate(Route route, Response response) throws IOException {
           String credential = Credentials.basic(username, password);
           return response.request().newBuilder()
               .header("Proxy-Authorization", credential)
               .build();
      }
    };
    
    OkHttpClient client = new OkHttpClient.Builder()
        .connectTimeout(60, TimeUnit.SECONDS)
        .writeTimeout(60, TimeUnit.SECONDS)
        .readTimeout(60, TimeUnit.SECONDS)
        .proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, proxyPort)))
        .proxyAuthenticator(proxyAuthenticator)
        .build();
    (출처 : http://stackoverflow.com/questions/35554380/okhttpclient-proxy-authentication-how-to)
    위와 같은 방식으로 프록시를 code 레벨에서 설정할 수 있다.
    실제로 아래와 같은 코드로 테스트를 성공했다.


      
  • public Builder proxySelector(ProxySelector proxySelector)
     
    : ProxySelector는 abstract class이다. 그리고 구현해야 하는 method는 아래 두개이다.

    - public abstract List<Proxy> select(URI uri);
    - public abstract void connectFailed(URI uri, SocketAddress sa, IOException ioe);

    selete()는 넘어온 URI에 맞는 Proxy 리스트를 리턴하도록 구현해야하고,
    connectFailed()는 connect이 Fail 되었을 때의 처리를 구현하도록 되어있다.

      
  • public Builder proxyAuthenticator(Authenticator proxyAuthenticator): 프록시에 연결할 때 필요한 인증을 수행하는 Authenticator를 설정한다.
    이와 관련된 예제는 위의 proxy()의 예제 안에 포함되어있다.
     
      
  • public Builder cache(Cache cache): 아래 이미지를 보면, 내부적으로 가지고 있는 캐시는 두가지 이다. 두 캐시는 둘중 하나만 쓸 수 있는 구조로 되어있다. 다만, setInternalCache는 내부적인 OkHttpClient Instance를 생성할 때만 사용되고, 외부로 공개되는 것은 cache() method뿐이다.

    여기서 넘겨주는 Cache class는 아래와 같이 2가지 Constructor를 가지고 있다.

    이 Cache class는 final로 선언되어 있어서, 재정의 하는 것이 불가능 하다. 즉, Retrofit에서 사용하는 cache는 Disk cache뿐이라고 봐도 될듯 하다.
    특별한 파일 시스템을 쓰는 경우에는 FileSystem interface를 구현하면 되는데, Android에서는 그럴일은 거의 없을 듯 하다.
  •  

  • public Builder socketFactory(SocketFactory socketFactory)
  • public Builder sslSocketFactory(SSLSocketFactory sslSocketFactory)
  • public Builder sslSocketFactory(SSLSocketFactory sslSocketFactory, X509TrustManager trustManager) : 일반용/SSL용 소켓을 생성하는 Factory를 넘겨준다.
  • public Builder hostnameVerifier(HostnameVerifier hostnameVerifier)
     
    : 호스트네임의 적합성 여부를 체크하는 Verifier를 넣어준다. 별일 없으면 OkHostnameVerifier.INSTANCE를 그냥 쓰자.
  • public Builder followSslRedirects(boolean followProtocolRedirects)
  • public Builder followRedirects(boolean followRedirects)
     
    : Redirect를 따라갈 것인가에 대한 true/false를 설정한다.

      
  • public Builder retryOnConnectionFailure(boolean retryOnConnectionFailure)
     
    : 연결 실패시 재 시도 할 것인가?

      
  • public Builder dispatcher(Dispatcher dispatcher): Dispatcher를 받을 수 있게 되어있지만, 사실 Dispatcher Class는 final class라서 사실상 의미가 없는 설정이다. 


  • public Builder addInterceptor(Interceptor interceptor) 
  • public Builder addNetworkInterceptor(Interceptor interceptor): 위의 두 Method를 보면 OkHttp에는 두종류의 Interceptor가 있다는 것을 알 수 있다.
     여기에서 이해를 돕기위해 addInterceptor()에 추가되는 Interceptor를 Application Interceptor라고 하자.

    그렇게 했을때 위와같은 그림을 그릴 수 있다. (참고 : https://github.com/square/okhttp/wiki/Interceptors)
    각 Interceptor는 Request때와 Response때 두번씩 intercept 할 수 있는 것이다.
    이와 관련된 좀 더 자세한 예제는 아래 링크에서 볼 수 있다.
    http://developer88.tistory.com/67

       
  • 그 외에도 아래와 같은 Method들을 통해 다양한 설정을 해볼 수 있다. 
        - public Builder certificatePinner(CertificatePinner certificatePinner)        - public Builder authenticator(Authenticator authenticator)
        - public Builder connectionPool(ConnectionPool connectionPool)
        - public Builder protocols(List<Protocol> protocols)
        - public Builder connectionSpecs(List<ConnectionSpec> connectionSpecs)
        - public Builder dns(Dns dns)         - public Builder cookieJar(CookieJar cookieJar)

댓글

이 블로그의 인기 게시물

[Android] DataBinding의 동작방식 - 4. include Tag 혹은 ViewStub 사용시의 Binding

[Android] Layout별 성능 비교[Measure 호출횟수 비교] (LinearLayout vs RelativeLayout vs ConstraintLayout)