IT 이모저모

Android 8.0과 Reactive Extensions을 이용한 응용프로그램 개발 - 4

exien 2018. 3. 14. 17:48

통신 처리 구현


통신은 아래 그림과 같이 okhttp, Retrofit, google-gson, Rx Java를 연계해서 활용합니다.


build.gradle은 아래와 같이 설정합니다. adapter-rxjava2는 Retrofit2 응답을 RxJava2와 연동하기 위한 라이브러리입니다. logging-interoeptor는 okhttp통신 로그를 출력하기위한 라이브러리입니다.


dependencies { 
    
    
    compile 'com.squareup.retrofit2 : retrofit : 2.3.0' 
    compile 'com.squareup.retrofit2 : converter-gson : 2.3.0' 
    compile 'com.squareup.retrofit2 : adapter-rxjava2 : 2.3.0'
    
    compile 'com.squareup.okhttp3 : logging-interceptor : 3.9.0'
    
    ... 생략 ... }


ProGuard 설정은 다음과 같이 합니다.


- dontwarn javax . annotation . **

# okhttp - dontwarn okhttp3 ** - dontwarn okio . **



# Retrofit - dontnote retrofit2 . Platform - dontwarn retrofit2 . Platform $ Java8 - keepattributes Signature - keepattributes Exceptions


API 인터페이스 정의


이번에는 사용자 목록 검색 API로 randomuser.me를 사용 합니다. randomuser.me 는 임의로 더미 사용자 정보를 생성하는 서비스입니다.


API 인터페이스는 Retrofit 라이브러리가 제공하는 각종 주석을 정의합니다. @GET 어노테이션 요청 경로를 지정하고 @Query 에 Request 매개 변수를 지정합니다. 그리고 Response는 io.reactivex.Observable을 지정하여 RxJava를 이용한 처리를 할 수 있도록 합니다.


/ **
 * Api Interface
 * / public interface Api { / **
   
    
     * 사용자 목록을 가져옵니다.
     *
     * @param page 페이지 번호
     * @return {@link RandomUserResponse}
     * / @GET ( "api? results = 20 & seed = hoge & nat = us" ) Observable < RandomUserResponse > list ( @Query ( "page" ) final Integer page ); }


위의 예는 https://randomuser.me/api 결과를 20건으로 하고 seed를 hoge 로 하고 nat(국가)를 us 로 하고 page는 가변 인자로 넘겨 받는 GET 요청을 하는 정의 입니다.


HTTP 클라이언트 빌더 준비


HTTP 클라이언트는 HTTP2.0을 지원하는 okhttp 라이브러리를 사용합니다. 응용 프로그램 내에서 HTTP 통신의 기본 설정을 통일하기 위해 Application 클래스에 okhtp 클라이언트 빌더 생성 프로세스를 구현하는게 좋습니다.


public class ChatApplication extends Application { ...     
    
    
    / **
     * {@link OkHttpClient.Builder}를 생성합니다.
     *
     * @return {@link OkHttpClient.Builder}
     * / public static OkHttpClient . Builder httpClientBuilder () { // HTTP 통신 로그를 Timber 통해 출력 final HttpLoggingInterceptor logging = new HttpLoggingInterceptor ( Timber :: d ); 
        logging . setLevel ( HttpLoggingInterceptor . Level . BASIC );
       
        
           
        
        return new OkHttpClient . Builder () . connectTimeout ( 10 , TimeUnit . SECONDS ) . readTimeout ( 20 , TimeUnit . SECONDS ) . writeTimeout ( 10 , TimeUnit . SECONDS ) . addInterceptor ( logging ); }  
    
    ... }


Retrofit 빌더 준비


API 액세스를 위한 Retrofit 빌더 생성 처리도 마찬가지로 Application 클래스에서 구현해 줍니다.


public class ChatApplication extends Application { // AsyncTask의 구현을 참고로 스레드 풀 크기를 정의 private static final int CPU_COUNT
             = Runtime . getRuntime (). availableProcessors (); private static final int CORE_POOL_SIZE
             = Math . max ( 2 , Math . min ( CPU_COUNT - 1 , 4 ));     
    
        
           
    
    private Executor mThreadPoolExecutor ; 
    
    / **
     * Get thread pool executor.
     *
     * @return {@link Executor}
     * / public Executor getThreadPoolExecutor () { if ( mThreadPoolExecutor == null ) { 
            mThreadPoolExecutor = Executors . newFixedThreadPool ( CORE_POOL_SIZE ); }
      
            
        
        
        return mThreadPoolExecutor ; }
    
    
    ...
    
    / **
     * {@link Retrofit.Builder}를 생성합니다.
     *
     * @return {@link Retrofit.Builder}
     * / public static Retrofit . Builder retrofitBuilder () { final Gson gson = new GsonBuilder () . setDateFormat ( "yyyy-MM-dd HH : mm : ss" ). create ();
       
           
                
        
        return new Retrofit . Builder () . callbackExecutor ( getInstance (). getThreadPoolExecutor ()) // 응답을 google-gson 퍼스 . addConverterFactory ( GsonConverterFactory . create ( gson )) // Retrofit 및 RxJava을 연계 . addCallAdapterFactory ( RxJava2CallAdapterFactory . create ()); }  
                
                
                
                
                
    
    
    ... }


API 를 사용하는 부분 구현


위에서 정의한 API 인터페이스인 HTTP 클라이언트 빌더와 Retrofit 빌더를 접근해서 사용하기 위한 클래스를 구현합니다. 이 부분을 싱글톤으로 구현 하여 API end point 마다 HTTP 클라이언트 인스턴스를 돌아 가면서 사용하도록 합니다. okhttp3은 인스턴스 커넥션 풀을 가지고 있기 때문에 인스턴스를 많이 생성하면 메모리를 사용하는 부분에서 좋지 않습니다.


/ **
 * randomuser.me API 접근 클래스
 * / public class RandomUserApi { private static final String ORIGIN = "https://randomuser.me" ;
   
        
    
    private static RandomUserApi instance ;  
    
    private Api mInterface ; 
    
    / **
     * randomuser.me Api 인터페이스를 가져옵니다.
     *
     * @return {@link Api}
     * / public static Api get () { synchronized ( RandomUserApi . class ) { if ( instance == null ) { 
                instance = new RandomUserApi (); } } return instance . mInterface ; }
        
          
                 
            
        
        
    
    
    / **
     * 생성자
     * / private RandomUserApi () { 
        mInterface = ChatApplication . retrofitBuilder () . client ( ChatApplication . httpClientBuilder () build ()) . baseUrl ( ORIGIN ) . build () . create ( Api . class ); }
       
                
                
                
                
    
    
    ... }