I'm migrating the following pattern of accessing the Android app's SQLite database into the RxJava world:
public List<Stuff> doStuff(){
synchronized (lock) {
open(); // this effectively checks for isOpen() then calls getWritableDatabase()
// query the database for stuff
close(); // SQLiteOpenHelper close method
return stuffList;
}
}
Something I'm struggling is when should I close the database connection? I know there are patterns for not closing the connection at all as well as closing the connection as part of the Activity method. However, those patterns would require me applying the logic to the whole database manager class which I'd like to avoid if possible. Was hoping maybe there's a suggested way to handle this with RxJava and specifically SqlBright wrapper? My migrated code looks something like this:
public Observable<List<Stuff>> doStuff(){
synchronized (lock) {
open();
String sql = <..>;
return db.createQuery(tableName, sql, args).mapToList(mStuffMapper);
// where do I close()?
}
}
The solution I'm after, ideally, should allow me to change this one method, keeping the rest with the current open/close pattern.
You can use Subscription to close the connection.
db.createQuery(tableName, sql, args)
.mapToList(mStuffMapper);
.doOnSubscribe(new Action0() {
#Override public void call() {
close();
}
});
Subscription subscribe = doStuff().subscribe();
subscribe.unsubscribe();
Related
Iam trying to figure out what RunInTransaction eventually does. Room documentation doesnt say much other than "Executes the specified Runnable in a database transaction".
From what i understand:
If we have an asynchronous operation like a query and then some insertion without the runInTransaction
roomDB.runInTransaction(new Runnable() {
#Override
public void run() {
query
}
});
insertions
insertions
The runInTransaction locks the database until the specified operation is completed. So at the first insertion the Thread pauses (please correct me) until the runInTransaction completes.
How can i control which procedure executes first?
But i think that database either way locks the tables and without using runInTransaction method. Please correct me if iam wrong.
Updated
#Dao
public interface RepoDao {
#Query("SELECT * FROM Table")
LiveData<List<Table>> getAll();
#Insert(onConflict = OnConflictStrategy.REPLACE)
void insert(List<Table> table);
}
Main Activity
repo = ((BasicApp)getApplication()).getRepository();
repo.insertDataFromNetwork();
((BasicApp)getApplication()).getDatabase().repoMethods()
.getAll().observe(this, new Observer<List<Table>>() {
#Override
public void onChanged(#Nullable List<Table> message) {
Log.d("hello");
}
});;
insertDataFromNetwork
mDatabase.runInTransaction(new Runnable() {
#Override
public void run() {
mDatabase.repoMethods().insert(....);
mDatabase.repoMethods().insert(....);
mDatabase.repoMethods().insert(....);
mDatabase.repoMethods().insert(....);
mDatabase.repoMethods().insert(....);
}
}
});
A transaction symbolizes a unit of work performed within a database management system against a database. (Wikipedia)
It means that, if you, for example, will insert 10 users and update 10 other users not using runInTransaction method, Room will execute each insertion and update as a single action (transaction), and will update observers, who are observing changes on the Users table each time. While doing the same thing in runInTransaction method will perform all this changes as one action (transaction) and will notify listeners only once.
How can i control which procedure executes first?
Just run them sequentially in a single thread.
And don't run database transactions in the main thread.
Realm.getInstance(context) will rarely return an already closed realm instance. How is this possible?
I am using Realm with RxJava, per https://realm.io/news/using-realm-with-rxjava/
In particular, this method throws an IllegalStateException: This Realm instance has already been closed, making it unusable.
#Override
public void call(final Subscriber<? super RealmList<T>> subscriber) {
final Realm realm = Realm.getInstance(context);
subscriber.add(Subscriptions.create(new Action0() {
#Override
public void call() {
try {
realm.close();
} catch (RealmException ex) {
subscriber.onError(ex);
}
}
}));
RealmList<T> object;
realm.beginTransaction(); //THROWS EXCEPTION
//...
}
If I comment out the realm.close(); issue, no problems. Though I think this will lead into a native memory leak, then.
My best guess as to why this is occurring is that multiple calls to this method are being made, and if these method calls line up perfectly, then an already closed realm instance can be retrieved?
EDIT: By using Schedulers.io(), I get a lot of Calling close() on a Realm that is already closed warnings. My guess here is that somehow after I am done using the .io() thread, the realm instance is automatically closed. Not sure why this would happen though.
EDIT2: By switching to using Schedulers.newThread() instead of Schedulers.io() for my observables, the issue stopped appearing. But I am seeing a lot of Remember to call close() on all Realm instances warnings. I am pretty sure I am closing them, so I am very confused about this.
EDIT3: By switching to using AndroidSchedulers.mainThread(), no errors. Except my Realm calls run on the main thread, which is bad bad bad. My guess why this causes no warnings is because the realm now lives on the main thread, which is also where realm.close() is called (via the rx.subscriber).
EDIT4: Here's the logic for my realm observable call.
#Override
public Observable<List<ImageArticleCategoryEntity>> getArticleBuckets() {
return RealmObservable.list(context, GET_ARTICLE_BUCKETS)
.filter(FILTER_OUT_NULL_OR_EMPTY_LIST)
.switchIfEmpty(refreshAndSaveAndLoadNewDataFromDb)
.map(CONVERT_FROM_REALMLIST_TO_IMAGE_ARTICLE_ENTITYLIST);
}
public void loadArticleImages() {
articleRepo.getArticleBuckets()
.subscribeOn(RealmThread.get())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<List<ImageArticleCategoryEntity>>() {
#Override
public void onCompleted() {
Timber.v("Loading article images complete!");
if (view != null)
view.hideLoadingAnimation();
}
#Override
public void onError(Throwable e) {
Timber.e("Error loading article images", e);
Log.e("tag", "Error loading article images", e);
if (view != null) {
view.hideLoadingAnimation();
view.showLoadingErrorToast();
}
}
#Override
public void onNext(List<ImageArticleCategoryEntity> integerImageArticleCategoryEntityHashMap) {
if (view != null)
view.loadArticleImages(integerImageArticleCategoryEntityHashMap);
}
});
Let's simplify how the lifecycle of the Realm instance is managed. If we manage it in the context of the resume/pause cycle of an Activity or Fragment, we can much more easily control and stop work that might consume that Realm instance. The tools in RxAndroid help a lot with that.
So, I'm going to assume for this example this is happening from an Activity, but a very similar approach could be used from a Fragment, or extracted into helper classes with just a hint more plumbing.
If you bind your Observable to the lifecycle of the Activity or Fragment using RxAndroid, which it appears you are already using for mainThread(), you should be able to manage your Realm instance easily within a lifecycle. I'm also using RxAsyncUtil for Async.toAsync, which makes the original creation of the Realm instance easier.
I've used Rx a lot more than Realm (though I've toyed with it a good bit), so forgive me if some of my API usage isn't perfect. I've also abbreviated things to java8-style lambdas just for ease-of-writing and reading. If you're not using something like retrolambda it should still be pretty easy to convert it back.
class MyActivity extends Activity {
private final CompositeSubscriptions realmSubscriptions = new CompositeSubscription();
private AsyncSubject<Realm> realm;
#Override
protected void onResume() {
super.onResume();
realm = AsyncSubject.create();
realmSubscriptions.add(getRealmInstance());
// This could actually happen anytime between onResume and onPause.
realmSubscriptions.add(loadArticlesImages());
}
private <T> Observable<T> bindToRealm(final Observable<T> observable) {
// Utility to bind to the activity lifecycle, observe on the main thread
// (implicit in the bindActivity call), and do work on the Realm thread.
return AppObservable.bindActivity(this, observable.subscribeOn(RealmThread.get()));
}
private Observable<List<ImageArticleCategoryEntity>> getArticleBuckets(
final Realm realm) {
/* As is, except the realm instance should be passed to RealmObservable.list instead
of the context. */
}
private Subscription getRealmInstance() {
// Grab the realm instance on the realm thread, while caching it in our AsyncSubject.
return bindtoRealm(Async.toAsync(() -> Realm.getInstance(MyActivity.this)))
.subscribe(realm);
}
private Subscription loadArticleImages() {
// Using the flatMap lets us defer this execution until the
// Realm instance comes back from being created on the Realm thread.
// Since it is in an AsyncSubject, it will be available nearly
// immediately once it has been created, and is cached for any future
// subscribers.
return bindToRealm(realm.flatMap((realm) ->
articleRepo.getArticleBuckets(realm).subscribeOn(RealmThread.get())))
.subscribe(
(next) -> {
if (view != null) {
view.loadArticleImages(next);
}
},
(error) -> {
Timber.e("Error loading article images", e);
Log.e("tag", "Error loading article images", e);
if (view != null) {
view.hideLoadingAnimation();
view.showLoadingErrorToast();
}
},
// onCompleted
() -> {
Timber.v("Loading article images complete!");
if (view != null) view.hideLoadingAnimation();
});
}
#Override
protected void onPause() {
// Stop any work which we added that involves the Realm instance.
realmSubscriptions.clear();
// Clean up the AsyncObservable. If it has a Realm instance, close it.
if (realm.getValue() != null) {
realm.getValue().close();
}
realm.dispose();
realm = null;
super.onPause();
}
}
You should be able to easily extract this outside of an activity as needed, and just pass an Activity/Fragment instance for the lifecycle binding, as well as the Observable<Realm> which in this case would be the AsyncSubject. If there are still race conditions due to the subscription work, you may want to experiment with adding .unsubscribeOn(AndroidSchedulers.mainThread()) or even .unsubscribeOn(Schedulers.immediate()) (I'm actually not sure which would be best in this scenario) to bindToRealm to ensure unsubscribing happens when you want it to in the onPause, before the Realm instance is closed.
I need save some objects in DB. I'm using this code in my Dao class.
public void saveActions(List<Action> actionList) throws SQLException {
for (Action action : actionList) {
createOrUpdate(action);
}
}
And sometimes I have CursorWindowAllocationException in createOrUpdate() function.
Does anyone have solution of this problem?
If you look up the source of CursorWindowAllocationException it reads:
This exception is thrown when a CursorWindow couldn't be allocated,
most probably due to memory not being available.
If you follow the stack, you'll see that the call com.j256.ormlite.android.AndroidDatabaseConnection.queryForLong is creating a cursor for every createOrUpdate call.
So what's likely happening here is that there are too many Cursors being created before the memory is freed.
You should execute these calls in a transaction, or better yet, use batch tasks. E.g.
actionDao.callBatchTasks(new Callable<Void>() {
public Void call() throws SQLException {
for (Action action : actionList) {
actionDao.createOrUpdate(action);
}
return null;
}
});
You must call cursor.close(); in finally { } block when you do not need cursor anymore. Cursor allocation will fail without calling close() on cursor after some time when system resources for cursor allocation will be not available.
This question already has answers here:
How can I avoid concurrency problems when using SQLite on Android?
(10 answers)
Closed 9 years ago.
I'm confused a little bit when tried to establish a multithreading work with my SQLite db, so I have a service which periodically load a data from server and insert it in different db tables, also any user in any time can store his own data in db when pressed "save button" in app windows, so based on the rules that only one thread at time can write a data in db I'd'd like to make a thread-cooperation. At first I have created a singleton which have only one db instance, and all goes pretty good with my read-db methods, cause all threads can read data in the same time, but what about writing? I use a thread inside write-function, and don't give a start another until previous thread has finished it work.(also I do it for calls from ui thread when user press save button)
Question: All I want to do is consider two situations - first is when threads call's same function to write data then I used synchronized, second - when threads call different write functions I should use a lock, right? So now I came to decision, but is it correct and right to do like that?
Code (Updated):
// Sync method for processing 1st situation
public synchronized void addPoints(final ArrayList<Point> points, final OnDBOperationListener listener) {
if (listener != null) {
// Lock for others write-threads in 2nd situaton
synchronized (mWriteLock) {
while (mWriteWait) {
try {
mWriteLock.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
mWriteWait = true;
try {
SQLiteDatabase db = getDatabase();
// write a data
listener.onSuccess();
} catch (Exception e) {
e.printStackTrace();
listener.onError();
} finally {
closeDatabase();
synchronized (mWriteLock) {
mWriteWait = false;
mWriteLock.notifyAll();
}
}
}
}
}
After a long search i finally found a gret answer for my broblem, so anyone who want to creat e multithreading acess to db should read this first What are the best practices for SQLite on Android?
I'm designing out a module in Android that does some processing and then writes to the database using ORMLite transactions. In particular, my background code will be something like:
public class BackgroundOperation implements Runnable {
#Override
public void run() {
//Do some stuff
//Write to the database in a transaction
try {
ORMHelper h = ORMHelper.getDefaultOrmHelper();
final MyModel modelObj = h.myModelDao.queryForId(someId);
TransactionManager.callInTransaction(
h.getConnectionSource(),
new Callable<Void>() {
public Void call() throws Exception {
modelObj.col1 = 10;
modelObj.col2 = "hello";
h.myModel2Dao.update(modelObj);
h.myModel2Dao.create(new MyModel2("a", "b"));
return null;
}
}
);
}
catch (Exception e) {
return null;
}
}
}
This runnable will then be executed by being submitted to a ThreadPoolExecutor. I want to be able to cancel the background thread if needed and am trying to make sure that if the operation is cancelled, then the transaction will simply fail and do nothing. For example, if I do this:
Future f = myThreadPoolExecutor.submit(new BackgroundOperation());
//Some time later
f.cancel(true);
Can I be sure that it will be an all or nothing deal with the transaction in ORMLite. That is, there is no cleanup needed and my modelObj will have either both col1 and col2 set or neither set? Do I have to do anything special when catching the InterruptedException in the Runnable to handle the case when a task is cancelled in this way, or can I simply exit?
If you call f.cancel(true), all that does is interrupt the Thread which causes wait(), sleep(), and some other methods to throw InterruptedException. It will not cancel the database transaction underway.
If you want, you can check for the interrupted bit in the middle of your IO operations:
h.myModel2Dao.update(modelObj);
if (Thread.currentThread().isInterrupted()) {
throw new RuntimeException("Thread was interrupted");
}
h.myModel2Dao.create(new MyModel2("a", "b"));
For more information about what happens when a thread is interrupted see here:
What does java.lang.Thread.interrupt() do?
Transactions are for when you are updating multiple objects as a single unit or writing to multiple tables. See the documentation about transactions which has an example of updating an Account and an Order inside of a transaction.
Also, you do not need to use a transaction if you are updating multiple fields in the same row. The update statement is considered to be a single unit and the database should ensure that the row gets updated atomically. Only if you are updating multiple different rows either in the same table or in separate tables do you need a transaction.
ORMLite will utilize the sqlite transactions under the covers. This is most likely a double phase commit which only allows you to commit a transaction as an entire unit.
In short, you can be assured that col1 and col2 will only be modified as a single atomic unit. Also, it if is interrupted the commit will fail and the changes to col1 and col2 will be rolled back.