I have an interesting problem. I have a SQLite update that I am performing within a AsyncTask on Android (because I also have had to do a ton of remote calls before doing the DB call). The code works like a charm, unless the application is pushed to the background (eg, using the Home button). The task continues to work in the background successfully, the DB call is made and returns 1 row changed, but the data never actually makes it to the DB. No errors or exceptions. Even stranger, the logs show everything working just fine - no exceptions, nada.
Again, when NOT pushed to the background this works fine.
The call:
result = (sqlDB.update("FormInstance", values, "InstanceId=?", new String[] { String.valueOf(form.getSubmissionId()) }) > 0);
Also there is no transaction involved with this call (unless it is happening under the hood of the Android SQLite code).
Anyone know of why this might be the case? Is there something that happens to DB connections or SQLLite that I am unaware of when pushed to the backround?
UPDATE
I have tried wrapping the DB call with a begintransaction/endtrans without any success:
sqlDB.beginTransaction();
try {
result = (sqlDB.update("FormInstance", values, "InstanceId=?", new String[] { String.valueOf(form.getSubmissionId()) }) > 0);
}
finally {
sqlDB.endTransaction();
}
Still acts as though it was successful but data never committed.Please note that I pulled the DB from the device and verified that it had NOT been updated.
After much testing, I found that while there was a onPause method occasionally updating the data, the real problem was that the SQLLite update was not really updating one column (FormStatus) when the update was performed. This was only the case when running in the background. I verified this by querying the result immediately after the update. The final solution was a secondary update that only updated the FormStatus column, which did work. Wrapping with begintrans/endtrans did not help.
Related
My queries with .equalTo() return out-of-date data when used with addListenerForSingleValueEvent, while removing .equalTo() causes the listener to return updated data. Any idea why?
.
I'm using the following query to fetch user's posts from Realtime Database with persistance enabled on Android:
mDatabase.child("posts").orderByChild("uid").equalTo(id)
where id is the id of the current user and each post stores its author's id as a field.
When .equalTo(id) is present, the new posts for the particular user are not returned in that query for the first few minutes. Even more, it seems to affect other queries for the same root ("posts") that contain .orderByChild. Eg, following would also fail to recognise the new post:
mDatabase.child("posts").orderByChild("archived")
Once I remove the .equalTo(id) the behaviour goes back to normal. I'm using addListenerForSingleValueEvent. Tried it also withaddValueEventListener which fires two events, one without the new post, one with it. Without .equalTo(id) both single and non-single listeners return the new post in the first callback. Restarting the app doesn't seem to help straight away - the first event stays out-of-date for the next few minutes. The new post is successfully fetched by different queries in other parts of the application (eg mDatabase.child("posts").child(id))
Any idea why .equalTo() causes such behaviour and how to avoid it (other than using non-single listener and ignoring first event)?
Note 1: same thing happens for .startAt(id).endAt(id)
Note 2: other parts of the Realtime Database are functioning normally, device is connected to the internet and new posts are containing the valid uid field matching the current user.
Update 26/10/2016
Calling mDatabase.child("posts").startAt(key).limitToFirst(4) also produces similar behaviour when trying to query a segment of the database (in our case to implement infinite scroll). It seems that explicitly adding .orderByKey() fixes that particular problem: mDatabase.child("posts").orderByKey().startAt(key).limitToFirst(4).
Though the issue outlined in the original question remains.
I've ran into the exact same problem as you, and after experimenting with almost everything, I've managed to solve it on my end.
I'm scanning barcodes and fetching foods that have the scanned barcode:
Query query = refFoods.orderByChild("barcode").equalTo(barcode);
query.addListenerForSingleValueEvent(new Value ... })};
On my rules i had
".indexOn": "['barcode']"
and after i changed it and took the "[]" out as in:
".indexOn": "barcode"
it started working delay free, where before it would take something like 5 minutes to udpate.
I'm starting to approach this wonderful world of Realm. I'm very happy of the results I'm getting and now I have one question to submit.
In my android app I've got a Fragment that displays data retrieved from Realm. The query condition is that the time this data refers to is in between the beginning and the end of today.
RealmResults<Appointment> results = realm
.where(MyObject.class)
.between("begin", rangeBegin, rangeEnd)
.between("end", rangeBegin, rangeEnd)
.findAllSorted("begin", Sort.ASCENDING);
This query is executed in the onStart() method helping me to exploit the live-update feature, which indeed works very well.
I've also added listeners for changes in order to optimize UI updates.
Now the question is: how does this live-update behave if the time conditions change? (Imagine I keep the app opened for more than one day without touching it or simply I keep the app opened for minutes around midnight)
From what I've seen it seems to do the same query done the very first time onStart() was executed.
Is there a way to have also live-updating query or should I re-run that query somewhere else outside onStart()?
Thank you in advance
Now the question is: how does this live-update behave if the time conditions change? (Imagine I keep the app opened for more than one day without touching it or simply I keep the app opened for minutes around midnight)
The query is pretty constant after you've set it up, so you'd need to execute a new query with different parameters for rangeStart and rangeEnd, and replace your other results.
This query is executed in the onStart() method helping me to exploit the live-update feature, which indeed works very well. I've also added listeners for changes in order to optimize UI updates.
Personally I'd advise to put the query in onCreateView() instead, and the Realm lifecycle management to onCreateView() and onDestroyView().
Also, you can avoid manually assigning RealmChangeListeners for displaying lists if you use RealmRecyclerViewAdapter (adapters 1.3.0 works with realm 1.2.0).
If you use RealmRecyclerViewAdapter, then just call adapter.updateData(newResults); and it'll update the view as needed.
I have a test case for my app which fills in the TextViews in an Activity and then simulates clicking the Save button which commits the data to a database. I repeat this several times with different data, call Instrumentation.waitForIdleSync(), and then check that the data inserted is in fact in the database. I recently ran this test three times in a row without changing or recompiling my code. The result each time was different: one test run passed and the other two test runs reported different data items missing from the database. What could cause this kind of behavior? Is it possibly due to some race condition between competing threads? How do I debug this when the outcome differs each time I run it?
Looks like a race condition.
remember in the world of threading there is no way to ensure runtime order.
I'm not an android dev so I'm only speculating but UI is only on one event thread generally so when you call the method from another thread (your test) you're probably breaking that as you're outside of the event thread.
You could try using a semaphore or more likely a lock on the resource.
http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/locks/Lock.html
http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/Semaphore.html
I (finally!) found a solution to this problem. I now call finish() on the tested Activity to make sure that all of its connections to the database are closed. This seems to ensure consistency in the data when I run the assertions.
I would suggest making a probe for the database data rather than a straight assert on it. By this I mean make a piece of code that will keep checking the database for up to a certain amount of time for a condition rather than waiting for x seconds (or idle time) then check, I am not on a proper computer so the following is only pseudo code
public static void assertDatabaseHasData(String message, String dataExpected, long maxTimeToWaitFor){
long timeToWaitUntil = System.getCurrentTimeMillis() + maxTimeToWaitFor;
boolean expectationMatched = false;
do {
if(databaseCheck() == dataExpected){
expecttionMatched == true;
}
}while(!expectationMatched && System.getCurrentTimeMillis() < timeToWaituntil);
assertTrue(message, expectationMatched);
}
When i get to a computer i will try to relook into the above and make it better (I would actually of used hamcrest rather than asserts but that is personal preference)
The code at the moment reads something in the order of...
DoAnything() {
OpenTheDatabase()
// ... Do all the things! ...
}
However, the database object is never closed. This is worrisome.
The database is opened as follows:
var db = window.openDatabase( ... paramters ... );
No .closeDatabase function exists, or the documentation is incomplete. I thought the following might suffice:
db=null;
I see that sqlite3_close(sqlite3*) and int sqlite3_close_v2(sqlite3*) exist, but I'm unsure how to apply them in this case.
How do I close the database, and is it necessary?
Generally you only have one database connection that you open on app startup, and there is no need to close it while the app is open. It's a single threaded, single user app, so a lot of the normal rules about database connections don't apply.
When the app shuts down, you can rely on the browser to close everything - given the average quality of code on the web, browsers have to be pretty good at cleanup.
Setting db to null and letting the garbage collector do its thing will probably also work, but it is better not to create the extra objects in the first place.
This Android application on Google uses the following method to refresh the database after replacing the database file with a backup:
public void resetDbConnection() {
this.cleanup();
this.db =
SQLiteDatabase.openDatabase(
"/data/data/com.totsp.bookworm/databases/bookworm.db",
null, SQLiteDatabase.OPEN_READWRITE);
}
I did not build this app, and I am not sure what happens.
I am trying to make this idea work in my own application, but the data appears to be cached by the views, and the app continues to show data from the database that was replaced, even after I call cleanup() and reopen the database. I have to terminate and restart the activity in order to see the new data.
I tried to call invalidate on my TabHost view, which pretty much contains everything. I thought that the views would redraw and refresh their underlying data, but this did also not have the expected result.
I ended up restarting the activity programmatically, which works, but this seems to be a drastic measure. Is there a better way?
Agreed with Pentium10, at least conceptually.
The application is using a Cursor to show its data. A Cursor in Android is akin to a client-side cursor in ODBC, in that it is a cached copy of all of the data represented by the query's result set.
Now, the normal way of handling changes in the database contents is to call requery() on the Cursor. That will ripple its changes through the CursorAdapter to attached ListViews or other AdapterViews.
In your case, I am not completely certain that will work, since you are closing, replacing, and re-opening the database. That should not be done with an open Cursor on the data, AFAIK. So, in your case, you'd need to close the Cursor, do the database shuffle, then run the query again to get a fresh Cursor on your new database.