IT 이모저모

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

exien 2018. 3. 15. 09:30

사용자 목록 검색하고 표시하기


사용자 목록을 갱신하는 API 호출 처리의 구현은 RecyclerView 어댑터 및 RecyclerViewHolder를 정의하고, API 호출이 완료 될 때 어댑터에 데이터를 설정하는 식으로 진행됩니다.


API 호출


전항에서 준비한 randomuser.me의 API 접근에서 사용자 목록을 가져옵니다. API 호출의 응답은 io.reactivex.Observable이므로 Reactive Extensions의 작법에 따라 응답의 핸들링을 구현할 수 있습니다.

 주목할만한 점은 통신 완료시 알림 개체 (mDataLoadedNotification)에 대한 통지 만하고 View 갱신 처리 등을 기술하지 않기 때문에 간결하게 쓸 수 있다는 점입니다. 여기서 말하는 통지 객체는 RxJava가 제공하는 Reactive Extensions에서 Subject이지만, 기본적으로는 Observer 패턴의 Subject라는 인식에 문제가 없습니다.

public class UserListFragment extends Fragment { ...     
    
    
    // 페이지 번호 private int mCurrentPageNumber ; // 통신의 완료 통지 객체 private Subject < RandomUserResponse > mDataLoadedNotification = PublishSubject . create ();
     
    
      
    
    ...
    
    / **
     * 사용자 목록을로드합니다.
     * / private void loadUserList () { 
        mCurrentPageNumber = mCurrentPageNumber + 1 ;
       

        RandomUserApi . get (). list ( mCurrentPageNumber ) // 통신이 실패했을 경우는 하늘의 응답 객체를 이용 . onErrorReturnItem ( new RandomUserResponse ()) // 통신 처리는 백그라운드 스레드에서 수행 . subscribeOn ( Schedulers . from ( ChatApplication . getInstance (). getThreadPoolExecutor ())) // 응답 처리를 MAIN 스레드에서 수행 . observeOn ( AndroidSchedulers . mainThread ()) // 통신이 완료되면 알림 . subscribe (
                
                 
                
                
                
                
                
                mDataLoadedNotification :: onNext ); }
    
    
    ... }

 그리고 통신 완료시 처리는 통신 완료 통지 객체의 청취자로서 구현합니다.

public class UserListFragment extends Fragment { ...     
    
    
    @Override public View onCreateView ( LayoutInflater inflater , ViewGroup container , Bundle savedInstanceState ) { ...
        
        
        
        bindEvents (); }
    
    
    ...
    
    / **
     * 이벤트 핸들러를 등록합니다.
     * / private void bindEvents () { // 통신 완료 청취자를 등록 
        mDataLoadedNotification . subscribe ( this :: onDataLoaded );
      
        
        
        ... }
    
    
    ...
    
    / **
     * 데이터로드 완료 이벤트를 처리합니다.
     *
     * @param data {@link RandomUserResponse}
     * / private void onDataLoaded ( final RandomUserResponse data ) { // RecyclerView 어댑터에 데이터를 추가 
        mUserListAdapter . addItem ( data . results ); } }
       
        
    

RecyclerView 어댑터의 정의

 이 항목에 대해서는 인터넷에 각종 기사가 있기 때문에 소스 코드를 참조하기로하고, 여기에 언급되지 않은 것이 많다 사항만을 열거하고 자세한 내용은 생략합니다.

  • 내부 클래스로서 정의 할 때 static 클래스로 (암시 적 참조가 가져다 메모리 누수를 일으키지 않기 때문에)
  • Activity와 Fragment의 참조를 가지는 경우에는 약 참조하는 (라이프 사이클의 여러 인스턴스 사이의 강한 참조 유지 의한 메모리 누수를 일으키지 않기 때문에)
  • LayoutInflater를 초기화 할 때 ContextThemeWrapper에서 테마를 적용 할

RecyclerViewHolder의 정의

 ViewHolder의 기본적인 구현 방법은 다른 기사에게 양보로서 여기에서는 RxJava + RxBinding 라이브러리와 EventBus 라이브러리를 사용하는 방법과 장점을 소개합니다.

 RecyclerView의 각 행을 클릭했을 때 뭔가 처리 할 때 자주 시달린다는 더블 클릭의 문제입니다. 이를 RxJava + RxBinding 라이브러리를 이용하면 쉽게 해결할 수 있습니다. 예를 들어 아래 코드처럼하면 1 번 이벤트가 발생하면 500 밀리 초 동안 이벤트가 발생하지 않도록 제어 할 수 있습니다.

public static class UserViewHolder extends RecyclerView . ViewHolder { ...      
    
    
    / **
     * 생성자
     * / private UserViewHolder ( final View itemView ) { ...
       
        

        bindUiEvents (); }
    
    
    / **
     * UI 이벤트 처리기를 등록합니다.
     * / private void bindUiEvents () { // 클릭 이벤트를 observable로서 취급 RxView . clicks ( itemView ) // 1 번 이벤트가 발생하면 500 밀리 초는 다음 이벤트를 무시 . debounce ( 500 , TimeUnit . MILLISECONDS ) / / 이벤트 핸들링은 MAIL 스레드에서 수행 . observeOn ( AndroidSchedulers . mainThread ()) // 핸들러를 설정 . subscribe ( this : onClick ); }
      
        
        
                
                 
                
                
                
                
    
    
    ... }

 RxBinding를 사용하기위한 build.gradle 설정은 다음과 같습니다에서 ProGuard 설정은 필요하지 않습니다.

dependencies { ... 의미 ...
    
    
    compile 'com.jakewharton.rxbinding2 : rxbinding : 2.0.0'
    
    ... 생략 ... }

 RecyclerView 행 클릭 이벤트를 처리하는 방법은 인터넷 상에서도 여러 가지 방법이 제시되고 있지만, 필자가 추천하는 것은 EventBus를 이용한 방법입니다. EventBus를 사용하여 이벤트의 발생원 (이번 경우 ViewHolder)과 이벤트 핸들링 구현 (UserListFragment)을 완전히 분리 할 수 ​​있습니다.

ViewHolder에서 이벤트를 통지
public static class UserViewHolder extends RecyclerView . ViewHolder { ...      
    
    
    / **
     * 행이 클릭되었을 때에 불려갑니다.
     * / private void onClick ( final Object ignore ) { // EventBus에 통지 EventBus . getDefault () post ( new Event . SelectUser ( this )); } }
       
        
         
    
Fragment에서 이벤트를 핸들링
public class UserListFragment extends Fragment { ...     
    
    
    / **
     * {@link Event.SelectUser} 이벤트를 처리합니다.
     *
     * @param e {@link Event.SelectUser}
     * / @Subscribe public void on ( final Event . SelectUser e ) { final ChatDetailFragment fragment
             = ChatDetailFragment . newInstance ( e . viewHolder . mUser );
    
       
          
        
        getFragmentManager () . beginTransaction () . addToBackStack ( ChatDetailFragment . TAG ) . replace ( R . id . container , fragment ) . commit (); } }
                
                
                
                
    

 EventBus를 사용하기위한 build.gradle 설정 및 ProGuard 설정은 다음과 같습니다.

dependencies { ... 의미 ...
    
    
    compile 'org.greenrobot : eventbus : 3.0.0'
    
    ... 생략 ... }
- keepattributes * Annotation * - keepclassmembers class ** { @org . greenrobot . eventbus . Subscribe <methods> ; } - keep enum org . greenrobot . eventbus . ThreadMode { *; }