【问题标题】:Architecture components: Observer keep observing even after removing it on onDestroy架构组件:即使在 onDestroy 上移除后,观察者也会继续观察
【发布时间】:2018-09-05 05:44:15
【问题描述】:

我正在开发一个应用程序,我需要每 30 秒进行一次网络调用,并删除以前的数据并插入新的数据。每次插入新数据时,我都会在 RecyclerView 中显示它。我正在使用 Handler 进行网络调用,并使用 LiveData 来观察数据变化。一切正常,只是实时数据观察器多次触发,因此数据被多次删除和插入,结果导致 RecyclerView 频繁刷新,导致它每 30 秒闪烁多次。

下面是我尝试过的代码:

在我的片段中,我这样做:

private LiveData<List<RestaurantTablesModel>> mData;
private Observer<List<RestaurantTablesModel>> mObserver;
private TablesViewModel mViewModel;

 @Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    View mView = inflater.inflate(R.layout.fragment_tables, container, false);
    ButterKnife.bind(this, mView);

    TablesViewModelFactory factory = InjectorUtils.provideTablesFactory(getActivity());
    mViewModel = ViewModelProviders.of(this, factory).get(TablesViewModel.class);

    setUpUserRecyclerView();

    return mView;

}

private void setUpRecyclerView() {

  mData = mViewModel.getTablesData(mLocationID);

    mObserver = tablesModels -> {
        if (tablesModels != null) {
            mTablesRecyclerAdapter.addTables(tablesModels);
            Log.e(LOG_TAG, "setUpUserRecyclerView: tablesModels");
        }
    };

  mData.observe(this, mObserver);
}

移除观察者 onDestroy:

@Override
public void onDestroy() {
   mData.removeObserver(mObserver);
   super.onDestroy();
}

以下是我在 ViewModel 中的方法:

public LiveData<List<TablesModel>> getTablesData(int mLocationID){
    return mRepository.getTablesData(mLocationID);
}

存储库:

public LiveData<List<TablesModel>> getTablesData(int mLocationID){

    LiveData<TablesModel[]> mTablesData = mDataSource.getTablesData();

    mTablesData.observeForever(tablesModels -> {
        mExecutors.diskIO().execute(() -> {

            //Completed: delete old table data if there are conflicts.

            if (tablesModels != null) {
                mDatabaseDao.deleteTables();

                mDatabaseDao.insertTablesData(tablesModels);
            }else {
                Log.e(LOG_TAG, "Nothing: ");
            }
        });
        Log.e("Handlers", "repository getTablesData");
    });

    return mDatabaseDao.getTablesData(mLocationID);
}

数据源:

private MutableLiveData<RestaurantTablesModel[]> mDownloadedTablesModel;

public LiveData<RestaurantTablesModel[]> getTablesData() {
    Log.e("Handlers", "getTablesData");
    fetchTablesData();
    return mDownloadedTablesModel;
}

public void fetchTablesData() {

    if (Utils.isNetworkAvailable(mContext)) {
        NetworkUtils.NetworkInterface mInterface = this;

        handler = new Handler();

        runnableCode = new Runnable() {

            @Override
            public void run() {
                // Do something here on the main thread
                Log.e("Handlers", "Called on network thread");

                URL getTablesURL = NetworkUtils.getAllTableUrl(mContext);

                NetworkUtils.getResponseFromAPI(mContext, getTablesURL, mInterface);

                // Repeat this the same runnable code block again another 30 seconds
                // 'this' is referencing the Runnable object
                handler.postDelayed(this, 30000);
            }
        };

        handler.post(runnableCode);

    } else {
        Log.d(LOG_TAG, "fetchTablesData: No network!");
    }
}

现在的问题是当我的片段被销毁并重新创建时,观察者被多次触发,这里是日志:

09-05 10:28:29.853 3666-3666/? E/TablesFragment: setUpRecyclerView: tablesModels
09-05 10:28:30.039 3666-3666/? E/TablesFragment: setUpRecyclerView: tablesModels
09-05 10:28:30.607 3666-3666/? E/TablesFragment: setUpRecyclerView: tablesModels
09-05 10:28:30.657 3666-3666/? E/TablesFragment: setUpRecyclerView: tablesModels
09-05 10:28:30.669 3666-3666/? E/TablesFragment: setUpRecyclerView: tablesModels
09-05 10:28:30.704 3666-3666/? E/TablesFragment: setUpRecyclerView: tablesModels

