Sync SQLite database without causing multiple onChange - android

We store a copy of the server state of some items locally in our app. When we get new data from the server, items may have been changed, removed or inserted. When data is synced, all current data is fetched from the server.
Local list:
Item 1, progress 23
Item 2, progress 75
Item 3, progress 88
Remote list: (item 2 was removed)
Item 1, progress 55
Item 3, progress 88
Item 4, progress 1 (NEW)
The current solution clears the table and then bulk inserts all items like this:
// Remove old content (this is to prevent dead items being left)
mContentResolver.delete(URI_TO_TABLE, null, null);
// Insert all new items
// Most existing items are changed in a sync, hence we may just insert them again instead of updating
final ContentValues[] inserts = new ContentValues[newItems.size()];
for (int i = 0; i < newItems.size(); i++) {
inserts[i] = getChallengeContentValues(newItems.get(i));
}
mContentResolver.bulkInsert(URI_TO_TABLE, inserts);
The problem here is that we also use a ContentObserver, which sends out two onChange() each time (one for delete and one for bulkInsert), causing our UI to first update when the table is cleared, emptying our list of items, and directly after update with the freshly synced list, populating the view again. This is causing a lot of ugly blinking.
Is there anyway to just get one onChange()? The applyBatch() seems to generate one onChange() per operation. Can you somehow tell ContentProvider to threat a bunch of updates as only one?
OR is there another way of basically taking the new list (remote) and store it in the database?

As you mentioned applyBatch() is the right way to do this. Create ContentProviderOperation for each of your add/update/delete transactions, store them as an ArrayList<ContentProviderOperation> operations and run them as in a single applyBatch() operation.
http://developer.android.com/reference/android/content/ContentProvider.html#applyBatch%28java.util.ArrayList%3Candroid.content.ContentProviderOperation%3E%29
If your table is huge and you do not want to have the overhead of applyBatch() and need must use bulkInsert() then you could add some sort of hack like adding a extra in the delete query would would instruct the provider to not trigger notifyDataSetChanged()

Related

SQLite id row not following sequential order after deleting rows

