【问题标题】:Paging Library invalidating data source not working分页库无效数据源不起作用
【发布时间】:2017-10-18 15:27:03
【问题描述】:

最近我在尝试这个:

我有一个由数据源支持的作业列表(我正在使用分页库),并且作业列表中的每个项目都有一个保存按钮,并且该保存按钮将作业的状态从未保存更新为已保存(反之亦然) 在数据库中,一旦更新它会使 DataSource 失效,现在失效应该导致当前页面立即重新加载,但这不会发生。

我检查了数据库中的值,它们实际上得到了更新,但 UI 并非如此。

代码:

public class JobsPagedListProvider {

private JobListDataSource<JobListItemEntity> mJobListDataSource;

public JobsPagedListProvider(JobsRepository jobsRepository) {
    mJobListDataSource = new JobListDataSource<>(jobsRepository);
}

public LivePagedListProvider<Integer, JobListItemEntity> jobList() {
    return new LivePagedListProvider<Integer, JobListItemEntity>() {
        @Override
        protected DataSource<Integer, JobListItemEntity> createDataSource() {
            return mJobListDataSource;
        }
    };
}

public void setQueryFilter(String query) {
    mJobListDataSource.setQuery(query);
}
}

这是我的自定义数据源:

public class JobListDataSource<T> extends TiledDataSource<T> {

private final JobsRepository mJobsRepository;
private final InvalidationTracker.Observer mObserver;


String query = "";

@Inject
public JobListDataSource(JobsRepository jobsRepository) {
    mJobsRepository = jobsRepository;

    mJobsRepository.setJobListDataSource(this);

    mObserver = new InvalidationTracker.Observer(JobListItemEntity.TABLE_NAME) {
        @Override
        public void onInvalidated(@NonNull Set<String> tables) {
            invalidate();
        }
    };

    jobsRepository.addInvalidationTracker(mObserver);
}

@Override
public boolean isInvalid() {
    mJobsRepository.refreshVersionSync();
    return super.isInvalid();
}

@Override
public int countItems() {
    return DataSource.COUNT_UNDEFINED;
}


@Override
public List<T> loadRange(int startPosition, int count) {
    return (List<T>) mJobsRepository.getJobs(query, startPosition, count);
}


public void setQuery(String query) {
    this.query = query;
}
}

这是 JobsRepository 中的代码,用于将作业从未保存更新为已保存:

public void saveJob(JobListItemEntity entity) {
    Completable.fromCallable(() -> {
        JobListItemEntity newJob = new JobListItemEntity(entity);
        newJob.isSaved = true;
        mJobDao.insert(newJob);
        Timber.d("updating entity from " + entity.isSaved + " to "
                + newJob.isSaved); //this gets printed in log
        //insertion in db is happening as expected but UI is not receiving new list
        mJobListDataSource.invalidate();
        return null;
    }).subscribeOn(Schedulers.newThread()).subscribe();
}

这里是工作列表的 Diffing 逻辑:

private static final DiffCallback<JobListItemEntity> DIFF_CALLBACK =  new DiffCallback<JobListItemEntity>() {
    @Override
    public boolean areItemsTheSame(@NonNull JobListItemEntity oldItem, @NonNull JobListItemEntity newItem) {
        return oldItem.jobID == newItem.jobID;
    }

    @Override
    public boolean areContentsTheSame(@NonNull JobListItemEntity oldItem, @NonNull JobListItemEntity newItem) {
        Timber.d(oldItem.isSaved + " comp with" + newItem.isSaved);
        return oldItem.jobID == newItem.jobID
                && oldItem.jobTitle.compareTo(newItem.jobTitle) == 0
                && oldItem.isSaved == newItem.isSaved;
    }
};

JobRepository 中的JobListDataSource(以下仅提及相关部分):

public class JobsRepository {
//holds an instance of datasource
private JobListDataSource mJobListDataSource;

//setter
public void setJobListDataSource(JobListDataSource jobListDataSource) {
    mJobListDataSource = jobListDataSource;
}

}

JobsRepository 中的getJobs():

public List<JobListItemEntity> getJobs(String query, int startPosition, int count) {
    if (!isJobListInit) {

        Observable<JobList> jobListObservable = mApiService.getOpenJobList(
                mRequestJobList.setPageNo(startPosition/count + 1)
                        .setMaxResults(count)
                        .setSearchKeyword(query));

        List<JobListItemEntity> jobs = mJobDao.getJobsLimitOffset(count, startPosition);

        //make a synchronous network call since we have no data in db to return
        if(jobs.size() == 0) {
            JobList jobList = jobListObservable.blockingSingle();
            updateJobList(jobList, startPosition);
        } else {
            //make an async call and return cached version meanwhile
            jobListObservable.subscribe(new Observer<JobList>() {
                @Override
                public void onSubscribe(Disposable d) {

                }

                @Override
                public void onNext(JobList jobList) {
                    updateJobList(jobList, startPosition);
                }

                @Override
                public void onError(Throwable e) {
                    Timber.e(e);
                }

                @Override
                public void onComplete() {

                }
            });
        }
    }

    return mJobDao.getJobsLimitOffset(count, startPosition);
}

