Is closing a database opened with window.openDatabase necessary? - android

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.

Related

TimeoutException: net.sqlcipher.database.SQLiteCompiledSql.finalize() timed out after 10 seconds (Android)

In my android app I am using Room with sqlcipher library for encrypt/decrypt. Often I see in Crashlytic the following crash:
java.util.concurrent.TimeoutException:
net.sqlcipher.database.SQLiteCompiledSql.finalize() timed out after 10
seconds at sun.misc.Unsafe.park(Native Method) at
java.util.concurrent.locks.LockSupport.park(LockSupport.java:190) at
java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:868)
at
java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:902)
at
java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1227)
at
java.util.concurrent.locks.ReentrantLock$FairSync.lock(ReentrantLock.java:231)
at
java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:294)
at
net.sqlcipher.database.SQLiteDatabase.lock(SQLiteDatabase.java:567)
at
net.sqlcipher.database.SQLiteCompiledSql.releaseSqlStatement(SQLiteCompiledSql.java:104)
at
net.sqlcipher.database.SQLiteCompiledSql.finalize(SQLiteCompiledSql.java:146)
at java.lang.Daemons$FinalizerDaemon.doFinalize(Daemons.java:289) at
java.lang.Daemons$FinalizerDaemon.runInternal(Daemons.java:276) at
java.lang.Daemons$Daemon.run(Daemons.java:137) at
java.lang.Thread.run(Thread.java:929)
The line where it crash is SQLiteDatabase.lock() line 567
Previously it was line 566, but in that method I inserted a check: if the database is not open -> return and don't proceed with locking, but it didn't help and the crash appeared again.
I think that this crash may be because the garbage collecting happens when the app is in the background (our app has a foreground service that works all the time). But not sure how to fix it.
As for Room: I don't close it, it is open all the time, because my app works all the time, so need it often. And close it after every query to the database is the bad practice.
I asked the developers of sqlcipher, but they don't know, what can cause this crash. Maybe anybody knows?
I've been seeing this in Crashlytics for quite a while, and seemingly has been able to fix this in the end completely, I'll post the full research here.
Issue
For queries like this one:
#Query("DELETE FROM table WHERE id NOT IN (:ids)")
abstract fun deleteNotInList(ids: List<String>): Int
Room generates code that doesn’t clean cursor and generated statements (on the image below, compare how the method one doesn’t use cursor, and doesn’t call release() methods, compared to the bottom one which calls cursor.close(); method names and queries are slightly different there, cause I simplified the snippets):
In this scenario statements are kept in memory un-released, and the releasing shifts to GC phase, inside finalize() method. And finalize() in SqlCipher (SQLiteCompiledSql) in turn needs to lock the Database in order to release the statement:
Problem is that the Database might be locked for a long transaction for more than 10 sec (or a batch of shorter transactions, awakening order using such locks isn’t guaranteed and isn’t fair).
GC watchdog crashes the thread when it reaches 10/20 seconds depending on the OS/JVM exact version.
Solution
Is to rewrite all DELETE / UPDATE using IN operator with manual raw queries like this:
#RawQuery
abstract fun deleteNotInListRaw(query: SimpleSQLiteQuery): Int
fun deleteNotInList(
ids: List<String>
) {
deleteNotInListRaw(
SimpleSQLiteQuery(
"DELETE FROM table WHERE id NOT IN (${ids.joinToString(prefix = "'", postfix = "'", separator = "','")})"
)
)
}
In this case the query uses cursor and closes it after deletion is done, still locking database but not on GC phase and on the dedicated thread.
P.S.
There’re more stable long term solutions possible but they needs to be implemented on the Room / SqlCipher sides.
In the given state, SqlCipher can be refactored to not lock the database on GC phase. There’s an open issue for that: https://github.com/sqlcipher/android-database-sqlcipher/issues/537
Room should probably fix the codegen and use the query builder and generate statement closure lines, there’s no open issues there, but I’ll double check this thought later and will raise this as an issue with them.
For us this has seemingly solved the issue completely.
Note that all DELETE / UPDATE queries using IN / NOT IN operators and probably some other operators that prevent Room from precompiling the query (due to runtime params) cause this. You can check the codegen to verify that generated code calls either cursor.close() or statement.release()

Android SQLite Update not working in background in AsyncTask

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.

