Zhuinden / realm-monarchy

[ACTIVE-ISH] A wrapper over Realm which exposes it as LiveData, managing Realm lifecycle internally.
Apache License 2.0
88 stars 11 forks source link

Sometimes not triggered getChangeRanges inside dispatchUpdatesTo #35

Closed NightDeveloper-wq closed 3 years ago

NightDeveloper-wq commented 3 years ago

I have an activity which has fragment. When the fragment is first opened, I set the observer and the adapter, then I make my realm query with fillQuery and pull all the data. When it is updated, I call adapter's updateData function through the observer in fragment. It also calls the dispatchUpdatesTo function, but this function is not triggered with every change. getInsertionRanges and getChangeRanges values are not triggered on every change and addition inside MonarchyDiffResult.

I have a list from 80th to 92nd index. As seen in my log in this list, index updates 85,87,89 do not come to the getChangeRanges function in any way. This causes those indexes not to be updated.

I wondered if there was an error in my code. Can you please help?

My log:

modifications state:UPDATE range.startIndex:80 range.length:2 modifications state:UPDATE range.startIndex:81 range.length:1 modifications state:UPDATE range.startIndex:86 range.length:1 modifications state:UPDATE range.startIndex:88 range.length:1 modifications state:UPDATE range.startIndex:90 range.length:1 modifications state:UPDATE range.startIndex:92 range.length:1 modifications state:UPDATE range.startIndex:80 range.length:1 modifications state:UPDATE range.startIndex:82 range.length:1 modifications state:UPDATE range.startIndex:83 range.length:2 modifications state:UPDATE range.startIndex:88 range.length:1 modifications state:UPDATE range.startIndex:90 range.length:2 modifications state:UPDATE range.startIndex:92 range.length:1

My fragment code:

public abstract class RealmFragment<T extends RealmModel> extends Fragment {

    public static String TAG = RealmFragment.class.getSimpleName();
    protected RealmResults<T> allResults;
    private LiveData<Monarchy.ManagedChangeSet<T>> changesModel;
    private Observer<Monarchy.ManagedChangeSet<T>> observer;
    private RealmAdapter realmAdapter;
    private boolean isFillDatas = false;
    private Parcelable recyclerViewState;

    @Override
    public void onResume() {
        super.onResume();

        if (MyApplication.getInstance().isInMultiWindowMode()) {
            clearDatas();
        }
        setValues();
    }

    @Override
    public void onPause() {
        super.onPause();
        callInitValues();
    }

    protected void initializeListAdapter() {

        try {
            if (recyclerView.getAdapter() == null || getRealmAdapter() == null) {
                setAdapter();
            }

            if (getAllResults() == null || !getAllResults().isValid()) {
                fillQuery();
            }

            if (recyclerView.getAdapter() != null && getRealmAdapter() != null && !isFillDatas) {
                setFillDatas(true);
            }

        } catch (Exception e) {
            Mylog.printStackTrace(TAG + " initializeListAdapter error ", e);
        }
    }

    public void fillData(Query<T> query, boolean isAsync) {
        removeChangesAndObservers();
        setDatas(query, isAsync, false);
    }

    public void fillData(RealmResults<T> realmResults, boolean isAsync) {
        removeChangesAndObservers();
        allResults = realmResults;

        if (changesModel == null) {
            setChanges(getCurrentActivity().getMonarchy().findAllManagedWithChanges(realmResults, isAsync));
        }
    }

    private void setDatas(Query<T> query, boolean isAsync, boolean isComingFromSearch) {
        setAllResultsByRealmQuery(query, isAsync);
        if (changesModel == null || isComingFromSearch) {
            setChanges(getCurrentActivity().getMonarchy().findAllManagedWithChanges(query, isAsync));
        }
    }

    protected void setUpRecycler(boolean hasFixedSize, boolean stackFromEnd, boolean scrollEnabled,
                                 ActivityCreator activityCreator,
                                 RecyclerView.ItemDecoration itemDecoration) {

        linearLayoutManager = (SmoothScrollingLinearLayoutManager) recyclerView.getLayoutManager();

        if (linearLayoutManager == null) {
            linearLayoutManager = new SmoothScrollingLinearLayoutManager(activityCreator, false);
        }

        recyclerView.setLayoutManager(linearLayoutManager);
        recyclerView.setNestedScrollingEnabled(scrollEnabled);
        linearLayoutManager.setStackFromEnd(stackFromEnd);
        recyclerView.setHasFixedSize(hasFixedSize);
        recyclerView.setItemAnimator(null);

        if (itemDecoration != null) {
            recyclerView.addItemDecoration(itemDecoration);
        }

        setValues();
    }

    private void setAllResultsByRealmQuery(Query<T> query, boolean isAsync) {
        if (isAsync) {
            allResults = query.createQuery(getCurrentActivity().getRealm()).findAllAsync(); // sort/distinct should be done with new queries, 5.0+
        } else {
            allResults = query.createQuery(getCurrentActivity().getRealm()).findAll(); // sort/distinct should be done with new queries, 5.0+
        }
    }