更新jobsRepository中的JobList:

private void updateJobList(JobList jobList, int startPosition) {
    JobListItemEntity[] jobs = jobList.getJobsData();
    mJobDao.insert(jobs);
    mJobListDataSource.invalidate();
}

【问题讨论】:

  • 显示一些你在做什么的代码。
  • 你在使用LivePagedListProvider吗?它没有问题
  • 我有自己的自定义数据源,是的,它是 LivePagedListProvider,我会发布一些代码,请稍等
  • 不清楚JobsPagedListProvider 中的mJobListDataSource 是如何连接到您的JobsRepository 中的。它们是同一个实例吗?
  • 你为什么不简单地使用这样的东西:@Query("SELECT * FROM user") LivePagedListProvider&lt;Integer, User&gt; loadAllUsers();

标签: android android-architecture-components


【解决方案1】:

看了DataSource的源码后,我意识到:

  1. 一旦失效的数据源将永远不会再次生效。
  2. invalidate() 表示:如果已经调用了 invalidate,则此方法不执行任何操作。

我实际上有一个由JobsPagedListProvider 提供的自定义数据源(JobListDataSource),所以当我在saveJob()(在JobsRepository 中定义)中使我的DataSource 无效时,它试图获取新的DataSource 实例(通过再次调用 loadRange() 来获取最新数据 - 这就是刷新 DataSource 的工作方式) 但由于我的DataSource 是单例并且已经无效,所以没有进行loadRange() 查询!

因此,请确保您没有单独的 DataSource 并手动(通过调用 invalidate())或在 DataSource 的构造函数中使用 InvalidationTracker 使您的 DataSource 无效。

所以最终的解决方案是这样的:

在 JobsPagedListProvider 中没有单例:

public class JobsPagedListProvider {

private JobListDataSource<JobListItemEntity> mJobListDataSource;

private final JobsRepository mJobsRepository;

public JobsPagedListProvider(JobsRepository jobsRepository) {
    mJobsRepository = jobsRepository;
}

public LivePagedListProvider<Integer, JobListItemEntity> jobList() {
    return new LivePagedListProvider<Integer, JobListItemEntity>() {
        @Override
        protected DataSource<Integer, JobListItemEntity> createDataSource() {
            //always return a new instance, because if DataSource gets invalidated a new instance will be required(that's how refreshing a DataSource works)          
            mJobListDataSource = new JobListDataSource<>(mJobsRepository);
            return mJobListDataSource;
        }
    };
}

public void setQueryFilter(String query) {
    mJobListDataSource.setQuery(query);
}
}

还要确保如果您从网络获取数据,您需要有正确的逻辑来检查数据是否过时,然后再查询网络,否则每次数据源失效时都会重新查询。 我通过在JobEntity 中有一个insertedAt 字段来解决它,该字段跟踪该项目何时插入数据库并检查它是否在getJobs()JobsRepository 中过时。

下面是 getJobs() 的代码:

public List<JobListItemEntity> getJobs(String query, int startPosition, int count) {
    Observable<JobList> jobListObservable = mApiService.getOpenJobList(
            mRequestJobList.setPageNo(startPosition / count + 1)
                    .setMaxResults(count)
                    .setSearchKeyword(query));

    List<JobListItemEntity> jobs = mJobDao.getJobsLimitOffset(count, startPosition);

    //no data in db, make a synchronous call to network to get the data
    if (jobs.size() == 0) {
        JobList jobList = jobListObservable.blockingSingle();
        updateJobList(jobList, startPosition, false);
    } else if (shouldRefetchJobList(jobs)) {
        //data available in db, so show a cached version and make async network call to update data
        jobListObservable.subscribe(new Observer<JobList>() {
            @Override
            public void onSubscribe(Disposable d) {

            }

            @Override
            public void onNext(JobList jobList) {
                updateJobList(jobList, startPosition, true);
            }

            @Override
            public void onError(Throwable e) {
                Timber.e(e);
            }

            @Override
            public void onComplete() {

            }
        });
    }

    return mJobDao.getJobsLimitOffset(count, startPosition);
}

最后删除 JobListDatasource 中的 InvalidationTracker,因为我们正在手动处理失效:

public class JobListDataSource<T> extends TiledDataSource<T> {

private final JobsRepository mJobsRepository;

String query = "";

public JobListDataSource(JobsRepository jobsRepository) {
    mJobsRepository = jobsRepository;
    mJobsRepository.setJobListDataSource(this);
}

@Override
public int countItems() {
    return DataSource.COUNT_UNDEFINED;
}

@Override
public List<T> loadRange(int startPosition, int count) {
    return (List<T>) mJobsRepository.getJobs(query, startPosition, count);
}


public void setQuery(String query) {
    this.query = query;
}
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-03-17
    • 2020-11-21
    • 2023-03-30
    • 2018-03-28
    • 2016-01-11
    • 2018-01-23
    相关资源
    最近更新 更多