Querying Realm, is the query read-consistent like Oracle? - android

I insert records from a ContentObserver in an Android Service like:
try {
realm.beginTransaction();
realm.copyToRealm(sms);
realm.commitTransaction();
} catch (RealmException re) {
realm.cancelTransaction();
}
Then in another Service I run a JobService with the following query:
Realm realm = Realm.getDefaultInstance();
RealmQuery<Sms> query = realm.where(Sms.class);
RealmResults<Sms> sms = query.findAll();
ArrayList<Sms> smsArrayList = new ArrayList<>();
smsArrayList.addAll(sms);
for (Sms sms : smsArrayList) {
process row
delete row
}
Is the query read-consistent in point in time, like Oracle?
What happens if the query runs for some time and new data is inserted from the other Service?
I like my query to be read-consistent. The new records inserted after the query starts should not be seen or be included. Is this the case or how should I code it?
I need to be sure I process the records in the job queue properly and I want to avoid read the same job twice or mix up due to the other Service inserting.
So I'd like to do a for loop over RealmResults<< Sms>>, process it, delete the row.
Then at some point in time start the query again and process the new records.

On looper threads, Realms (and RealmResults and RealmObjects) are updated automatically. You can disable this by calling setAutoRefresh (https://realm.io/docs/java/latest/api/io/realm/Realm.html#setAutoRefresh-boolean-). Alternatively you can start a write transaction.
So you could do:
Realm realm = Realm.getDefaultInstance();
realm.setAutoRefresh(false);
// do all your reads here ...
realm.waitForChange(); // to update the instance if other threads have commit changes
or alternatively:
Realm realm = Realm.getDefaultInstance();
realm.writeTransaction(); // will wait if another write transaction is in progress
// do all your reads here ...
realm.cancelTransaction(); // no writes :-)

Related

Get notified when data changed in database

I'd like to know how can I be notified when data change in my database.
Is there a solution to this or not?
The fact is I know how to push and get data in it but I don't know how to be notified when a modification happens
If the responsibility changing the data in your database is your app, you should create a database layer and then create some callback to notify your app your data has an Insert, update or delete operation.
If you use sqlite like local database:
First approach, using the android SDK content provider, the content observer class. Create a content observer that monitors some table in your local sqlite.
http://www.grokkingandroid.com/use-contentobserver-to-listen-to-changes/
If you use a ORM, there are callbacks in the ORM to notify changes. For example GreenDao using AsyncSession:
AsyncSession asyncSession = App.getInstance().daoSession.startAsyncSession();
asyncSession.setListener( new AsyncOperationListener() {
#Override
public void onAsyncOperationCompleted(AsyncOperation operation) {
// do whats needed
}
});
asyncSession.insert(MyObject);
If you use Realm.IO, there is a callback from the realm transaction to notify is an update is done.
Realm realm = Realm.getDefaultInstance();
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
MyObject myobject = new MyObject();
myobject.setId(UUID.randomUUID().toString());
myobject.setName("My object name test");
realm.copyToRealm(myobject);
}
}, new Realm.Transaction.Callback() {
#Override
public void onSuccess() {
//realm already update
Log.e("lh", "this callback i can use to notify. after save " + realm.allObjects(MyObject.class).size());
realm.close();
}
});
Changues in the backend.
If the changes are in the backend. A possible solution is send a push notification and start to do a background service in the app.
In this URL you have a PHP script to do a push notification to google cloud.
https://gist.github.com/prime31/5675017
You must attach the google cloud push notification receiver in you android app and create a specific service that will do an HTTP request to download the new data if you expose your database with a REST endpoint.
the most simple way is to keep a row in database and update its value when data is changed.

Update mutiple rows in table using Realm in Android

