12/08/2018, 16:19

Android Architecture Components: Paging Library

Nhiều ứng dụng của chúng ta cần load rất nhiều data information từ Database. Queries đến Database có thể mất nhiều thời gian và sử dụng rất nhiều bộ nhớ. Do đó Google đã release thư viện mới là Paging Library có thể giúp bạn sử lý vấn đề này. Main component of Paging Library là PagedListAdapter ...

Nhiều ứng dụng của chúng ta cần load rất nhiều data information từ Database. Queries đến Database có thể mất nhiều thời gian và sử dụng rất nhiều bộ nhớ. Do đó Google đã release thư viện mới là Paging Library có thể giúp bạn sử lý vấn đề này.

Main component of Paging Library là PagedListAdapter kế thừa từ RecyclerViewAdapter, PagedList, và DataSource.

DataSourde

DataSource là interface cho pager source để cung cấp các dữ liệu. Khi sử dụng bạn phải implement 1 trong 2 lại datasourde: DataSource và TiledDataSource nó sẽ được sử dụng khi bạn load N data từ N-1 data.

  • Sử dụng KeyedDataSource nếu bạn cấn sử dụng N data để lấy N+1 data. Ví dụ: khi bạn muốn fetch comment of a post status of Facebook bạn cần biết được id của comment N để lấy được comment N+1
  • Sử dụng TiledDataSource nếu bạn muốn fetch từ bất kỳ vị trí nào bạn muốn trong your data store. Class này sẽ support request tập các dữ liệu bất đầu từ vị trí bạn muốn. Ví dụ 20 item từ vị trí 1200
  • loadCount(): bạn cũng có thể implement một phương thức khác, phương thức loadCount() cho bạn biết có một số lượng hữu hạn hay vô hạn item bạn cần hiển thị trong your list.

Nếu bạn sử dụng Room Persistent Library để quản lý database, nó sẽ tự động tạo DataSource class. Ví dụ: queries dưới đây trả về TiledDataSource:

@Query("select * from users WHERE age > :age order by name DESC, id ASC")
TiledDataSource<User> usersOlderThan(int age);

PagedList

The PagedList là thành phần tự động load data và provide tín hiệu để update data trên RecyclerViewAdapter. Data sẽ được tự động load trên background thread và được sử dụng trên main thread. Nó hỗ trợ cả: list scroll vô hạn và list có giới hạn. Bạn cũng có thể thiết lập một vài cấu hình: Size of first page and số item khi load

PagedListAdapter

Class implement từ RecyclerView.Adapter đại diện cho PagedList:family_mmgg:. Ví dụ khi 1 page mới được load thì PagedListAdapter sẽ báo hiệu cho RecyclerView data đã đến, RecyclerView sẽ update lại data. PagedListAdapter sử dụng background để tính toán sự thay đổi từ PagedList, sau đó RecyclerView sẽ thực hiện những thay đổi cần thiết.

Paging Library Example

  1. Add Component Architecture to your project Open build.gradle trong project và add lines bên dưới
allprojects {
    repositories {
        jcenter()
        maven { url 'https://maven.google.com' }
    }
}

Add Architecture Component

Mở build.gradle trong app/ add lines bên dưới

dependencies {
    ....
    implementation 'com.android.support:appcompat-v7:26.1.0'
   
    //For Lifecycles, LiveData, and ViewModel
    implementation 'android.arch.lifecycle:runtime:1.0.0'
    implementation 'android.arch.lifecycle:extensions:1.0.0-alpha9-1'
    annotationProcessor "android.arch.lifecycle:compiler:1.0.0-alpha9-1"
    
    //For Room
    implementation 'android.arch.persistence.room:runtime:1.0.0-alpha9-1'
    annotationProcessor "android.arch.persistence.room:compiler:1.0.0-alpha9-1"
    
    //For Paging
    implementation 'android.arch.paging:runtime:1.0.0-alpha1'
 }
  1. Create DataSource

Create Entity

@Entity
public class User {
    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "user_id")
    public long userId;
    @ColumnInfo(name = "first_name")
    public String firstName;
    public String address;
 
    .....
}

