Recommended design pattern for writing to SQLite database in Android - android

Folks,
I'm looking for a design pattern that enables a UI thread to interact with a client-side SQLite database that may have bulk inserts (taking 10s of seconds), quick inserts, and reads, and doesn't block the UI thread.
I would like advice on whether or not I am using the optimal design pattern for this, as I have been recently debugging deadlock and synchronization issues and I am not 100% confident in my final product.
All DB access is now bottlenecked through a singleton class. Here is pseudocode showing how I am approaching writes in my singleton, DataManager:
public class DataManager {
private SQLiteDatabase mDb;
private ArrayList<Message> mCachedMessages;
public ArrayList<Message> readMessages() {
return mCachedMessages;
}
public void writeMessage(Message m) {
new WriteMessageAsyncTask().execute(m);
}
protected synchronized void dbWriteMessage(Message m) {
this.mDb.replace(MESSAGE_TABLE_NAME, null, m.toContentValues());
}
protected ArrayList<Message> dbReadMessages() {
// SQLite query for messages
}
private class WriteMessageAsyncTask extends AsyncTask<Message, Void, ArrayList<Messages>> {
protected Void doInBackground(Message... args) {
DataManager.this.mDb.execSQL("BEGIN TRANSACTION;");
DataManager.this.dbWriteMessage(args[0]);
// More possibly expensive DB writes
DataManager.this.mDb.execSQL("COMMIT TRANSACTION;");
ArrayList<Messages> newMessages = DataManager.this.dbReadMessages();
return newMessages;
}
protected void onPostExecute(ArrayList<Message> newMessages) {
DataManager.this.mCachedMessages = newMessages;
}
}
}
Highlights:
First: all public write operations (writeMessage) happen via an AsyncTask, never on the main
thread
Next: all write operations are synchronized and wrapped in
BEGIN TRANSACTIONS
Next: read operations are
non-synchronized, since they need not block during writes
Finally: the results of read operations are cached on the main
thread in the onPostExecute
Does this represent the Android best practice for writing potentially large volumes of data to a SQLite database while minimizing impact to the UI thread? Are there any obvious synchronization issues with the pseudocode you see above?
Update
There is a significant bug in my code above, and it is as follows:
DataManager.this.mDb.execSQL("BEGIN TRANSACTION;");
That line acquires a lock on the database. However, it is a DEFERRED lock, so until a write happens, other clients can both read and write.
DataManager.this.dbWriteMessage(args[0]);
That line actually modifies the database. At this point, the lock is a RESERVED lock, so no other clients may write.
Note there are more possibly expensive DB writes after the first dbWriteMessage call. Assume that each write operation happens in a protected synchronized method. That means that a lock is acquire on DataManager, the write happens, and the lock is released. If WriteAsyncMessageTask is the only writer, this is fine.
Now let's assume that there is some other task that also does write operations, but does not use a transaction (because it's a quick write). Here's what it might look like:
private class WriteSingleMessageAsyncTask extends AsyncTask<Message, Void, Message> {
protected Message doInBackground(Message... args) {
DataManager.this.dbWriteMessage(args[0]);
return args[0];
}
protected void onPostExecute(Message newMessages) {
if (DataManager.this.mCachedMessages != null)
DataManager.this.mCachedMessages.add(newMessages);
}
}
In this case, if WriteSingleMessageAsyncTask is executing at the same time as WriteMessageAsyncTask, and WriteMessageAsyncTask has executed at least one write already, it is possible for WriteSingleMessageAsyncTask to call dbWriteMessage, acquire the lock on DataManager, but then be blocked from completing its write due to the RESERVED lock. WriteMessageAsyncTask is acquiring and giving up the lock on DataManager repeatedly, which is a problem.
The takeaway: combining transactions and singleton object-level locking could lead to deadlock. Make sure you have the object-level lock prior to beginning a transaction.
The fix to my original WriteMessageAsyncTask class:
synchronized(DataManager.this) {
DataManager.this.mDb.execSQL("BEGIN TRANSACTION;");
DataManager.this.dbWriteMessage(args[0]);
// More possibly expensive DB writes
DataManager.this.mDb.execSQL("COMMIT TRANSACTION;");
}
Update 2
Check out this video from Google I/O 2012:
http://youtu.be/gbQb1PVjfqM?t=19m13s
It suggests a design pattern making use of the built-in exclusive transactions and then using yieldIfContendedSafely

