qingmei2 / MVVM-Architecture

The practice of MVVM + Jetpack architecture in Android.
1.83k stars 282 forks source link

关于实现点赞功能的问题(Paging) #25

Closed windows7lake closed 5 years ago

windows7lake commented 5 years ago

Hello,关于实现点赞功能的细节想要请教一下,根据 Android官方架构组件Paging-Ex:列表状态的响应式管理 一文中关于更新item的思路,我尝试了类似于点赞功能的实现。 通过更新room的数据去通知pagelist的方式可行,但是却遇到了一个问题:当点击点赞按钮后,再重复点击点赞按钮取消点赞,此时按钮状态不会改变。 重复试验发现,点击同一个item时不会更新,但是点击其他item之后再去点击原先的item,此时item的点赞状态会更新。 同时尝试了打印了submitList提交的数据,其中的数据点赞状态是有被更新的,但是因为DiffUtil.ItemCallback中的areContentsTheSame(oldItem: Repo, newItem: Repo)方法返回的oldItem和newItem是相同的,导致点赞状态不会被更新。

不知道您这边是怎么实现这个功能的呢?是否也遇到过类似的问题? 代码如下:

@Dao
interface UserReposDao {
    @Update
    fun insertItem(repo: Repo)
}
class ReposRepository(
        remote: RemoteReposDataSource,
        local: LocalReposDataSource,
        val mAutoDisposeObserver: AsyncProcessor<Unit> = AsyncProcessor.create()
) : BaseRepositoryBoth<RemoteReposDataSource, LocalReposDataSource>(remote, local) {

    .......

    fun setSelectMode(item: Repo, state: Int) {
        item.forksCount = state
        localDataSource.insertNewItemData(item)
                .observeOn(RxSchedulers.ui)
                .doOnError { toast { "${it.message}" } }
                .subscribe()
    }
    .......
}

class RemoteReposDataSource(private val serviceManager: ServiceManager) : IRemoteDataSource {
    .......
}

class LocalReposDataSource(
        private val db: UserDatabase
) : ILocalDataSource {
    fun insertNewItemData(newItem: Repo): Completable {
        return Completable.fromAction {
            db.runInTransaction { db.userReposDao().insertItem(newItem) }
        }.subscribeOn(RxSchedulers.io)
    }
}
@SuppressWarnings("checkResult")
class ReposViewModel(
        private val repo: ReposRepository
) : BaseViewModel() {

    .......
    fun setItemSelectMode(item: Repo, state: Int) {
        repo.setSelectMode(item, state)
    }

    .......
}

class ReposFragment : BaseFragment() {

    .......
    private fun binds() {

        .......
        // list item clicked event.
        mAdapter.getItemClickEvent()
                .autoDisposable(scopeProvider)
                .subscribe(::itemSubViewClickAction)

        .......
    }

    private fun itemSubViewClickAction(item: Repo) {
        mViewModel.setItemSelectMode(item, if (item.forksCount != 1000) 1000 else 1)
    }
}
qingmei2 commented 5 years ago

@windows7lake

你好,不好意思我没有太理解你的意思:

但是因为DiffUtil.ItemCallback中的areContentsTheSame(oldItem: Repo, newItem: Repo)方法返回的oldItem和newItem是相同的,导致点赞状态不会被更新。

这时的对象为何会是相同的呢,我的理解是item.forksCount发生了变化,Paging内部会自动执行onItemChanged的更新逻辑呀。

windows7lake commented 5 years ago

@qingmei2 我的想法是和你一样的,我也认为Paging内部应该会自动执行onItemChanged的更新逻辑才对,所以我就一直寻找问题的原因,直到打印areContentsTheSame(oldItem: Repo, newItem: Repo)中的log时才发现:点击同一个item时,返回的oldItem和newItem是相同的。 我也看了一下DiffUtil的相关源码,也没有什么收获。你之前有用paging实现过类似的功能吗?是怎么处理的呢?

windows7lake commented 5 years ago

下面是连续点击同一个item时输出的log: submitList是提交给PageList的数据,areContentsTheSame 是DiffUtil打印的数据


2019-08-14 16:07:56.312 29145-29145/com.qingmei2.sample E/ReposFragment$override: =============submitList  id: 200820107   count: 1
2019-08-14 16:07:56.314 29145-29759/com.qingmei2.sample E/ReposPagedAdapter$Companion$diffCallback$1$override: =============areContentsTheSame id: 200820107  ==  oldItem: 1   ==   newItem: 1  

2019-08-14 16:07:59.610 29145-29145/com.qingmei2.sample E/ReposFragment$override: =============submitList  id: 200820107   count: 1000
2019-08-14 16:07:59.612 29145-29904/com.qingmei2.sample E/ReposPagedAdapter$Companion$diffCallback$1$override: =============areContentsTheSame id: 200820107  ==  oldItem: 1   ==   newItem: 1000  