而且它触发的次数比以前更多,每次重新创建片段时,我认为观察者在片段的重新创建时被调用,并且观察者的前一个实例仍在播放中。

但如果我在 OnDestroy 中移除观察者,为什么会发生这种情况? 任何帮助将不胜感激。

编辑:

我更改了代码以检查 LiveData 和 Observer 是否为空,然后仅对其进行初始化。但它没有帮助,它仍然被多次调用。

if (mTablesData == null){
        mData = mViewModel.getTablesData(mLocationID);

        if (mObserver == null){
            mObserver = tablesModels -> {
                if (tablesModels != null) {
                    mTablesRecyclerAdapter.addTables(tablesModels);
                    Log.e(LOG_TAG, "setUpUserRecyclerView: tablesModels");
                }
            };

            mData.observe(this, mObserver);
        }

    }

编辑 2:

也试过了,但效果不佳:

   mTablesData = mViewModel.getTablesData(mLocationID);

    mObserver = tablesModels -> {
        if (tablesModels != null) {
            mTablesRecyclerAdapter.addTables(tablesModels);
            Log.e(LOG_TAG, "setUpRecyclerView: tablesModels");
        }
    };

    if (!mTablesData.hasObservers()) {
        mTablesData.observe(this, mObserver);
    }

【问题讨论】:

  • onDestroy 方法是否被调用?您是否尝试设置断点以查看观察者是否真的被移除?
  • 我只是将 LOG 放入 onDestroy,是的,它正在被调用
  • 正如@pskink 提到的那样,也许您的方法被多次调用。在创建这些变量之前检查 mData 和 mObserver 是否为空,以防止重新创建。但是这个方法应该只调用一次。
  • 也尝试在你的 repo 中做同样的事情:mTablesData.observeForever(tablesModels -&gt; {...。看起来是根本原因,不建议使用mTablesData.observeForever
  • 销毁时不需要mData.removeObserver(mObserver);,因为这是实时数据的功能,它们会自动取消订阅。 BTW,如何将下载的数据设置为 mDownloadedTablesModel?

标签: android android-room android-handler android-architecture-components android-livedata


【解决方案1】:

所以我们从 cmets 的实验中了解到,你需要在观察之前检查 mTablesData 是否已经被观察到,并且只有在没有被观察到时才观察,比如

if (!mTablesData.hasObservers()) { mTablesData.observeForever(tablesModels -> { ...

【讨论】:

    【解决方案2】:

    首先,如果我理解正确,您使用RecyclerViewRecyclerView 中的每个片段在其setUpUserRecyclerView(); 方法中调用setUpUserRecyclerView();。所以,如果你有 3 个片段,你将有 3 个观察者。如果你想让他们都使用ActivityViewModel 你必须在这里指向父Activity -> ViewModelProviders.of(getActivity(), factory)

    第二,你为什么在你的存储库中使用observeForever?你可以只用observe吗?

    最后,如果您想在每 30 秒内运行一次此请求,为什么不使用 PeriodicWorkRequest of WorkManager -> https://developer.android.com/topic/libraries/architecture/workmanager/basics#java

    希望我能以某种方式提供帮助:)

    【讨论】:

    • 不,我只有一个片段有RecyclerView。无论如何,我在 Deividas Strioga 的帮助下解决了这个问题。但是感谢您告诉我有关 PeriodicWorkRequest 的信息,我一定会检查一下
    • 酷,很乐意至少在一件事上提供帮助
    【解决方案3】:

    我认为您需要将 mObserver 包装在 CompositeDisposable 中。

    CompositeDisposable disposable = new CompositeDisposable();
    
    disposable.add(mObserver);
    
    @Override
    public void onDestroy() {
       mData.removeObserver(mObserver);
       disposable.clear();
       super.onDestroy();
    }
    

    希望对你有帮助。

    【讨论】:

    • 和RxJAVA有关吗?因为我没有使用它
    • 对不起,我以为观察者是 rxjava 观察者
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-07-17
    • 1970-01-01
    • 2018-09-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-08
    相关资源
    最近更新 更多