从Room观察LiveData会导致PagedListAdapter刷新,ViewModel也会在方向更改时刷新数据

时间:2018-07-21 10:16:23

标签: android-viewmodel android-livedata android-paging mutablelivedata

我正在构建一个使用ArticleBoundaryCallback发起对API的调用的应用,并将响应存储在Room中。我还使用LiveData收听该表,并在PagedListAdapter中显示项目。

问题在于,每次将新数据插入到Room(文章)表中时,整个列表都会刷新。

此外,在更改配置时,似乎又要重新获取整个数据(ViewModel不保留它,RecyclerView被重新创建)。

每次插入时,RecyclerView都会跳转(如果插入的是新数据,则跳转几行;如果将新数据替换为旧数据,则跳转到开头)。

整个代码在此GitHub repo中。

我的课程是:

文章:

@Entity(tableName = "article",
    indices={@Index(value="id")})public class Article {
@PrimaryKey(autoGenerate = false)
@SerializedName("_id")
@Expose
@NonNull
private String id;
@SerializedName("web_url")
@Expose
private String webUrl;
@SerializedName("snippet")
@Expose
private String snippet;
@SerializedName("print_page")
@Expose
private String printPage;
@SerializedName("source")
@Expose
private String source;
@SerializedName("multimedia")
@Expose
@Ignore
private List<Multimedium> multimedia = null;

文章DAO:

@Dao
public interface ArticleDao {

@Insert(onConflict = OnConflictStrategy.REPLACE)
long insert(Article article);

@Insert(onConflict = OnConflictStrategy.REPLACE)
void update(Article... repos);

@Insert(onConflict = OnConflictStrategy.REPLACE)
void insertArticles(List<Article> articles);


@Delete
void delete(Article... articles);

@Query("DELETE FROM article")
void deleteAll();

@Query("SELECT * FROM article")
List<Article> getArticles();

@Query("SELECT * FROM article")
DataSource.Factory<Integer, Article> getAllArticles();}

LocalCache(从会议室存储/检索)

public class LocalCache {
private static final String TAG = LocalCache.class.getSimpleName();
private ArticleDao articleDao;
private Executor ioExecutor;

public LocalCache(AppDatabase appDatabase, Executor ioExecutor) {
    this.articleDao = appDatabase.getArticleDao();
    this.ioExecutor = ioExecutor;
}

public void insertAllArticles(List<Article> articleArrayList){
    ioExecutor.execute(new Runnable() {
        @Override
        public void run() {
            Log.d(TAG, "inserting " + articleArrayList.size() + " repos");
            articleDao.insertArticles(articleArrayList);
        }
    });
}

public void otherFunction(ArrayList<Article> articleArrayList){
    // TODO
}

public DataSource.Factory<Integer, Article> getAllArticles() {
    return articleDao.getAllArticles();
}

AppRepository

public class AppRepository {

private static final String TAG = AppRepository.class.getSimpleName();
private static final int DATABASE_PAGE_SIZE = 20;
private Service service;
private LocalCache localCache;
private LiveData<PagedList<Article>> mPagedListLiveData;

public AppRepository(Service service, LocalCache localCache) {
    this.service = service;
    this.localCache = localCache;
}

/**
 * Search - match the query.
 */
public ApiSearchResultObject search(String q){
    Log.d(TAG, "New query: " + q);

    // Get data source factory from the local cache
    DataSource.Factory dataSourceFactory = localCache.getAllArticles();

    // every new query creates a new BoundaryCallback
    // The BoundaryCallback will observe when the user reaches to the edges of
    // the list and update the database with extra data
    ArticleBoundaryCallback boundaryCallback = new ArticleBoundaryCallback(q, service, localCache);

    // Get the paged list
    LiveData data = new LivePagedListBuilder(dataSourceFactory, DATABASE_PAGE_SIZE)
            .setBoundaryCallback(boundaryCallback)
            .build();

    mPagedListLiveData = data;

    ApiSearchResultObject apiSearchResultObject = new ApiSearchResultObject();
    apiSearchResultObject.setArticles(data);

    return apiSearchResultObject;
}

public DataSource.Factory getAllArticles() {
    return localCache.getAllArticles();
}

public void insertAllArticles(ArrayList<Article> articleList) {
    localCache.insertAllArticles(articleList);
}
}

ViewModel

public class DBArticleListViewModel extends ViewModel {

private AppRepository repository;

// init a mutable live data to listen for queries
private MutableLiveData<String> queryLiveData = new MutableLiveData();

// make the search after each new search item is posted with (searchRepo) using "map"
private LiveData<ApiSearchResultObject> repositoryResult =  Transformations.map(queryLiveData, queryString -> {
    return repository.search(queryString);
});

// constructor, init repo
public DBArticleListViewModel(@NonNull AppRepository repository) {
    this.repository = repository;
}

// get my Articles!!
public LiveData<PagedList<Article>> articlesLiveData = Transformations.switchMap(repositoryResult, object ->
        object.getArticles());

// get teh Network errors!
public LiveData<String> errorsLiveData = Transformations.switchMap(repositoryResult, object ->
        object.getNetworkErrors());


// Search REPO
public final void searchRepo(@NonNull String queryString) {
    this.queryLiveData.postValue(queryString);
}

// LAST Query string used
public final String lastQueryValue() {
    return (String)this.queryLiveData.getValue();
}

活动-从VM观察

 DummyPagedListAdapter articleListAdapter = new DummyPagedListAdapter(this);

    localDBViewModel = ViewModelProviders.of(this, Injection.provideViewModelFactory(this)).get(DBArticleListViewModel.class);

    localDBViewModel.articlesLiveData.observe(this, pagedListLiveData ->{
        Log.d(TAG, "articlesLiveData.observe size: " + pagedListLiveData.size());
        if(pagedListLiveData != null)
            articleListAdapter.submitList(pagedListLiveData);
    });

    recyclerView.setAdapter(articleListAdapter);

适配器

public class DummyPagedListAdapter extends PagedListAdapter<Article, ArticleViewHolder> {

private final ArticleListActivity mParentActivity;

public DummyPagedListAdapter(ArticleListActivity parentActivity) {
    super(Article.DIFF_CALLBACK);
    mParentActivity = parentActivity;
}

@NonNull
@Override
public ArticleViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    View itemView = LayoutInflater.from(mParentActivity).inflate(R.layout.article_list_content, parent, false);
    return new ArticleViewHolder(itemView);
}

@Override
public void onBindViewHolder(@NonNull ArticleViewHolder holder, int position) {
    Article article = getItem(position);

    if (article != null) {
        holder.bindTo(article);
    } else {
        holder.clear();
    }
}
}

DIFF

   public static DiffUtil.ItemCallback<Article> DIFF_CALLBACK = new 
DiffUtil.ItemCallback<Article>() {
    @Override
    public boolean areItemsTheSame(@NonNull Article oldItem, @NonNull 
Article newItem) {
        return oldItem.getId() == newItem.getId();
    }

    @Override
    public boolean areContentsTheSame(@NonNull Article oldItem, @NonNull 
Article newItem) {
        return oldItem.getWebUrl() == newItem.getWebUrl();
    }
};

我真的需要解决这个问题。谢谢!

1 个答案:

答案 0 :(得分:3)

是的..我花了一段时间,但我解决了。如我所想,这是一个愚蠢的问题:在适配器用来确定要从观察到的数据集中添加和忽略什么的DIFF_CALLBACK中,我使用的是作为字符串的比较器oldItem.getId()== newItem.getId()! !!当然,适配器总是在获取“新值”并添加它们。.

更正了DiffUtil.ItemCallback

 public static DiffUtil.ItemCallback<Article> DIFF_CALLBACK = new DiffUtil.ItemCallback<Article>() {
    @Override
    public boolean areItemsTheSame(@NonNull Article oldItem, @NonNull Article newItem) {
        return oldItem.getStoreOrder() == newItem.getStoreOrder();
    }

    @Override
    public boolean areContentsTheSame(@NonNull Article oldItem, @NonNull Article newItem) {
        return oldItem.getId().equals(newItem.getId()) && oldItem.getWebUrl().equals(newItem.getWebUrl());
    }
};

我希望这可以提醒您始终注意最基本的事情。我为此浪费了很多时间。希望你不会:)