    public abstract void setAdapter();

    public void setValues() {
        initializeListAdapter();

        if (observer == null) {
            setObserver(changes -> {
                realmAdapter.updateData(changes != null ? changes.getRealmResults() : null, new MonarchyDiffResult<>(changes));
                }
            })
        }
    }

    public void fillQuery() {
        fillData(new MessageAndHeaderQueries().getAllMessagesQuery(talkerId), false);
    }

    private void callInitValues() {
        if (MyApplication.getInstance().isInMultiWindowMode()) {
            return;
        }

        clearDatas();
    }

    private void clearDatas() {
        removeObservers();
        setFillDatas(false);
    }

    private void removeObservers() {

        if (changesModel != null) {
            changesModel.removeObservers(this);
        }
    }

    public boolean isFillDatas() {
        return isFillDatas;
    }

    private void setFillDatas(boolean fillDatas) {
        if (fillDatas != this.isFillDatas) {
            isFillDatas = fillDatas;
            if (!isFillDatas) {
                initAllResultsAndObserver();
                if (linearLayoutManager != null) {
                    recyclerViewState = linearLayoutManager.onSaveInstanceState();
                }
            }
        }
    }

    public RealmResults<T> getAllResults() {
        return allResults;
    }

    public void setChanges(LiveData<Monarchy.ManagedChangeSet<T>> changes) {
        this.changesModel = changes;
    }

    private void removeChangesAndObservers() {
        setChanges(null);
        removeObservers();
    }

    public ActivityCreator getCurrentActivity() {
        return MyApplication.getInstance().getCurrentActivity();
    }

    public RealmAdapter getRealmAdapter() {
        return realmAdapter;
    }

    protected void setRealmAdapter(RealmAdapter realmAdapter) {
        this.realmAdapter = realmAdapter;
    }

    public void initAllResultsAndObserver() {
        observer = null;
        allResults = null;
    }

    protected void setObserver(Observer<Monarchy.ManagedChangeSet<T>> observer) {
        this.observer = observer;
        if (linearLayoutManager != null && recyclerViewState != null) {
            linearLayoutManager.onRestoreInstanceState(recyclerViewState);
        }
        changesModel.observe(this, this.observer);
    }

Adapter code:

public abstract class RealmAdapter<T extends RealmModel, VH extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<VH> {

    private List<T> models;
    private String comingTAG;
    private RealmFragment<T> realmFragment;

    public RealmAdapter(RealmFragment<T> realmFragment, String TAG) {

        super();

        this.comingTAG = TAG;
        this.realmFragment = realmFragment;
        this.models = realmFragment.getAllResults();
        setHasStableIds(true);
    }

    @Override
    public int getItemCount() {

        try {
            if (models == null)
                models = realmFragment.getAllResults();

            return models == null ? 0 : models.size();

        } catch (Exception e) {
//            Mylog.printStackTrace(comingTAG + " getItemCount error", e);
        }

        return 0;
    }

    @SuppressWarnings("WeakerAccess")
    @Nullable
    public T getItem(int index) {

        try {

            if (models == null)
                models = realmFragment.getAllResults();

            if (models != null && !models.isEmpty()) {
                return models.get(index);
            }

        } catch (Exception e) {
            Mylog.printStackTrace(comingTAG + " getItem error", e);
        }

        return null;
    }

    public void updateData(List<T> items, @Nullable CustomDiffResult diffResult) {

        this.models = items;

        if (diffResult != null) {
            diffResult.dispatchUpdatesTo(this);
        }

    }
}
Zhuinden commented 3 years ago

Hrmm, well in the change set, I'm just providing what Realm is giving me. Is your RealmResults sorted? To stabilize change indexes, it's best to use some form of sort().

NightDeveloper-wq commented 3 years ago

My list is already sorted but I guess the error is here. The setValue or postValue function or directly onChange function in ManagedLiveResults is not always triggered because of these checks. Can you check it out?

 private OrderedRealmCollectionChangeListener<RealmResults<T>> realmChangeListener = new OrderedRealmCollectionChangeListener<RealmResults<T>>() {
        @Override
public void onChange(@Nonnull RealmResults<T> realmResults, @Nonnull OrderedCollectionChangeSet changeSet) {
            Logger.i("xx changeListener changeSet:" + changeSet + "realmResults: " + realmResults.asJSON());
            RealmManager.ManagedChangeSet<T> managedChangeSet = new RealmManager.ManagedChangeSet<>(realmResults, changeSet);
            if (changeSet.getState() == OrderedCollectionChangeSet.State.INITIAL) {
                setValue(managedChangeSet);
            } else {
                postValue(managedChangeSet);
            }
        }
    };
Zhuinden commented 3 years ago

Hmm. :/ this could be a bug in my sample then, but you can try just using setValue in both cases

NightDeveloper-wq commented 3 years ago

I thought so but I couldn't be sure. Very thanks