Библиотека подкачки с пользовательским классом модели, который отличается от класса модели SQLite - Android - PullRequest
0 голосов
/ 24 июня 2019

Я следую за Обзором библиотеки подкачки от разработчиков Android, который использует DataSource.Factory для получения данных из базы данных следующим образом:

@Dao
interface ConcertDao {
    // The Int type parameter tells Room to use a PositionalDataSource
    // object, with position-based loading under the hood.
    @Query("SELECT * FROM Concerts ORDER BY date DESC")
    fun concertsByDate(): DataSource.Factory<Int, Concert>
}

Однако в моем случае у меня нет таблицы Concert, а есть таблица ConcertAA, в которой значения хранятся по-другому.

Например, мой Concert класс:

data class Concert(val date: Long, val bands: List<Band>)

Принимая во внимание, что мой ConcertAA активный класс Android:

@Table(name = "Concerts")
class ConcertAA(): Model(){

    @Column(name = "Bands")
    var bands: String? = null

    @Column(name = "Date", index = true)
    var date: Long? = null

}

Где я сохраняю группы как Json String.

Следовательно, у меня вопрос: как мне получить ConcertDao, где в момент запроса к моей базе данных я преобразую каждый объект ConcertAA в объект Concert, который будет использоваться в списке? Поскольку запрос SELECT * FROM Concerts ORDER BY date DESC вернет список ConcertAA, а не список Concert.

1 Ответ

0 голосов
/ 27 июня 2019

Даже если следующий ответит на мой собственный вопрос, для логики сети базы данных, где данные могут изменяться, этого решения недостаточно, поскольку оно не обрабатывает обновления данных, уже сохраненных в базе данных.

Проститеколичество абстрактных классов - я обычно работаю над расширением и возможностью повторного использования.Кроме того, простите за смешение кода Java и кода Kotlin ... по какой-то причине некоторые библиотеки не были опознаны в моем проекте, использующем Kotlin, но не стесняйтесь использовать Kotlin в этих случаях.новый DataSource класс

abstract class MyDataSource<Value>: PositionalDataSource<Value>() {

    override fun loadInitial(params: PositionalDataSource.LoadInitialParams, callback: PositionalDataSource.LoadInitialCallback<Value>) {
        callback.onResult(getDataFromDatabase(params.requestedLoadSize, params.requestedStartPosition)?: listOf(), 0)
    }

    override fun loadRange(params: PositionalDataSource.LoadRangeParams, callback: PositionalDataSource.LoadRangeCallback<Value>) {
        callback.onResult(getDataFromDatabase(params.loadSize, params.startPosition)?: listOf())
    }

    abstract fun getDataFromDatabase(limit: Int, offset: Int): List<Value>?
}

Реализованный Concert класс данных:

class ConcertDataSource : MyDataSource<Concert>() {

    override fun getDataFromDatabase(limit: Int, offset: Int): List<Concert>? {
        return ConcertAA.getConcerts(limit, offset)
    }
}

Затем нам нужен DataSourceFactory, который будет создавать наш экземпляр DataSource:

abstract class MyDataSourceFactory<Key, Value> : DataSource.Factory<Key, Value>() {

    val mutableLiveData: MutableLiveData<DataSource<Key, Value>>? = null

    override fun create(): DataSource<Key, Value> {
        val dataSource = createDataSource()
        mutableLiveData?.postValue(dataSource)
        return dataSource
    }

    abstract fun createDataSource(): DataSource<Key, Value>
}

И реализованный DataSourceFactory для ConcertDataSource:

class ConcertDataSourceFactory : MyDataSourceFactory<Int, Concert>() {

    override fun createDataSource(): DataSource<Int, Concert> {
        return ConcertDataSource()
    }
}

Затем ViewModel для PagedList, который будет иметь наш источник данных:

public class ConcertViewModel extends ViewModel {
    public final LiveData<PagedList<Concert>> concertList;
    private FetchCallback callback;
    private final int pageSize = 10

    PagedList.BoundaryCallback<Concert> boundaryCallback = new PagedList.BoundaryCallback<Concert>(){
        boolean frontLoaded =false;

        public void onZeroItemsLoaded() {
//            callback.fetch(...);
        }

        public void onItemAtFrontLoaded(@NonNull Concert itemAtFront) {
            if(!frontLoaded) {
//                callback.fetch(...);
            }
            frontLoaded = true;
        }

        public void onItemAtEndLoaded(@NonNull Concert itemAtEnd) {
//            callback.fetch(...);
        }
    };

    public ConcertViewModel(FetchCallback callback) {
        this.callback = callback;
        ConcertDataSourceFactory dataSourceFactory = new ConcertDataSourceFactory();
        PagedList.Config config = new PagedList.Config.Builder()
                .setPageSize(pageSize)
                .setInitialLoadSizeHint(pageSize)
                .setEnablePlaceholders(false)
                .build();
        concertList = new LivePagedListBuilder<>(dataSourceFactory, config).setBoundaryCallback(boundaryCallback).build();
    }

    public void refresh() {
        conertList.getValue().getDataSource().invalidate();
    }
}

Мы используемBoundaryCallback для прослушивания всякий раз, когда PagedList требуется загружать больше, когда он достигает какой-либо из сторон списка, и данные нашей базы данных не содержат никаких дополнительных данных.Это позволит получать больше данных с сервера (или любого поставщика данных).Выборка вызывается с помощью настраиваемого обратного вызова:

interface FetchCallback{
    fun fetch(limit: Int?, concertIdOffset: String?)
}

Теперь, поскольку нам нужно передать обратный вызов нашему ViewModel, нам нужно сообщить ViewModelProviders, как создать новый экземпляр ViewModel с параметрами.Это делается с помощью ViewModelProvider.Factory:

abstract class MyViewModelFactory(
        private val callback: FetchCallback) : ViewModelProvider.Factory {

    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        return createViewModel(callback)
    }

    abstract fun <T : ViewModel> createViewModel(callback: FetchCallback): T
}

И нашей реализованной ViewModelFactory для концерта:

class ConcertViewModelFactory(callback: FetchCallback) : MyViewModelFactory(callback) {

    override fun <T : ViewModel> createViewModel(callback: FetchCallback): T {
        return ConcertViewModel(callback) as T
    }
}

Наконец, мы инициализируем ViewModel с нашей точки зрения (Activity, Fragment и т. Д.):

val factory = ConcertViewModelFactory(
            object: FetchCallback{
                override fun fetch(limit: Int?, eventIdOffset: String?, level: RequestParameters.RequestLevel, showLoader: Boolean) {
                    //Do the call to the server
                }
            })

    viewModel = ViewModelProviders.of(this, factory).get(ConcertViewModel::class.java)
    adapter = ConcertAdapter()
    viewModel.concertList.observe(viewLifecycleOwner, Observer(adapter::submitList))

Это будет ответом на мой вопрос, однако обратите внимание, что (по крайней мере, на данный момент) библиотека подкачки для Android не обрабатывает изменяемые данные.Например, если у концерта есть список групп, которые могут измениться (например, одна из групп не может попасть на концерт), логика не может обновить данные, сохраненные в базе данных, поскольку она запрашивает дополнительные данные только на сервере, когдадостигает любой из границ (сверху или снизу) данных, хранящихся в базе данных.Следовательно, для моих собственных целей эта библиотека бесполезна.

...