Data Access Objects (DAO)

Để đơn giản trong connection giữa DataSource và RecyclerView, chúng ta sử dụng LivePagedListProvider.

@Dao
public interface UserDao {
 
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    public void insertAll(List<User> users);
 
    @Query("SELECT * FROM User")
    public abstract LivePagedListProvider<Integer,User> usersByFirstName();
 
}

Create DataBase

@Database(entities = {User.class}, version = 1)
abstract public class AppDatabase extends RoomDatabase {
    public static final String DATABASE_NAME = "UserDb";
 
    public abstract UserDao userDao();
}
  1. Create ViewModel ViewModel sẽ extends từ ViewModel của Architecture Component và sử dụng chúng để tham chiếu tới LiveData của PagedList. Chúng ta sẽ lấy tham chiếu đó từ DAO bằng cách gọi method getUsers(). Config cấu hình mà bạn muốn. Ví dụ: Size của page là 50, và size của mối lần load data là 50
public class UserViewModel extends ViewModel {
 
    public LiveData<PagedList<User>> userList;
 
    public UserViewModel() {
 
    }
 
    public void init(UserDao userDao) {
        userList = userDao.usersByFirstName().create(0,
                new PagedList.Config.Builder()
                        .setEnablePlaceholders(true)
                        .setPageSize(50)
                        .setPrefetchDistance(50)
                        .build());
    }
}

Trong onCreate của Activity chúng ta sẽ tham chiếu tới ViewModel và RecyclerView

RecyclerView recyclerView = findViewById(R.id.userList);
LinearLayoutManager llm = new LinearLayoutManager(this);
llm.setOrientation(LinearLayoutManager.VERTICAL);
recyclerView.setLayoutManager(llm);
 
 UserViewModel viewModel = ViewModelProviders.of(this).get(UserViewModel.class);
 viewModel.init(userDao);
 final UserAdapter userUserAdapter = new UserAdapter();
 
 viewModel.userList.observe(this, pagedList -> {
        userUserAdapter.setList(pagedList);
 });
 
 recyclerView.setAdapter(userUserAdapter);
  1. Create Adapter Để nói cho PagedListAdapter biết là làm thế nào để biết sự khác nhau giữa 2 phần từ, implement class DiffCallback
@Entity
public class User {
    public static DiffCallback<User> DIFF_CALLBACK = new DiffCallback<User>() {
        @Override
        public boolean areItemsTheSame(@NonNull User oldItem, @NonNull User newItem) {
            return oldItem.userId == newItem.userId;
        }
 
        @Override
        public boolean areContentsTheSame(@NonNull User oldItem, @NonNull User newItem) {
            return oldItem.equals(newItem);
        }
    };
    
    .....
 
    @Override
    public boolean equals(Object obj) {
        if (obj == this)
            return true;
 
        User user = (User) obj;
 
        return user.userId == this.userId && user.firstName == this.firstName;
    }
}

Create Adapter

public class UserAdapter extends PagedListAdapter<User, UserAdapter.UserItemViewHolder> {
 
    protected UserAdapter() {
        super(User.DIFF_CALLBACK);
    }
 
    @Override
    public UserItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
        View view = layoutInflater.inflate(R.layout.item_user_list, parent, false);
        return new UserItemViewHolder(view);
    }
 
    @Override
    public void onBindViewHolder(UserItemViewHolder holder, int position) {
        User user= getItem(position);
        if(user!=null) {
            holder.bindTo(user);
        }
    }
 
}

Xem code tại đây 5. Kết thúc Android có nhiều khái niệm và thành phần mới với thành phần kiến trúc. Nhưng điều đó có nghĩa là bạn có thể tách riêng chúng. Vì vậy nếu bạn muốn, bạn sẽ chỉ có thể sử dụng vòng đời, LiveData và PagedList hoặc chỉ ViewModel hoặc chỉ Room Persistent. Nhưng bạn cũng có thể sử dụng chúng với nhau. Vậy hãy bắt đầu sử dụng Architecture Components để tạo ra một kiến trúc có thể phù hợp cho ứng dụng của bạn.

0