ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Android Architecture Components #6 - Paging
    Android/AAC 2019. 8. 6. 17:58

    RecyclerView에서 페이징 처리를 조금더 쉽고 효율적으로 작성하게 하기 위한 라이브러리이다.

    많은 양의 데이터를 한꺼번에 로딩하지 않고 점진적인 로딩방법을 이용함으로 네트웤 사용량 및 시스템 리소스를 적게 사용이 가능하다.


    Gradle Dependency

    implementation 'androidx.paging:paging-runtime:2.1.0'
    // Rx 호환 선택
    implementation "androidx.paging:paging-rxjava2:2.1.0"

    1. DataSource 생성하기

     

    DataSource는 실제 데이터를 획득하는 역할을 한다. 데이터는 Network에서 받아온 데이터 또는 DB에서 받아오는 데이터 일 수도 있다.

     

    DataSource는 추상 클래스로 이를 구현한 ItemKeyedDataSource, PageKeyDataSource, PositionalDataSource중 하나를 이용하고 어느것을 이용할지는 획득한 Json 데이터의 형태에 따라 달라진다..

     

    ItemKeyedDataSource<Key,Value> : 아이템에 다음 데이터에 대한 정보가 있을때 사용

    PageKeyDataSource<Key,Value> : 페이지 정보에 다음페이지에 대한 정보가 있을때 사용

    PositionalDataSource<Key,Value> : 특정 위치의 데이터를 획득할때 ( 50번째 위치에서 10개 )

     

    이용할 DataSource 클래스를 선택하였다면 아래의 메서드를 구현해준다.

    loadInitial() : 최초에 한번 호출하고, 초기 데이터 획듹이 목적이다. 매개변수로 전달되는 LoadInitalParams에 담긴 Config 정보 ( 초기 데이터 갯수 )를 활용해서 초기 데이터 획득 작업진행. 획득한 데이터를 두번째 매개변수인 LoadInitialCallback에 담아주면된다.

    loadBefore() : 이전 페이지 데이터 획득

    loadAfter() : 다음 페이지 데이터 획득

     

    class MyDataSource : PageKeyedDataSource<Long, String>() {
        override fun loadInitial(
            params: LoadInitialParams<Long>,
            callback: LoadInitialCallback<Long, String>
        ) {
    
        }
    
        override fun loadAfter(
            params: LoadParams<Long>,
            callback: LoadCallback<Long, String>
        ) {
    
        }
    
        override fun loadBefore(
            params: LoadParams<Long>,
            callback: LoadCallback<Long, String>
        ) {
            
        }
    }

    PageKeyedDataSource vs ItemKeyedDataSource

     

    PageKeyedDataSource

    서버에서 페이지번호에 대한 정보를 유지하는 경우에 사용한다. callback에 데이터를 담을때 다음 페이지 번호를 지정하면 다음 페이지 획득을 위해 loadAfter() 함수 호출시 매개변수로 전달한다.

    class MyDataSource : PageKeyedDataSource<Long, String>() {
        override fun loadAfter(
            params: LoadParams<Long>,
            callback: LoadCallback<Long, String>
        ) {
    
        }
    }

    ItemKeyedDataSource

    별도로 getKey라는 함수를 추가로 오버라이드 받아 작성해야 한다.

    각 항목의 정보로 다음페이지를 요청하는 것임으로 다름 페이지 요청시 이 함수가 자동으로 호출되어 이곳에서 리턴 시키는 값을 loadAfter() 함수 호출시 첫번째 매개변수로 지정해준다.

     

    만약 10개의 항목이 출력되어 있고 마지막 항목의 id값이 10이라면 다음페이지를 획득하기 위해서 getKey() 함수가 호출이 되고 이 함수에서 10이 리턴될 것이다. 그러면 이 10이라는 정보가 loadAfter()에 자동 전달되어 이 정보를 이용하여 다음 페이지를 요청하는 구조이다.

     

    서버가 이에 맞게 대응되어 있어야 한다. 즉 서버에서 항목의 id값을 보고 그 다음항목 10개를 구성하는 알고리즘이 있어야 한다.

    class MyDataSource2 : ItemKeyedDataSource<Long, String>() {
        override fun loadInitial(
            params: LoadInitialParams<Long>,
            callback: LoadInitialCallback<String>
        ) {
        }
    
        override fun loadAfter(
            params: LoadParams<Long>,
            callback: LoadCallback<String>
        ) {
        }
    
        override fun loadBefore(
            params: LoadParams<Long>,
            callback: LoadCallback<String>
        ) {
        }
    
        override fun getKey(
            item: String
        ): Long {
        }
    }

    2. DataSource.Factory 생성하기

     

    DataSource.Factory는 DataSource 객체를 만들어주는 역할이다.

    create() 함수를 오버라이드 받아 작성하고, 이 함수에서 준비된 DataSource 객체를 생성해서 리턴시키면 된다.

    LivePagedListBuilder에 의해 등록해주면 create 함수가 자동으로 호출 된다.

    class MyDataFactory : DataSource.Factory<Long, String>() {
        override fun create(): DataSource<Long, String> {
            return MyDataSource()
        }
    }

    3. ViewModel??

    AAC 컨셉이 MVVM 구현을 지원해주기 위해 나온 것이니 ViewModel을 구현해주도록 합시다.

     

    ViewModel은 Activity의 추상화로 Activity에 있어야할 데이터를 가지고 있어야 한다

    그래서 ViewModel은 DataSource를 이용하는 역할로 사용하여 DataSource.Factory와 PagedList.Config 객체에 LivePagedListBuilder에 전달만 하면 된다.

    이렇게 되면 LivePagedListBuilder에 의해 Factory에서 생성한 DataSource를 Config 정보대로 이용해 데이터를 획득하고 결과 값으로 페이지 데이터가 담기는 PagedList 객체가 리턴되게 된다.

    이 PagedList 객체를 ViewModel의 결과 값으로 Activity에 전달하면 된다.

    class MyViewModel : ViewModel() {
    
        private var itemLiveData: LiveData<PagedList<ItemModel>>? = null
        private var pagedListConfig: PagedList.Config = PagedList.Config.Builder()
            .setInitialLoadSizeHint(3)
            .setPageSize(3)
            .build()
    
        val news: LiveData<PagedList<ItemModel>>
            get() {
    			... 데이터 반환
            }
    
    }
    

    PagedList.Config : PagedList가 DataSource에서 가져와야 할 데이터의 크기, 미리 불러와야 하는 데이터 사이즈 등을 정의 및 설정한다

    LivePagedListBuilder : DataSource.Factory와 PagedList.Config을 인자로 받아 LiveData<PageList>를 생성한다.

    RxPagedListBuilder : RxJava2를 지원하기 위해 추가되었다.

     


    4. 데이터 출력을 위한 PageListAdapter

    class MyListAdapter(context: Context) :
    		PagedListAdapter<ItemModel, MyListAdapter.ItemViewHolder>(DIFF_CALLBACK) {
    
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder {
            val layoutInflater = LayoutInflater.from(parent.context)
            val binding = ItemMainBinding.inflate(layoutInflater, parent, false)
            return ItemViewHolder(binding)
        }
    
        override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
            val article = getItem(position)
            holder.binding.setData(article)
        }
    
        inner class ItemViewHolder(var binding: ItemMainBinding) : RecyclerView.ViewHolder(binding.getRoot())
    
        companion object {
            var DIFF_CALLBACK: DiffUtil.ItemCallback<ItemModel> = object : DiffUtil.ItemCallback<ItemModel>() {
                override fun areItemsTheSame(oldItem: ItemModel, newItem: ItemModel): Boolean {
                    return oldItem.id == newItem.id
                }
    
                override fun areContentsTheSame(oldItem: ItemModel, newItem: ItemModel): Boolean {
                    return oldItem == newItem
                }
            }
        }
        
    }
    

    PagedListAdapter를 상속받아서 구현을 해주면된다.

    댓글

Designed by Tistory.