I am using Realm to store my values in local database.
My requirement is that i need to change one field status=1 based on some condition.
I have tried following method to accomplish this task. And it is working fine.
RealmResults<NotificationOrder> notificationOrders=realm
.where(NotificationOrder.class)
.equalTo(RealmConstants.TBL_NOTIFICATION_ORDER.property_id,ConstantMethod.getPreference(getActivity(),UserDefault.kPropertyId))
.equalTo(RealmConstants.TBL_NOTIFICATION_ORDER.status,0)
.findAll();
for (NotificationOrder order:notificationOrders) {
realm.beginTransaction();
order.setStatus(1);
realm.commitTransaction();
}
Now there may be 1000 of such rows in my local db and using for loop to update single row doesn't seem proper way.
So my question :Is there any way like MYSQL Update Queries in Realm by which we can update all rows having status=0 by single statement instead of updating single row one by one ?
Thanks.
If I know right, the objects in the transaction ought to be managed, so
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
RealmResults<NotificationOrder> notificationOrders = realm
.where(NotificationOrder.class)
.equalTo(RealmConstants.TBL_NOTIFICATION_ORDER.property_id,ConstantMethod.getPreference(getActivity(),UserDefault.kPropertyId))
.equalTo(RealmConstants.TBL_NOTIFICATION_ORDER.status,0)
.findAll();
for(NotificationOrder order : notificationOrders) {
order.setStatus(1);
}
}
});
Should be sufficient.
You can do, bulk update by setValue method like this:
realm.where(NotificationOrder.class)
.equalTo(RealmConstants.TBL_NOTIFICATION_ORDER.property_id,ConstantMethod.getPreference(getActivity(),UserDefault.kPropertyId))
.equalTo(RealmConstants.TBL_NOTIFICATION_ORDER.status,0)
.findAll()
.setValue(RealmConstants.REALM_FIELD_NAME,value); // 1st parameter is field name, 2nd is value

Accessing RealmDB from multiple Threads