I am trying to make a notes taking app and the way I wrote app to delete specific row is in this way -
FIRST - If a user taps on any cardView from a recyclerView, first activity will forwards cardView position using getAdapterPosition().
SECOND - Second activity receives cardView`s positon, to delete a specific row from SQLite database, I called a method known as 'totalNotes()' which will return total number of rows present in SQLite database. Finally, to delete the row I subtract the number received from 'totalNotes()' method with the getAdapterPosition().
PROBLEM - They way I have programmed it only works if the rows in the database are in sequential order. However, when a user wants to delete a row number 3 and 2 from 5 rows, the remaning rows are 1,4 and 5. How can make the database so auto implement sequential order after row deletion?
I have looked on 'almost' similar problem on the site but they fail when I try to implement in my code. I am new to Android development.
[Demo Picture][2]
How can I assign sequential IDs?
How my app looks like
You should not base on the row number sequential. Almost(if not all) all databases have the same behavior on auto-increment field. When you get data from database you need to get also the ID, and to keep the ID inside the APP so when deleting from DB you will delete base on that ID:
id = note.noteID; /// Selected note (noteID) to be deleted.
--------
dbHandler!!.deleteNote(id);
---------
// Here you bind your note to your viewHolder
fun bindItems(note: UserNotes) {
noteTitle.text = note.noteTitle
noteText.text = note.noteText
noteText.noteId = note.noteID;
}
Using sequential Ids is bad, because when you delete a record with Id = 5, the next record you add to your table is not going to be Id = 5 as well, it's going to be Id = 6, so it's going to cause you problems sooner or later.
The way #Simion suggested is:
You ask for all the data, (i.e. all your notes)
When you select a note you ask for the Id of that note, and save it in a variable.
If the user decides to delete that note you should do deleteNote(noteId), that way you can use anything you want as an Id and you don't rely on the Ids being sequential (which is, as I said before, usually a bad idea)

Robospice using caching in a dynamic ormlite database

I have an android app that loads data from a server and displays it in an endless scroll in a recyclerview.
It loads 5 items per page and if you scroll down, it triggers another 5 page to load.
Each time I scroll down, robospice is triggered and the 5 items will be cached using the current page number as the cacheKey into a SQLite database through ORMlite. The array of items returned by my server will then be added onto the total list of all items which I saved in an arraylist. This total list is then passed to my recyclerview adapter, which updates the recyclerview.
Therefore:
(cacheKey/page number:1) - first 5 items
(cacheKey/page number:2) - next 5 items
(cacheKey/page number:3) - next 5 items
Now I can also delete items on my android app. So therefore, if I don't like item 4 on page 2 for example, I can swipe left and delete it.
Since I don't keep track of the pages in my arraylist (it is a total list of everything), I can't really go:
spiceManager.getFromCache() - fetches the items from cache but since I don't keep track of the page in which the item was loaded from (remember, I load from the total arraylist), I do not have the cache key. I can't tell if the 9th item was loaded from the 2 page, for example, and therefore I don't have the cache key to fetch it from the cache.
spiceManager.removeDataFromCache() - delete the data and the associated cacheKey for those 5 items but save the 5 items temporarily into a arraylist.
spiceManager.putInCache() - I would delete the item in the temporary arraylist and then put the data from the arraylist back into cache with the same cacheKey for the 4 items as 1 is now removed
What I can do however is this:
spiceManager.removeAllDataFromCache() - remove all the data from the cache / SQLite DB.
spiceManager.putInCache() - put into cache all the data excluding the item that was previously deleted and issuing out a new cache key.
What I'm worried about with this method however, is that it is highly resource intensive. Each time you delete something, the whole DB is deleted and resaved without the deleted item.
I think that if I want to add a new item dynamically to my SQLite DB, this issue would also come about so I would also have to delete the whole DB and re-add it the whole arraylist into my SQLite and then re-issue a cacheKey.
Are there any other strategies out there for what I want to do when I want to make changes to my SQLite cache?
Thanks!

Android: SugarORM: Listing certain number of records instead of all

I am using SugarORM database. I am extracting records by calling
String action = "recordId DESC";
db_record_listing = Select.from(Records_rejected.class).orderBy(action).list();
However, such database would accumulate over time, and I would like to show the recent 100 records.
Question:
Is it possible to list out the 100 records that most recently added?
Thanks!
You can simply use LIMIT to restrict the number of items returned.
For SugarORM you can add another chain:
Select.from(...).orderBy(...).limit("100").list();

Insert, Update and Delete from a SQLite Database (Android)

I already have a SQLite Database setup which I am using as cache for the Android application. The application does a HTTP Request and gets back a List of objects which I can insert into the db. After the first request, if I do anymore requests, how do all of the following in a better way:
1) insert all new objects from the list
2) update all objects that were already in the db
3) delete all rows that were not there in the latest list of objects.
I know that options 1 and 2 can be done using the "INSERT OR UPDATE" query. How can I manage the 3rd option efficiently?
Right now my approach is to delete all from table and then insert all. But that isn't very efficient. Any ideas how to improve it?
For that you can use the ids of the rows. For doing that first retrieve all the rows which you want to delete using SELECT query and add it a temporary arraylist, then use for loop over the arraylist to delete all those rows by using DELETE query.
You should do your operations using the applyBatch() method of the ContentProvider (http://developer.android.com/reference/android/content/ContentProvider.html#applyBatch(java.util.ArrayList)).
You can perform this method in a separate thread asynchronously so that you do not block anything else. You will have to create a list of ContentProviderOperations. In fact, you only need to specify the ones you need to insert or update within the ArrayList and implement the applyBatch() method such that it will automatically delete the rest of the entries in the database.
To answer your question about how to delete the entries not in the table, the logical assumption would be to search through your data sequentially and then delete the ones that do not need to exist.
I guess the intention is to refresh the Http request result set saved in the database. So I think the most efficient way is do a transaction or batch operation to delete all rows from the table first and then insert the new rows. A transaction might be better so that the result rows are either all new or all old, but not mixed.

Android SQLite - Update table only if different

I currently successfully use a SQLite database which is populated with data from the web. I create an array of values and add these as a row to the database.
Currently to update the database, on starting the activity I clear the database and repopulate it using the data from the web.
Is there an easy method to do one of the following?
A: Only update a row in the table if data has changed (I'm not sure how I could do this unless there was a consistent primary key - what would happen is a new row would be added with the changed data, however there would be no way to know which of the old rows to remove)
B: get all of the rows of data from the web, then empty and fill the database in one go rather than after getting each row
I hope this makes sense. I can provide my code but I don't think it's especially useful for this example.
Context:
On starting the activity, the database is scanned to retrieve values for a different task. However, this takes longer than it needs to because the database is emptied and refilled slowly. Therefore the task can only complete when the database is fully repopulated.
In an ideal world, the database would be scanned and values used for the task, and that database would only be replaced when the complete set of updated data is available.
Your main concern with approach (b) - clearing out all data and slowly repopulating - seems to be that any query between the empty and completion of the refill would need to be refused.
You could simply put the empty/repopulate process in a transaction. Thereby the database will always have data to offer for reading.
Alternatively, if that's not a viable solution, how about appending newer results to the existing ones, but inserted as with an 'active' key set to 0. Then, once the process of adding entries is complete, use a transaction to find and remove currently active entries, and (in the same transaction) update the inactive entries to active.

Categories

Resources