I can't really say much about the synchronization/deadlock part, that would be hugely dependent on the rest of your code. Since DataManager class doesn't really interact with the UI, you might want to use a service (IntentService) rather than an AsyncTask. You can show notifications when you are done syncing. You don't really need onPostExecute() if you are not calling UI code.

You may want to consider this info from the SDK (http://developer.android.com/reference/android/os/AsyncTask.html)
When first introduced, AsyncTasks were executed serially on a single
background thread. Starting with DONUT, this was changed to a pool of
threads allowing multiple tasks to operate in parallel. Starting with
HONEYCOMB, tasks are executed on a single thread to avoid common
application errors caused by parallel execution.
If you truly want parallel execution, you can invoke
executeOnExecutor(java.util.concurrent.Executor, Object[]) with
THREAD_POOL_EXECUTOR.

FYI, every SQL statement ran on SQLite is ran under a transaction even if you don’t specify one.
Check below threads if you are doing Bulk Insert in SQLite:
Android Database Transaction
SQLite Bulk Insert

Related

Query efficiency with Parse using local store in Android

My question is very simple, what is the best approach to work with Parse using the local store at the time I want to query the saved objects.
Is it better to trigger several queries to the local store directly on the main thread and avoid nesting a lot of anonymous classes or using a background thread?
It's important thing to notice is that this method is going to be called very frequently and the pattern will be repeated in several places with different queries. I'm evaluating both efficiency and code quality in readability. These methods will be called synchronously so we can assume the data will be consistent at any time.
As the objects are being saved locally I would expect the queries to be very fast in response. Here's a rough sample of how the code would look like in both cases.
Option one:
public void processBatches() {
ParseQuery<Batch> batchQuery = Batch.getQuery();
int batchCount = batchQuery.fromLocalDatastore().count();
List<Batch> batches = batchQuery.fromLocalDatastore().find();
for(Batch b : batches) {
// do whatever I need to do
}
}
Option two:
public void processBatches() {
ParseQuery<Batch> batchQuery = Batch.getQuery();
int batchCount = batchQuery.fromLocalDatastore().countInBackground(new CountCallback() {
#Override
public void done(int i, ParseException e) {
if (i > 0) {
batchQuery.findInBackground(new FindCallback<Batch>() {
#Override
public void done(List<Batch> list, ParseException e) {
for (Batch batch : list) {
// do whatever I need to do
}
}
});
}
}
});
}
Well since in option one you are blocking the UI thread, there could be a delay in the user's ability to interact with your application. This is not a very good option since even if it is for just a moment, users don't want to be waiting unless they know operations are happening. But, if you know that at any time there will be little to no delay, go ahead and do it.
Nevertheless, I argue that option two is going to be the best option. This is because, in general, all network operations should be performed in the background. Although in your case you are performing local datastore queries, suppose a user has gone to their application task manager and cleared the data (very rare this will happen) what happens now when you perform the find from local data store and processing of Batch objects? Well, the app crashes. Again, this is not a very good option for the usability for your application.
Choose the second option, and allow an AsyncThread to run the find() and count() query operations to the network if there is nothing found from local data store queries. Also, from the Parse documentation for find:
public Task<List<T>> findInBackground()
Retrieves a list of ParseObjects that satisfy this query from the source in a background thread.
This is preferable to using ParseQuery.find(), unless your code is already running in a background thread.
Returns:
A Task that will be resolved when the find has completed.
Parse's creators prefers that the users of their API use a background thread to perform operations.
It really depends.
Is the user triggering the update? If so then do it on the main thread because you don't want them waiting
If not, then is the data access a result of fetching data from the web (and hence you should already be on a background thread) so could probably just remain on the background thread
Also what happens in "// do whatever I need to do"? Is it an update to the UI or more background processing?

Can you encapsulate multiple nested transactions across different threads into an overall transaction with greenDAO?

I am working on an Android application that uses greenDAO as a data persistence layer. The application downloads data from various different sources across multiple threads (determined by a thread pool), each piece of data is inserted into the database in a transaction using insertOrReplaceInTx. This is working fine.
My question is whether it is technically possible, using greenDAO, to encapsulate these different transactions (which occur on different threads) into an overall transaction, using nested transactions. I know in theory it is possible to do this if all the transactions were taking place on a single thread, however I am unsure if this possible with the insertOrReplaceInTx calls occurring on different threads.
The reason I wish to encapsulate these into a single overall transaction is because they represent a synchronisation process within an app. In the event of any single part of the import failing, I wish to abort and rollback all of the modifications within the overall transaction.
If I begin a transaction with db.beginTransaction on the main thread where I initiate the import process, this creates a deadlock when another thread tries to insertOrReplaceInTxt.
Is the correct way to counter this to ensure that all greenDAO transactions are taking place on the same thread?
Afaik, you cannot because each thread manages its own connection.
If you have such dependency between these operations, you probably want to sync them anyways.
e.g. what if Job A finishes way before Job B and Job B's db connection fails. Your data will go out of sync again. You still need some logic for the other job.
Also, writers are mutually exclusive.
I would suggest creating a utility class that can run a list of runnables in a transaction. Each job, when finished, enqueues a Runnable to this utility. These runnables include the actual database commands.
When the last one arrives (this depends on your dependency logic), the utility will run all runnables in a transaction.
A sample implementation may look like this: (I used a simple counter but you may need a more complex logic)
class DbBundle {
AtomicInteger mLatch;
List<Runnable> mRunnables = new ArrayList();
DbBundle(int numberOfTx) {
mLatch = new AtomicInteger(numberOfTx);
}
void cancel() {
mLatch.set(-1); // so decrement can never reach 0 in submit
}
boolean isCanceled() {
mLatch.count() < 0;
}
void submit(Runnable runnable) {
mRunnables.add(runnable);
if (mLatch.decrementAndGet() == 0) {
db.beginTransaction();
try {
for (Runnable r : mRunnables) r.run();
db.setTransactionSuccessful()
} finally {
db.endTransaction();
}
}
}
}
When you create each job, you pass this shared DbBundle and the last one will execute them all.
So a job would look like:
....
try {
if (!dbBundle.isCanceled()) { // avoid extra request if it is already canceled
final List<User> users = webservice.getUsers();
dbBundle.submit(new Runnable() {
void onRun() {
saveUsers(users);//which calls db. no transaction code.
});
});
} catch(Throwable t) {
dbBundle.cancel();
}

Should I mark LruCache as volatile?

I recall reading somewhere that android guarantees that LruCache provides latest info for all threads, and that one thread's operation will complete before the same thread sees an edit on the cache from another thread. I am using LruCache to store bitmaps obtained from my app's server, and using a pool of threads to obtain bitmaps from the network.
Now I cannot find the reference to this in the Android docs or any other mention. Do I need to mark LruCache instances as volatile or set synchronize(LruCache) around cache operations?
mibollma is not wrong in his response regarding Android LruCache Thread Safety. People often mistake thread safety and atomicity.
If a class is thread safe, it means that, when for instance two threads call an operation on it, the internals do not break. Vector is such a class with every operation being synchronized. If two different threads call Vector.add, they will both synchronize on the instance and the state is not broken. For instance something like this:
synchronized void add(final T obj) {
objects[index++] = obj;
}
This thread-safe in the sense that no two threads will add an element at the same position. If it would not be synchronized they could both read index = 0 and try to write at that position.
Now why do you still need to synchronize? Imagine you have a case like this:
if(!collection.contains(element)) {
collection.add(element);
}
In that case your operation is not atomic. You synchronize once, when you ask if the element is already present and a second time when you add that element. But there is a window in between those two calls when another thread could make progress and your assumption of the collection not containing the element is broken.
In pseudo code:
if(!coll.contains(element)) { // << you have the exclusive lock here
//Thread 2 calls coll.add(element) << you do not have the lock anymore
coll.add(element); // << doomed!
}
So this is why the answer is correct in the sense that you should synchronize around non-atomic operations like
synchronized(coll) {
if(!coll.contains(element)) { // << you have the exclusive lock here
// Thread 2 wants to call << still holding the lock
// coll.add(element) but
// cannot because you hold the lock
coll.add(element); // << unicorns!
}
}
Because synchronization is pretty expensive the concurrent collections come with atomic operations like putIfAbsent.
Now back to your original question: should you make the LruCache volatile? In general you do not mark the LruCache itself volatile but the reference to it. If such a reference is shared across threads and you plan to update that field then yes.
If a field is not marked volatile a thread might not see the updated value. But again: this is just the reference to the LruCache itself and has nothing to do directly with its contents.
In your specific scenario I would rather have the reference final instead of volatile since no thread should set the reference to null anyways.
The question if you need to put synchronized around your cache operations depends on the case. If you want to create a single atomic operation, like putIfAbsent, then yes.
public void putIfAbsent(final K key, final V value) {
synchronized(lruCache) {
if(!lruCache.containsKey(key)) {
lruCache.put(key, value);
}
}
}
But later in your code, when you call just lruCache.get(key) there is no need to wrap that into a synchronized block itself. Only when you plan to create an atomic operation that should not interfere with another thread.

AsyncTask Implementation

In my application, i need to reduce the time of inserting videos and other details from the database. I'm trying to use the AsyncTask concept in my application. But it gives an error in my insertion part when i do it in doInBackground().Another class does the insertion.
abc.insert(arguments);
where abc is the object of my database class.
Error shown is nullpointerexception in my class where insertion is done.
Is there any solution for this?
thanks,
niki
I don't fully get your case, but it sounds like your are a victim of synchronization issue - several Threads are calling your insertion code.
If this is the case, then a simle solutions is to use Java synchronized statement around your insertion code, e.g.:
public synchronized void insert() { /* code to insert */ }
or
private Object lock = new Object(); // a field of your DBHelper
// somewhere below in your DBHelper
synchronized (lock) {
/* code to insert */
}
If you are simply looking to add data to your database in a worker thread with no UI update post addition then use Java Threads insted of AsynTask.
The sole objective of AsyncTask is to perform the expensive operations in background and update UI after the operation has been performed.
As suggested by Arhimed the database locking can be an issue in your case if you have fired multiple AsyncTasks.

Best method for saving data - preferences, sqlite, serializable or other?

I've been investigating alternative methods for saving my game's data between turns, and wonder if anyone can point me in the right direction.
I have approximately 32k of data which must be saved during onPause. I ruled out preferences due to the sheer quantity of data. I spent a few days playing around with SQLite but couldn't get the data to save in less than two seconds (although the time certainly hasn't been wasted).
I've decided that I'll use the database for loading constant data at the beginning of the game. This will certainly make it easier to tweak various parameters and default values in the game. But this still leaves me looking for the ideal method for writing data.
The data that needs to be saved is basically nine occurrences of class A and nine occurrences of class B. I'm an intensive month into the learning curve of Android (and the nuances of Java, coming from a C++ background) and have been googling like crazy. This brought two possibilities to mind -
1) Serialization (ObjectOutputStream)
I thought this would be the perfect solution but, having read several other posts regarding the subject, gather that it isn't highly recommended on the Android platform due to speed and memory allocations provoking the garbage collector into a potential rage.
2) DataOutputStream class
My current thought is to add Load and Save functions to both classes and to use DataOutputStream and DataInputStream calls in them to write and read the data respectively.
The data in the classes are primitives (strings and ints mostly) and arrays of primitives, so there's nothing too complicated in there to break down. Would this second solution seem a good, viable one? Or are there other solutions that I am unaware of as yet?
You should use an Async task to save the data, I used this method to fetch highscores at the start a game:
new HighscoreTask().execute(this);
the Async task looks like this:
public class HighscoreTask extends AsyncTask<MainView, Void, Void> {
protected void onPreExecute() {
}
protected void onPostExecute(final Void unused) {
}
#Override
protected Void doInBackground(MainView... params) {
HighScoreFactory.syncScores();
return null;
}
}
All the database interaction happens in HighScoreFactory.syncScores() this can take as long as it needs because it happens in the background. In my case it sends an HTTP request to an external server and loads these into a database. It's never caused any problems and works seamlessly.
Why do you have a 2 second limit on your database write? If it is purely for the sake of UI responsiveness, then there is another approach you can take.
You don't actually have to perform the save within your onPause method itself, you could just kick off a new Thread that actually does the save for you.
private void backgroundSave(){
Thread backgroundThread = new Thread() {
#Override
public void run() {
//do save here
}
};
backgroundThread.start();
}
#Override
protected void onPause() {
super.onPause();
backgroundSave();
}
You could alternatively use an AsyncTask for this.
You might have to consider the case when a user attempts to restart your app before the save is complete, but that shouldn't be too hard to take into account.
Have you tried insert data to the database in transaction?
try{
db.beginTransaction();
//here insert data to database
db.setTransactionSuccessful();
} finally {
db.endTranscation();
}
That can speed up operation.
Create a new Thread that does the data writing using Context.openFileOutput(String name, int mode) with this as the context. You can then write it in the background with the new thread and retrieve it with: Context.openFileInput(String name) again with this as the context. Hopefully this helps.

Categories

Resources