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

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