Using large databases in Android Apps

I have an Android App that uses an SQLite database. The problem is that the database has gotten to be quite large (41.6 MB) and I'm starting to wonder if I am correctly loading it into the app.
Right now what I'm doing is in the onCreate() method of my app's splash screen I'm opening the database, querying it, saving the data as objects, then closing the database. It takes a noticeable amount of time on a cold launch. Ideally I want to do all of this on the first launch and save the data somewhere on the device to speed up future launches. Is there a way I can do that?
I have same problem before.Probably thats the same case. Mine one because I pinging too many times of the Backend API and there is one more problem where you insert into your Object. probably when you insert it, The data that should just insert once, you insert it too many times on the for loop if there are many object that you are going to insert. Probably you can put your code when inserting it. lots of dev doesn't care about that but actually thats really annoying to user.
in my case because of this :
manager.removeAllStop();
manager.removeAllUpload(); //--> remove the data before insert the new one
for (Stop stop : SomeStopsList) { //--> when I want to loop through and want to add it to other object
**manager.insertAppTourStopList(appTourStopList);** //--> PROBLEM HERE !!!! THIS COULD BE OUTSIDE FROM THE LIST BECAUSE YOU WANT THIS TO BE ADDED ONCE ONLY
ArrayList<Upload> uploadList = new ArrayList<Upload>(); // --> new object
for (Upload uploadData : stopList.uploads) { //--> get the list and loop
Upload upload = new Upload(); //--> create new object
upload.setStopId(stopList.getId()); //--> insert it
}
}

How to over come database not opened exception?

I am implementing an app related to database.
So many times I am calling open and close database connection to insert, update and delete.
It is working fine.
But some times I am getting a database not opened exception in different situations.
How to solve these issues?
Well unless you put proper exception handling you would never know what causes this.
However a good idea is to adopt good ORM mapper for SQL Light with Android and this will improve your database interactions and exception handling and opening and closing it efficiently.
You can opt for SUGAR or ORMLight if you wish; In my opinion this should help you to fix your problem.
Based on the information you provided I can assume that the problem is in your business logic and nobody but you should be able to tell you the root cause.
Without your code here, we won't be able to point you to exact place.
One of the possible reasons can be that by your business logic you are trying to do some operation (insert, update whatever) on closed database.
You can do some workaround to try to ensure that your DB is always open when it is needed. If you implement database getter method with so called lazy initialization approach it will guarantee at least, that the DB is open when you need to access it.
Here is what I am talking about:
1. make a public method which supposed to return DB object:
public SQLiteDatabase getDB() {
if ((mDataBase == null) || (!mDataBase.isOpen())) {
// create or open your database using an OpenHelper
mDataBase = SQLiteOpenHelper.getWritableDatabase();
}
return mDataBase;
}
Now, everywhere in your code use this method to access the DB instead of directly accessing a variable mDataBase.
Note that the code is just to give you an idea and not actually compilable.
Still, I would recommend you to fix your business logic instead of using this workaround.

Unable to access database using raw key data after forcing app to stop

I'm trying to use a row key data in order to avoid key derivation (According to the SQLCipher documentation):
this.myDb = SQLiteDatabase.openOrCreateDatabase(databaseFile, "", null);
this.myDb.rawExecSQL("PRAGMA key = \"x'2DD29CA851E7B56E4697B0E1F08507293D761A05CE4D1B628663F411A8086D99'\"");
Cursor myCursor = this.myDb.rawQuery(query, params);
When I force the application to stop, accessing the data becomes impossible.
This behaviour does not happen without using SQLCipher. The database is readable even after forcing application to stop.
I'm I doing something wrong ?
Thanks in advance,
My understanding is that the database is not accessible except by the app - this is to prevent other apps from accessing data they shouldn't
You need to show us more code so we be able to give you the right answer.
Your call is equal to
SQLiteDatabase.openDataBase(databaseFile.getPath(),"",
CREATE_IF_NECESSARY, null);
When you force close your application everething is restored, including all the static params.
So the only thing that can effect from previously run of the application is the data you are saving at the data base.
So try to look at that direction.
For those who are facing the same problem, a solution is given here:
https://github.com/sqlcipher/android-database-sqlcipher/issues/62#issuecomment-8090646

Categories

Resources