Live Paged Lists, Architecture Components, and Room… or Realm?

About the paging library

Room’s answer to the Pagination problem

@Database(entities = {Task.class}, version = 1)
@TypeConverters({RoomTypeConverters.class})
public abstract class DatabaseManager
extends RoomDatabase {
public abstract TaskDao taskDao();
}
@Entity(tableName = Task.TABLE_NAME)
public class Task {
// DIFF CALLBACK, HASHCODE, EQUALS...
public static final String TABLE_NAME = "TASK";
public static final String COLUMN_ID = "task_id";
public static final String COLUMN_TEXT = "task_text";
public static final String COLUMN_DATE = "task_date";

@PrimaryKey
@ColumnInfo(name = COLUMN_ID)
private int id;

@ColumnInfo(name = COLUMN_TEXT)
private String text;

@ColumnInfo(name = COLUMN_DATE)
private Date date;
...
@Dao
public interface TaskDao {
@Query("SELECT * FROM " + Task.TABLE_NAME + " ORDER BY "
+ Task.COLUMN_DATE + " ASC ")
LivePagedListProvider<Integer, Task> tasksSortedByDate();
// Replaced by DataSource.Factory<Integer, Task>
...
public class TaskAdapter
extends PagedListAdapter<Task, TaskAdapter.ViewHolder> {
public TaskAdapter() {
super(Task.DIFF_CALLBACK);
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
Task task = getItem(position);
if(task != null) {
holder.bind(task);
}
}
public class TaskViewModel
extends ViewModel {
private final TaskDao taskDao;

private LiveData<PagedList<Task>> liveResults;

public TaskViewModel() {
taskDao = Injector.get().taskDao();
liveResults = taskDao.tasksSortedByDate()
.create(0, new PagedList.Config.Builder()
.setPageSize(20)
.setPrefetchDistance(20)
.setEnablePlaceholders(true)
.build());

}
public LiveData<PagedList<Task>> getTasks() {
return liveResults;
}
}
public class TaskFragment
extends Fragment {
RecyclerView recyclerView;
...
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
...
TaskViewModel viewModel = ViewModelProviders.of(this).get(TaskViewModel.class);
...
final TaskAdapter taskAdapter = new TaskAdapter();
recyclerView.setAdapter(taskAdapter);
viewModel.getTasks().observe(this, pagedList -> {
taskAdapter.setList(pagedList);
});

}
}

Making Realm work with Paging Library

The pagination library is independent from Room, and allows any data source to be exposed as a TiledDataSource.

The end result I had more than half a year ago

public class Task extends RealmObject {
// DIFFUTIL ITEMCALLBACK (DIFFCALLBACK), HASHCODE, EQUALS

@PrimaryKey
private int id;

private String text;

private Date date;
@Singleton
public class TaskDao {
private final RealmPaginationManager realmPaginationManager;

@Inject
TaskDao(RealmPaginationManager paginationManager) {
this.realmPaginationManager = paginationManager;
}

public LivePagedListProvider<Integer, Task> tasksSortedByDate() {
return realmPaginationManager.createLivePagedListProvider(
realm -> realm.where(Task.class)
.findAllSorted(TaskFields.DATE)); // not Async

}
}
@Module
public class DatabaseModule {
@Provides
@Singleton
RealmPaginationManager realmPaginationManager() {
return new RealmPaginationManager();
}
}
public class TaskViewModel
extends ViewModel {
private final TaskDao taskDao;

private LiveData<PagedList<Task>> liveResults;

public TaskViewModel() {
taskDao = Injector.get().taskDao();
liveResults = taskDao.tasksSortedByDate().create(0,
new PagedList.Config.Builder()
.setPageSize(20)
.setPrefetchDistance(20)
.setEnablePlaceholders(true)
.build());
}

public LiveData<PagedList<Task>> getTasks() {
return liveResults;
}
}

So how the hell does it work?

class RealmTiledDataSource<T extends RealmModel> extends TiledDataSource<T> {
private final RealmResults<T> liveResults;
private final RealmChangeListener changeListener =
element -> {
invalidate();
};
... @Override
@WorkerThread
public List<T> loadRange(int startPosition, int count) {
int countItems = countItems();
List<T> list = new ArrayList<>(count);
for(int i = startPosition;
i < startPosition + count && i < countItems; i++) {
list.add(workerRealm.copyFromRealm(liveResults.get(i)));
}
return Collections.unmodifiableList(list);
}
}

Where can I find this?

DataSource.Factory<Integer, RealmDog> realmDataSourceFactory =   
monarchy.createDataSourceFactory(realm ->
realm.where(RealmDog.class));
DataSource.Factory<Integer, Dog> dataSourceFactory =
realmDataSourceFactory.map(
input -> Dog.create(input.getName()));
LiveData<PagedList<Dog>> dogs = monarchy.findAllPagedWithChanges(
realmDataSourceFactory,
new LivePagedListBuilder<>(dataSourceFactory, 20));
dogs.observe(this, dogs -> {...});

Conclusion

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Gabor Varadi

Gabor Varadi

4.1K Followers

Android dev. Zhuinden, or EpicPandaForce @ SO. Extension function fan #Kotlin, dislikes multiple Activities/Fragment backstack.