Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ChangeListener called after removeChangeListener #2401

Closed
huibvv opened this issue Mar 8, 2016 · 9 comments
Closed

ChangeListener called after removeChangeListener #2401

huibvv opened this issue Mar 8, 2016 · 9 comments
Labels

Comments

@huibvv
Copy link

huibvv commented Mar 8, 2016

Goal (what do you want to achieve?)

Notify on change

Expected Results

Not being notified when removing the listener

Actual Results (e.g. full stack trace with exception)

Notification fires after the listener has been removed

Steps & Code to Reproduce (Or describe your current debugging efforts)

The activity containing the views depends on multiple records. I want these views to be updated in real time, so i've put a listener to the child list. After removing the listener (and after the activity has been destroyed), the listener is still being called.

 @Override
    public void onPause() {
        if(mHardware != null){
            mHardware.getValues().where().findAll().removeChangeListener(mListener);
        }
        super.onPause();
    }

    @Override
    public void onResume() {
        redraw();
        if(mHardware != null){
            mHardware.getValues().where().findAll().addChangeListener(mListener);
        }
        super.onResume();
    }

    private RealmChangeListener mListener = new RealmChangeListener() {
        @Override
        public void onChange() {
            redraw();
        }
    };

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        mRealm.close();
    }

Version of Realm and tooling

Realm version(s): 0.87.5

Android Studio version: 1.5.1

Android version(s) on device/simulator: 21

@beeender
Copy link
Contributor

beeender commented Mar 9, 2016

mHardware.getValues().where().findAll().removeChangeListener(mListener);

this line equals to

RealmResults newResults = mHardware.getValues().where().findAll();
newResults.removeChangbeListener(mListener);

It creates a new RealmResults which is different from the one created in onResume, and remove the listener on that one, because of there is no listener registered on it, removeChangeListener just does nothing.

To fix this:

private RealmResults results;

@Override
public void onResume() {
    redraw();
    if(mHardware != null) {
        // You might also want to check if the results is null here, no need to rebuild a Results if it exists.
        results = mHardware.getValues().where().findAll();
        results.addChangeListener(mListener);
    }
    super.onResume();
}

@Override
public void onPause() {
    if(mHardware != null){
        results.removeChangeListener(mListener);
    }
    super.onPause();
}

@jjhesk
Copy link

jjhesk commented Mar 9, 2016

what do i need to do in the adapter?
I got
mutable call during read transaction io.realm adapter on the recyclerview when it removes the item from the list. Mutable method call during read transaction
Here is my recyclerview adapter extension.

package com.hkm.hbstore.adapters.bookmark;

import android.support.v7.widget.RecyclerView;

import com.hypebeast.sdk.api.realm.hbx.rProduct;
import com.marshalchen.ultimaterecyclerview.UltimateRecyclerviewViewHolder;
import com.marshalchen.ultimaterecyclerview.quickAdapter.easyRegularAdapter;

import java.util.ArrayList;
import java.util.List;

import io.realm.RealmChangeListener;
import io.realm.RealmObject;
import io.realm.RealmResults;

/**
 * Created by hesk on 9/3/16.
 */
public abstract class realmUltimateAdapter<T extends RealmObject, BINDHOLDER extends UltimateRecyclerviewViewHolder> extends easyRegularAdapter<T, BINDHOLDER> {

    protected RealmResults<T> source;
    private final RealmChangeListener listener;

    public realmUltimateAdapter(boolean automaticUpdate, RealmResults<T> source) {
        super(source);
        this.source = source;
        listener = (!automaticUpdate) ? null : new RealmChangeListener() {
            @Override
            public void onChange() {
                notifyDataSetChanged();
            }
        };

        if (listener != null && source != null) {
            //    source.realm.handlerController.addChangeListenerAsWeakReference(listener);
            source.addChangeListener(listener);
        }
    }


    @Override
    public int getAdapterItemCount() {
        return source.size();
    }


