I have a table which stores the list of products. I want to get notified only when any of the row gets updated.Can I use RealmChangeListener. Below id my code
public void updateProducts(final List<Product> products) {
Realm realmObj = Realm.getDefaultInstance();
for (Product product : products) {
if (product.shouldBeDeleted()) {
delete(product.getBarcode());
} else {
realmObj.beginTransaction();
realmObj.copyToRealmOrUpdate(product);
realmObj.commitTransaction();
}
}
realmObj.close();
}
Yes you can use realm change listener...
I am using in my project like this
product.addChangeListener(new RealmChangeListener<RealmModel>() {
#Override
public void onChange(RealmModel realmModel) {
//Your code when any row update or insert new record
}
});
For detail you can refer this link
https://realm.io/docs/java/latest/api/io/realm/RealmChangeListener.html
You can use our fine-grained collection notifications, which will report what kind of modification are done:
RealmResults<Product> products = realm.where(Product.class).findAll();
products.addChangeListener(new OrderedRealmCollectionChangeListener<RealmResults<Product>>() {
#Override
public void onChange(RealmResults<Product> products, OrderedCollectionChangeSet changeSet) {
for (int i : changeSet.getChanges()) {
// Item at index i was updated
}
}
});
public void updateProducts(final List<Product> products) {
try(Realm r = Realm.getDefaultInstance()) {
r.executeTransaction((realm) -> {
for (Product product : products) {
if (product.shouldBeDeleted()) {
delete(product.getBarcode());
} else {
realm.insertOrUpdate(product);
}
}
});
}
}
And elsewhere on UI thread:
private Realm realm;
private RealmResults<Product> results;
private RealmChangeListener<RealmResults<Product>> listener = (element) -> {
if(element.isLoaded()) {
// results are loaded, or a change occurred!
}
};
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.some_layout);
realm = Realm.getDefaultInstance();
results = realm.where(Product.class).findAllAsync();
results.addChangeListener(listener);
}
#Override
public void onDestroy() {
super.onDestroy();
results.removeChangeListener(listener);
results = null;
realm.close();
}
P.S. you'll want to be notified on inserts and deletes as well, or your UI will get desynchronized. Thankfully that is the default behavior.
Related
I have an Android app which uses firestore as its database. I have followed this series of blog posts to set up my firestore database in my app : https://firebase.googleblog.com/2017/12/using-android-architecture-components.html and then followed this stackoverflow entry to change my code to work for firestore: Android Architecture Components with Firebase specifically Firestore.
After this I was successful to display the result of my query in a recycler view, however when I added the swap to update (I do soft delete by setting a isActive flag to false) action in my app, LiveData was inconsistent in refreshing the RecyclerView. Here is my code snippets:
MainActivity.java
TaskViewModel viewModel =
ViewModelProviders.of(this).get(TaskViewModel.class);
LiveData<LinkedList<TaskProperties>> liveData = viewModel.getTaskPropertiesLiveData();
final MainActivity mainActivityReference = this;
liveData.observe(this, new Observer<LinkedList<TaskProperties>>() {
#Override
public void onChanged(#Nullable LinkedList<TaskProperties> taskProperties) {
if (taskProperties != null) {
// Get a handle to the RecyclerView.
mRecyclerView = findViewById(R.id.recyclerview);
// Create an adapter and supply the data to be displayed.
mAdapter = new TaskListAdapter(mainActivityReference, taskProperties);
// Connect the adapter with the RecyclerView.
ItemTouchHelper.Callback callback = new SimpleItemTouchHelperCallback(mAdapter);
ItemTouchHelper touchHelper = new ItemTouchHelper(callback);
touchHelper.attachToRecyclerView(mRecyclerView);
mRecyclerView.setAdapter(mAdapter);
// Give the RecyclerView a default layout manager.
mRecyclerView.setLayoutManager(new LinearLayoutManager(mainActivityReference));
}
}
});
View Model:
public class TaskViewModel extends ViewModel {
private LinkedList<TaskProperties> taskProperties;
private static final Query PROJECT_REF = FirebaseFirestore.getInstance().collection("project").whereEqualTo("active", true);
private final FirebaseQueryLiveData liveData = new FirebaseQueryLiveData(PROJECT_REF);
public TaskViewModel() {
taskPropertiesLiveData.addSource(liveData, new Observer<QuerySnapshot>() {
#Override
public void onChanged(#Nullable final QuerySnapshot querySnapshot) {
if (querySnapshot != null) {
new Thread(new Runnable() {
#Override
public void run() {
taskProperties = new LinkedList<TaskProperties>();
for (DocumentSnapshot document : querySnapshot.getDocuments()) {
taskProperties.addLast(document.toObject(TaskProperties.class));
}
taskPropertiesLiveData.postValue(taskProperties);
}
}).start();
} else {
taskPropertiesLiveData.setValue(null);
}
}
});
}
#NonNull
public LiveData<LinkedList<TaskProperties>> getTaskPropertiesLiveData() {
return taskPropertiesLiveData;
}
}
Code in the callback class to remove :
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
mAdapter.onItemDismiss(viewHolder.getAdapterPosition());
}
Constructor in Adapter:-
public TaskListAdapter(Context context,LinkedList<TaskProperties> taskList) {
mInflater = LayoutInflater.from(context);
this.taskList = taskList;
}
Code in Adapter to remove:-
public void onItemDismiss(int position) {
TaskDao taskDao = new TaskDao();
taskDao.softDeleteTaskInDB(taskList.get(position));
}
Code in DAO class to update( soft delete) :-
public void softDeleteTaskInDB(TaskProperties taskProperties){
taskProperties.setActive(false);
database.collection("project")
.document(taskProperties.getTask())
.set(taskProperties).
addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Log.d(DEBUG_TAG, "DocumentSnapshot successfully written!");
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.w(DEBUG_TAG, "Error writing document", e);
}
});
Log.i(DEBUG_TAG,taskProperties.getTask());
}
I have observed that LiveData was able to refresh the view when I was deleting one component from the end of the list, however when I deleted from the middle of the list the view sometimes does not refresh properly. From the logs I found that the position that is being passed into the adapter class is working fine, however the tasklist array does not have the most updated value.
For example if the task list contains :-
Cat
Dog
Mouse
Rabbit
Tiger
and if delete Mouse and then Rabbit in quick succession, the onItemDismiss in adapter class receives position 3 in both cases, but the taskList variable in the Adapter class still contains Mouse at position 3. This means the LiveData might not have refreshed the RecyclerView.
Can someone please tell me where am I going wrong?
Thanks,
Sangho
I have this method, i want return value when the transaction complete, but i cant. This's my code
public List<Group> getConversations() {
final RealmResults<Group> conversations;
try {
mRealm = Realm.getDefaultInstance();
mRealm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
RealmResults<Group> conversations = realm.where(Group.class).findAllSorted("time", Sort.DESCENDING);
cursorConversation(conversations);
}
}, new Realm.Transaction.OnSuccess() {
#Override
public void onSuccess() {
//return conversation
}
});
}
return null;
}
What should i do ?
I am not sure what are you doing in cursorConversation(..) but you can use the same method on returned values from Realm.
give a try
public List<Group> getConversations() {
try (Realm realm = Realm.getDefaultInstance()) {
return realm.copyFromRealm(realm.where(Group.class).findAllSorted("time", Sort.DESCENDING));
}
}
You don't need to run a transaction for getting the conversations. You can run your query on the realm db and add a change listener to the result. When the query completes, it'll call that change listener with the RealmResults<Converstaion>, Check this link for more.
Something like
public void listenToConversations(RealmChangeListener<RealmResults<Conversation>> listener) {
RealmResults<Conversations> conversations = realm.where(Group.class).sort("time", Sort.DESCENDING).findAllAsync();
conversations.addChangeListener(listener);
}
where listener is something like
listener = new RealmChangeListener<RealmResults<Conversations>>() {
\#Override
public void onChange(RealmResults<Conversations> conversations) {
// React to change
}
}
You'll also need to remove listener to avoid any memory leaks.
io.realm:realm-gradle-plugin:2.0.0'
Android Studio 2.2.2
I am trying to delete objects from the realm database. The items seems to get deleted. But when I close the app and load items from the database the deleted ones still seem to have a reference to them. This is my code below for deleting.
If the delete onSuccess is called I send back the item to be removed from the recyclerview's adapter. Is this the correct way to do this?
#Override
public void deletePerson(final Person person, final DeleteListener deleteListener) {
mRealm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
RealmResults<Person> results = realm.where(Person.class).equalTo("mId", person.getId()).findAll();
results.deleteAllFromRealm();
}
}, new Realm.Transaction.OnSuccess() {
#Override
public void onSuccess() {
/* send the person object back to be removed from the recyclerview after success*/
deleteListener.onDeleteSuccess(person);
}
}, new Realm.Transaction.OnError() {
#Override
public void onError(Throwable error) {
deleteListener.onDeleteFailure(error.getMessage());
}
});
}
And when I load the persons the ones that are deleted seem to have a reference in realm and doesn't seem to be completely removed.
#Override
public void loadPersons(final LoadPersonListener loadPersonListener) {
if(mRealm.isClosed()) {
mRealm = Realm.getDefaultInstance();
}
RealmResults<Person> personsList = mRealm.where(Person.class).findAll();
if(personsList.size() > 0) {
loadPersonListener.onLoadPersonSuccess(personsList);
}
else {
loadPersonListener.onLoadPersonFailure("No items in the database");
}
}
You aren't removing anything from the Realm at the moment, you're just querying. Also, you're accessing the Person you sent in on a background thread, which ought to throw IllegalStateException.
So instead of
#Override
public void deletePerson(final Person person, final DeleteListener deleteListener) {
mRealm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
RealmResults<Person> results = realm.where(Person.class).equalTo("mId", person.getId()).findAll();
}
You should have
#Override
public void deletePerson(final Person person, final DeleteListener deleteListener) {
final String id = person.getId();
mRealm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
realm.where(Person.class).equalTo("mId", id).findAll().deleteAllFromRealm();
}
Here in you code you only query data asynchronously. For deletion use .remove() method while looping on result of query.
Suppose, that mId is #Primary key, removal will look like:
RealmResults<Person> results = realm.where(Person.class).equalTo("mId", person.getId()).findFirst().removeFromRealm();
I'm trying to read from Firebase (offline), but the method onDataChange is never called,
private class MyAuthStateListener implements Firebase.AuthStateListener {
#Override
public void onAuthStateChanged(AuthData authData) {
if (authData != null) {
// user is logged in
userId = authData.getUid();
mFirebase = mFirebase.child(authData.getUid()).child("xxx");
Query mQuery = mFirebase.orderByChild("externalId").equalTo(externalId);
mQuery.addListenerForSingleValueEvent(new MyValueEventListener());
}
}
}
private class MyValueEventListener implements ValueEventListener {
#Override
public void onDataChange(DataSnapshot mDataSnapshot) {
if (mDataSnapshot.hasChildren()) {
Iterable<DataSnapshot> i = mDataSnapshot.getChildren();
Iterator<DataSnapshot> mIterator = i.iterator();
if (mIterator.hasNext()) {
mArrayList.clear();
}
while (mIterator.hasNext()) {
DataSnapshot c = mIterator.next();
mArrayList.add(c.getValue(mObject.class));
}
}
onTaskComplete(); // call the notifyDataSetChange() on the adapter.
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
}
Have I done something wrong?
Online it works well, but offline it won't works.
The new objects are created with a FAB that open a new activity (to modify the object) and than I return to that activity.
Thank you all for the help.
I want to connect to the data in the database and display it in list view
but I have Error in object Realm and stopped my application
How can solve the problem??
protected void onResume() {
super.onResume();
Realm realm = Realm.getInstance(getApplicationContext());
realm.beginTransaction();
List<Car> cars = realm.allObjects(Car.class);
String[] names = new String[cars.size()];
for (int i = 0; i < names.length; i++) {
names[i] = cars.get(i).getName();
}
ListView listView = (ListView) findViewById(R.id.listView);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1);
listView.setAdapter(adapter);
}
}
Apart from that you did not specify the logcat, Realm probably tells you that you've left a transaction open and crashes instead.
You only need transactions to write into RealmObjects that are attached to a realm. You also need to close every realm instance that you open. You also must note that you cannot read from a closed realm.
For example, this works:
Realm realm = null;
try {
Cat cat = new Cat(); //public class Cat extends RealmObject {
cat.setName("Meowmeow"); //cat is not yet attached to a realm, therefore you can modify it
realm = Realm.getInstance(context); //open instance of default realm
realm.beginTransaction();
realm.copyToRealmOrUpdate(cat); //cat is now attached to the realm,
//and cannot be written outside the transaction.
realm.commitTransaction();
} catch(Exception e) {
if(realm != null) {
try { //newer versions of Realm like 0.84.0+ have `realm.isInTransaction()`
realm.cancelTransaction();
} catch(IllegalStateException e) {
//realm not in transaction
}
}
throw e;
} finally {
if(realm != null) {
realm.close(); //every open realm must be closed
}
}
If you're using a newer version of Realm, you can also do this on a background thread without all the manual opening and closing.
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
// begin and end transaction calls are done for you
Dog dog = realm.where(Dog.class).equals("age", 1).findFirst();
d.setAge(3);
}
}, new Realm.Transaction.Callback() {
#Override
public void onSuccess() {
// Original RealmResults<T> objects and Realm objects
// are automatically updated
// ON THREADS THAT HAVE A LOOPER ASSOCIATED WITH THEM (main thread)
//the realm is written and data is updated, do whatever you want
}
});
And for displaying your data, this works (0.83.0+):
public class HelloWorldActivity extends AppCompatActivity {
protected Realm realm;
ListView listView;
#Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
realm = Realm.getInstance(this);
setContentView(R.layout.activity_hello_world);
this.listView = (ListView)findViewById(R.id.list_view);
listView.setAdapter(new CarAdapter(this, realm.where(Car.class).findAll(), true);
}
#Override
public void onDestroy() {
realm.close();
}
}
And you'll need an adapter for your... listView...
public class CarAdapter extends RealmBaseAdapter<Car> {
public RealmModelAdapter(Context context, RealmResults<Car> realmResults, boolean automaticUpdate) {
super(context, realmResults, automaticUpdate);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO
//implement viewholder pattern here:
//http://developer.android.com/training/improving-layouts/smooth-scrolling.html#ViewHolder
//Listview is obsolete so I won't bother,
//use RecyclerView when you get the chance.
}
}