2019-08-14 16:08:12.650 29145-29145/com.qingmei2.sample E/ReposFragment$override: =============submitList  id: 200820107   count: 1
2019-08-14 16:08:12.652 29145-29759/com.qingmei2.sample E/ReposPagedAdapter$Companion$diffCallback$1$override: =============areContentsTheSame id: 200820107  ==  oldItem: 1   ==   newItem: 1  

2019-08-14 16:08:21.999 29145-29145/com.qingmei2.sample E/ReposFragment$override: =============submitList  id: 200820107   count: 1000
2019-08-14 16:08:22.003 29145-29904/com.qingmei2.sample E/ReposPagedAdapter$Companion$diffCallback$1$override: =============areContentsTheSame id: 200820107  ==  oldItem: 1   ==   newItem: 1000  
`
windows7lake commented 5 years ago

快速点击item的log,快速点击同一个item的时候,forksCount在界面上的显示有时会变化

2019-08-14 16:13:07.925 29145-29145/com.qingmei2.sample E/ReposFragment$override: =============submitList  id: 200820107   count: 1
2019-08-14 16:13:07.928 29145-29759/com.qingmei2.sample E/ReposPagedAdapter$Companion$diffCallback$1$override: =============areContentsTheSame id: 200820107  ==  oldItem: 1   ==   newItem: 1  
2019-08-14 16:13:15.385 29145-29145/com.qingmei2.sample E/ReposFragment$override: =============submitList  id: 200820107   count: 1000
2019-08-14 16:13:15.387 29145-29904/com.qingmei2.sample E/ReposPagedAdapter$Companion$diffCallback$1$override: =============areContentsTheSame id: 200820107  ==  oldItem: 1   ==   newItem: 1000  
2019-08-14 16:13:16.361 29145-29145/com.qingmei2.sample E/ReposFragment$override: =============submitList  id: 200820107   count: 1
2019-08-14 16:13:16.364 29145-29759/com.qingmei2.sample E/ReposPagedAdapter$Companion$diffCallback$1$override: =============areContentsTheSame id: 200820107  ==  oldItem: 1   ==   newItem: 1  
2019-08-14 16:13:17.043 29145-29145/com.qingmei2.sample E/ReposFragment$override: =============submitList  id: 200820107   count: 1000
2019-08-14 16:13:17.045 29145-29904/com.qingmei2.sample E/ReposPagedAdapter$Companion$diffCallback$1$override: =============areContentsTheSame id: 200820107  ==  oldItem: 1   ==   newItem: 1000  
2019-08-14 16:13:17.747 29145-29145/com.qingmei2.sample E/ReposFragment$override: =============submitList  id: 200820107   count: 1
2019-08-14 16:13:17.749 29145-29759/com.qingmei2.sample E/ReposPagedAdapter$Companion$diffCallback$1$override: =============areContentsTheSame id: 200820107  ==  oldItem: 1   ==   newItem: 1  
2019-08-14 16:13:18.325 29145-29145/com.qingmei2.sample E/ReposFragment$override: =============submitList  id: 200820107   count: 1000
2019-08-14 16:13:18.327 29145-29904/com.qingmei2.sample E/ReposPagedAdapter$Companion$diffCallback$1$override: =============areContentsTheSame id: 200820107  ==  oldItem: 1   ==   newItem: 1000  
2019-08-14 16:13:19.511 29145-29145/com.qingmei2.sample E/ReposFragment$override: =============submitList  id: 200820107   count: 1000
2019-08-14 16:13:19.512 29145-29759/com.qingmei2.sample E/ReposPagedAdapter$Companion$diffCallback$1$override: =============areContentsTheSame id: 200820107  ==  oldItem: 1000   ==   newItem: 1000  
2019-08-14 16:13:21.042 29145-29145/com.qingmei2.sample E/ReposFragment$override: =============submitList  id: 200820107   count: 1
2019-08-14 16:13:21.044 29145-29904/com.qingmei2.sample E/ReposPagedAdapter$Companion$diffCallback$1$override: =============areContentsTheSame id: 200820107  ==  oldItem: 1000   ==   newItem: 1  
2019-08-14 16:13:21.844 29145-29145/com.qingmei2.sample E/ReposFragment$override: =============submitList  id: 200820107   count: 1000
2019-08-14 16:13:21.846 29145-29759/com.qingmei2.sample E/ReposPagedAdapter$Companion$diffCallback$1$override: =============areContentsTheSame id: 200820107  ==  oldItem: 1000   ==   newItem: 1000  
2019-08-14 16:13:23.058 29145-29145/com.qingmei2.sample E/ReposFragment$override: =============submitList  id: 200820107   count: 1
2019-08-14 16:13:23.060 29145-29904/com.qingmei2.sample E/ReposPagedAdapter$Companion$diffCallback$1$override: =============areContentsTheSame id: 200820107  ==  oldItem: 1000   ==   newItem: 1  
2019-08-14 16:13:23.959 29145-29145/com.qingmei2.sample E/ReposFragment$override: =============submitList  id: 200820107   count: 1000
2019-08-14 16:13:23.961 29145-29759/com.qingmei2.sample E/ReposPagedAdapter$Companion$diffCallback$1$override: =============areContentsTheSame id: 200820107  ==  oldItem: 1000   ==   newItem: 1000  
2019-08-14 16:13:24.545 29145-29145/com.qingmei2.sample E/ReposFragment$override: =============submitList  id: 200820107   count: 1
2019-08-14 16:13:24.547 29145-29904/com.qingmei2.sample E/ReposPagedAdapter$Companion$diffCallback$1$override: =============areContentsTheSame id: 200820107  ==  oldItem: 1000   ==   newItem: 1  
2019-08-14 16:13:25.030 29145-29145/com.qingmei2.sample E/ReposFragment$override: =============submitList  id: 200820107   count: 1000
2019-08-14 16:13:25.031 29145-29759/com.qingmei2.sample E/ReposPagedAdapter$Companion$diffCallback$1$override: =============areContentsTheSame id: 200820107  ==  oldItem: 1000   ==   newItem: 1000  
2019-08-14 16:13:25.663 29145-29145/com.qingmei2.sample E/ReposFragment$override: =============submitList  id: 200820107   count: 1
2019-08-14 16:13:25.666 29145-29904/com.qingmei2.sample E/ReposPagedAdapter$Companion$diffCallback$1$override: =============areContentsTheSame id: 200820107  ==  oldItem: 1000   ==   newItem: 1  
qingmei2 commented 5 years ago

1和1000本质没有区别,既然点赞能够响应在ui上,取消点赞也能响应在ui上。

会不会是哪里代码实现有问题呢(比如为Dao的接口上试试为@Update配置下冲突策略如OnConflictStrategy.REPLACE)?公司的项目是有实现点赞功能的,但合同约定不能放代码,这个只能靠个人debug了。

windows7lake commented 5 years ago

Room的Insert和Update以及冲突策略OnConflictStrategy.REPLACE,我都试过了,没有什么变化。我之前是在我自己的代码上操作的,我觉得可能是我某些代码的原因,但是我把上面的代码放到clone下来的MVVM-Rhine中也是同样的问题? 另外开始我怀疑可能是AutoDisposeViewHolder问题,所以也尝试把AutoDisposeViewHolder替换成RecyclerView.ViewHolder,可是问题依然存在。

qingmei2 commented 5 years ago

@windows7lake

感谢你的反馈,个人的想法是,尝试一下在源码中打一下断点呢,或者使用相关工具查询一下数据库最新的数据?

windows7lake commented 5 years ago

@qingmei2 谢谢你的提议,我查看了每次写入后的数据库,发现数据是有正常写入的。 然后我断点看了submitList的源码,发现mPagedList 会在某个时间被赋值为当前状态的PagedList,进而导致oldSnapshot 和newSnapshot 一致。 AsyncPagedListDiffer的submitList代码:

public void submitList(@Nullable final PagedList<T> pagedList,
@Nullable final Runnable commitCallback) {

    ......

    if (mPagedList != null) {
        // first update scheduled on this list, so capture mPages as a snapshot, removing
        // callbacks so we don't have resolve updates against a moving target
        mPagedList.removeWeakCallback(mPagedListCallback);
        mSnapshot = (PagedList<T>) mPagedList.snapshot();
        mPagedList = null;
    }

    if (mSnapshot == null || mPagedList != null) {
        throw new IllegalStateException("must be in snapshot state to diff");
    }

    final PagedList<T> oldSnapshot = mSnapshot;
    final PagedList<T> newSnapshot = (PagedList<T>) pagedList.snapshot();
    mConfig.getBackgroundThreadExecutor().execute(new Runnable() {
        @Override
        public void run() {
            final DiffUtil.DiffResult result;
            result = PagedStorageDiffHelper.computeDiff(
                    oldSnapshot.mStorage,
                    newSnapshot.mStorage,
                    mConfig.getDiffCallback());

            mMainThreadExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    if (mMaxScheduledGeneration == runGeneration) {
                        latchPagedList(pagedList, newSnapshot, result,
                                oldSnapshot.mLastLoad, commitCallback);
                    }
                }
            });
        }
    });
}

我有点好奇你的DiffUtil.ItemCallback和我的有什么区别吗?还是说做了什么特殊的处理?另外你的paging-runtime版本是多少呢?能不能提供一个小demo学习一下。谢谢!

windows7lake commented 5 years ago

能讲一下你那边用paging实现点赞的方法吗?感觉像是paging的问题