    protected T getItem(final int pos) {
        synchronized (mLock) {
            return source.get(pos);
        }
    }

    /**
     * Determine if the object provide is in this adapter
     *
     * @return true if the object is in this adapter
     */
    public boolean hasItem(T object) {
        synchronized (mLock) {
            return source.contains(object);
        }
    }

    /**
     * Returns whether this {@code List} contains no elements.
     *
     * @return {@code true} if this {@code List} has no elements, {@code false}
     * otherwise.
     * @see #source
     */
    public boolean isEmpty() {
        return source.size() == 0;
    }

    /**
     * @return a copy of the {@code List} of elements.
     */
    public RealmResults<T> getObjects() {
        synchronized (mLock) {
            return source;
        }
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        //    if (position >= getAdapterItemCount()) return;
        if (getItemViewType(position) == VIEW_TYPES.ADVIEW) {
            onBindAdViewHolder(holder, position);
        } else if (getItemViewType(position) == VIEW_TYPES.CUSTOMVIEW) {
            onBindCustomViewHolder(holder, position);
        } else if (getItemViewType(position) == VIEW_TYPES.HEADER) {
            onBindHeaderViewHolder(holder, position);
        } else if (getItemViewType(position) == VIEW_TYPES.FOOTER) {
            onBindFooterViewHolder(holder, position);
        } else if (getItemViewType(position) == VIEW_TYPES.NORMAL) {
            // if (position >= getAdapterItemCount()) return;
            T object;
            synchronized (mLock) {
                object = source.get(getItemDataPosFromInternalPos(position));
            }
            withBindHolder((BINDHOLDER) holder, object, position);
        }
    }


    public final void removeInternalR(int position) {
        if (hasHeaderView() && position == 0) return;
        if (enableLoadMore() && position == getItemCount() - 1) return;
        if (source.size() > 0) {
            synchronized (mLock) {
                source.removeChangeListener(listener);
                source.remove(hasHeaderView() ? position - 1 : position);
            }
            notifyItemRemoved(position);
        }
    }
}

@jjhesk
Copy link

jjhesk commented Mar 9, 2016

finally this is my solution

public final void removeInternalRealmSupport(int position) {
        if (hasHeaderView() && position == 0) return;
        if (enableLoadMore() && position == getItemCount() - 1) return;
        if (source.size() > 0) {
            synchronized (mLock) {
                Realm realm = Realm.getInstance(RealmUtil.realmCfg(ctm));
                realm.beginTransaction();
                source.remove(hasHeaderView() ? position - 1 : position);
                realm.commitTransaction();
            }
            notifyItemRemoved(position);
        }
    }

add this above code in the adapter to get all the remove issue resolve.

@beeender
Copy link
Contributor

beeender commented Mar 9, 2016

You can just use Realm realm = source.getRealm() to get the Realm instance which is used to create the RealmResults.

@beeender
Copy link
Contributor

beeender commented Mar 9, 2016

Please just ignored what i said above. RealmResults.getRealm() will return a BaseRealm which is not public, and cannot be used outside. What you did in #2401 (comment) is OK. But please notice that we are maintaining reference counter for creating only one Realm instance (to the same file) in every thread. So the returned realm by getInstance() here is actually the same Realm instance as the one created source. Otherwise it won't work. Sorry for the misleading.

@jjhesk
Copy link

jjhesk commented Mar 9, 2016

I have tried getRealm and it did not get the thing because it was private or protected.

@jjhesk
Copy link

jjhesk commented Mar 9, 2016

@beeender i think it is still not the perfect solution as i have recall the realm from the initiation. Are there any options that i can call directly from the source object and get the instance of the Realm?

@cmelchior
Copy link
Contributor

@jjhesk Why not just add the Realm instance as an extra parameter in the constructor?

@beeender
Copy link
Contributor

@huibvv @jjhesk I hope your issues have been solved. I am closing this now, feel free to reopen it if needed.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 17, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

4 participants