I would like to know if there is any recommended practice of using RealmDB across multiple threads.
My scenario: I am looping through the records in RealmDB using one thread and doing some action. Based on the response from the previous action I would like to remove the records from another thread.
What would be best way to achieve this?
You can pass RealmObject field values (e.g. id, primaryKey) across the threads, which means that when you are done with your "action1" on the other thread, you can transfer the id(s) to the thread that is responsible for handling Realm operations, query the Object(s) which needs to be removed and delete them from Realm, you can executeTransactionAsync to further takeaway delete operation(s) from the thread where Realm is operating.
EDIT
In Realm Write operations don't block Read operations.
RealmResults & RealmObjects are LIVE objects until we close Realm instance
If you use Read operations as Observable, all further modification will be notified, If you don't want to use Observable you can also use addChange listener.
Lets have a look at some code:
Lets say in one of your class you have a Realm instance, and you are doing read operation on ThreadA (mainThread in this example)
realm.where(GitHubUser.class).findAll().asObservable()
.filter(RealmResults::isLoaded)
.filter(RealmResults::isValid)
.subscribeOn(AndroidSchedulers.mainThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(gitHubUsers -> {
for (GitHubUser gitHubUser : gitHubUsers) {
Log.e("TAG", "data = " + gitHubUser.getLogin());
}
});
And a corresponding addChangeListener version
RealmResults realmResults = realm.where(GitHubUser.class).findAll();
realmResults.addChangeListener(new RealmChangeListener<RealmResults>() {
#Override
public void onChange(RealmResults element) {
for (GitHubUser gitHubUser : gitHubUsers) {
Log.e("TAG", "data = " + gitHubUser.getLogin());
}
}
});
for (GitHubUser gitHubUser : gitHubUsers) {
Log.e("TAG", "data = " + gitHubUser.getLogin());
}
and let's say you get the trigger and want to delete one of the entry on a separate thread, what you should do is, get a new Realm instance, delete the entry as shown below and close the Realm instance.
This way you will not face any thread issue and your Read query gets a notification after you delete the entry and you can update your view with updated data.
new Thread(() -> {
Realm realm1 = Realm.getDefaultInstance();
GitHubUser gitHubUser = realm1.where(GitHubUser.class)
.equalTo("login", "loginString")
.findFirst();
if (gitHubUser != null) {
realm1.executeTransaction(realm2 -> gitHubUser.deleteFromRealm());
}
realm1.close();
}).run();

FindFirst returns null

I'm using Realm to provide the database for my application. But...
After login, the server returns the data and I create the account (of AccountManager) and save these datas at the database of the application, like this (at an AsyncTask, of course):
UserRealm userRealm = new UserRealm();
//setter of the userRealm...
Realm realm = Realm.getInstance(context);
realm.beginTransaction();
realm.copyToRealmOrUpdate(userRealm);
realm.commitTransaction();
realm.close();
After, I close the LoginActivity and at the onResume of the MainActivity, I try to load the user, like this(at an AsyncTask, again...):
public static UserRealm getUser(Context context) {
try {
return Realm.getInstance(context).where(UserRealm.class).findFirst();
} catch (Exception e) {
if(DebugUtil.DEBUG) { //enabled
e.printStackTrace();
}
}
return null;
}
But this returns null, I don't know what happens with it.
UserRealm: https://gist.github.com/ppamorim/88f2553a6ff990876bc6
AsyncTask is in a threadpool, and considering you open Realm instances that you never close with your getUser() call, your Realm version becomes locked at the version when you first called getUser().
return Realm.getInstance(context).where(UserRealm.class).findFirst(); // never closed
So even though you commit a transaction on another thread in the threadpool, not all threads will be up to date (because you locked them on an old version by opening Realm instances that are never closed), and sometimes the object will be null.
Solution, close all Realm instances on background threads (or force an update if that's not enough for some reason).

HowTo: Provide notification to Android app whenever SQLite table updates

I am in process of developing an Android tablet app using sqllite 3.7.4 which would
perform following:
Fetches information from the UI
Performs some logic and store related information to the sqlite database
The stored information has to be send immediately OR at schedule
interval (ex. at 5:00 on xyz date) over the network
Currently, we have developed a dispacher mechanism (thread ), which constantly polls the database for new information inserted in the database. The thread fetches the information and send to the network module.
But, I feel this is not the correct approach as
Polling every time is a overhead. There can be times when there is nothing to execute
It is not real time , because we poll after every 5 seconds
So
Is there a way to send a trigger to my network module as soon as information is updated in database?
Or any better way to achieve this task?
Thanks in advance.
This question is about one year ago, but i think this is a common problem. This is how i handled the Database changes:
In my Adapter ( SQL ADAPTER) i have methods for updating / deleting or inserting data into the Database obviously. Like this method:
public long addProduct(String code, String name ... String gid, String gdate) {
ContentValues initialValues = new ContentValues();
initialValues.put(KEY_CODE, code);
initialValues.put(KEY_NAME, name);
...
initialValues.put(KEY_CHECK, this.checkfalse);
initialValues.put(KEY_GID, gid);
---------
Intent i = new Intent("data_inserted");
i.putExtra("date", date);
sendBroadcast(i);
---------
return mDb.insert(SQLITE_TABLE, null, initialValues);
}
After the change happened it will send an broadcast intent. To fetch this first register your Broadcast Receiver in your onCreate Method (uploaderClass or whatever). This will look like this:
registerReceiver(Updated, new IntentFilter("data_inserted"));
And this Method to handle the following actions!
private final BroadcastReceiver Updated= new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
doSomething(); // Data was inserted upload it
}
};
EDIT :
To fetch only new items from database, I sign new products. I have a colum "info" which can contain Strings like "upload", "update" or "delete". Then i fetch all Items from the database which contain these special strings and upload them. After that i set them to null or an empty String. Whatever you wish. Hope i explained it not to complicated :)
You can create your own database listener whenever something is updated to the database it will fetch the information and send to the network. I think will clear some idea for implementing this thing.
Now we can use Room to achieve this.
database.getInvalidationTracker().addObserver(new InvalidationTracker.Observer(tableToObserve) {
#Override
public void onInvalidated(#NonNull Set<String> tables) {
}
});
database is an instance of RoomDatabase, which is the base class for all Room databases. All classes that are annotated with "#Database" (which is the way to use the Room Libary) must extend this class.
In the creator method of InvalidationTracker.Observer, you can pass in an array of String (or in the form of varargs) to indicate the tables you'd like to observe, if any update happen to those tables, the callback method onInvalidated is invoked.
Some links to refer to:
https://developer.android.com/reference/android/arch/persistence/room/InvalidationTracker.html#addObserver(android.arch.persistence.room.InvalidationTracker.Observer)
hope so this will help you
private Handler h;
// in create time
h = new Handler();
// call where you want
h.postDelayed(myRunnable2, 1000); // after 1000 millisecond this function call automatically
// this function
private Runnable myRunnable2 = new Runnable() {
public void run() {
// do some thing
h.postDelayed(myRunnable2, 1000);
}
};

Categories

Resources