I have an SQLiteOpenHelper class that implements the singleton pattern and has three methods: addLogs(), getLogs(), and deleteLogs(). Seven threads (triggered by a BroadcastReceiver at certain schedules) access the database–six of them only use addLogs(), and the other one uses getLogs() and calls deleteLogs() before it finishes.
My problem is, because the last thread makes two different calls to the SQLite database, the other threads can call on addLogs() even before deleteLogs() is called, which corrupts my output. How can I make that one thread treat the two calls to the SQLiteOpenHelper as a single, atomic transaction?
What I’ve tried: Create a static ReentrantLock member in the SQLiteOpenHelper, and call lock() and unlock() from every one of the seven threads before and after their transactions. The other six threads still get through even though the last one still has the lock, which it calls before getLogs() and releases after deleteLogs().
UPDATE: This is the seventh thread that accesses the two methods.
public class Uploader extends Thread {
private Context context;
public Uploader(Context context) {
this.context = context;
}
@Override
public void run() {
Logger.d("STARTED: Uploader");
DB db = DB.getInstance(context);
List<JsonLog> logs = db.getLogs(); // FIRST CALL
String json = new Gson().toJson(logs); // convert to JSON
// connect to the server and upload
HttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost(This.URL);
post.setHeader("content-type", "application/json");
try {
Logger.d("to upload: " + json);
StringEntity se = new StringEntity(json);
post.setEntity(se);
StatusLine status = client.execute(post).getStatusLine();
// if upload is successful
if (status.getStatusCode() == HttpStatus.SC_OK) {
db.deleteLogs(); // SECOND CALL
} else {
Logger.d("Uploader failed. " + status.getStatusCode() + ": " + status.getReasonPhrase());
}
} catch (Exception e) {
e.printStackTrace();
}
Logger.d("FINISHED: Uploader");
}
}
SQLite has transactions as in
db.beginTransaction(),db.setTransactionSuccessful()to mark the Tx to be committed anddb.endTransaction()to finally mark the Tx as to be done.So you could in your
getLogs()do thebeginTransactionand in thedeleteLogsto mark it as done (and successful).[Update]
Now with your code snippet you probably would want to call
beginTransactionbefore your 1st call and in afinallyblock, that adds to thetry-catchblock call intoendTransaction. At the end of thetrypart if the status is ok, you would callsetTransactionSuccessful(after thedeletecall.