Android: how cancel/interrupt/break pending SQLite query? - android

In my Android application user can fill database with imported data, then perform predefined, complex SQL query. With some data pattern, query will take very long (minutes and more on HTC Hero), while common usage will not exceed 10-15 seconds.
Is there any way to cancel pending SQLite query on Android? Timeout would be as good as well, partial results are not required. I'm performing query within AsyncTask.doInBackground()
I know best way is optimize query or change app logic, but in this case results depends on user knowledge anyway, so I'd like to help those who enter troublesome pattern.

You can't cancel pending sql query. It's done in SQLite engine. SQLite has native methods to interrupt query, but it's not accessible in java interface.
The best would be to improve query performance.
Read this: http://www.sqlite.org/cvstrac/wiki?p=PerformanceTuning
You can divide long running queries to smaller by limiting row number. Then it would be easier to cancel query that is performed in chunks.

ASyncTask supports cancellation and the flag should cancel all, including your SQL Query.
ASyncTask.cancel(boolean mayInterruptIfRunning)

SQLite provides a intrerupt method:
http://www.sqlite.org/c3ref/interrupt.html
I'm not sure how you would call that in Android though.

You can cancel running queries on sqlite by using android.os.CancellationSignal which can be passed to some of the methods of android.database.sqlite.SQLiteDatabase

Related

Start the API call as soon as record inserted in Sqlite database in android

I have a requirement, if a record inserted in local DB(Sqlite) as soon as possible app triggers API call to send offline record to server. I don't want to use Timer and Alarm service because those will run continuously in background even there is no offline records in local DB. Any other alternative solutions?
Note: Target SDK level is Andorid 8 version.
As I understood your task (in general) - is how to invoke some function (some sendNewRecord() for instance) when new row(s) were inserted in some Sqlite table.
I suppose there are 3 alternative ways to implement such a task in Sqlite:
To invoke your function at the same place in code where you put your insert-new-data code. I think that's the most obvious and the simplest choice. I hope in your app there is only one place where you do it (Repository pattern or its analogue).
To put some observer(callback function) to your table and process changes in table. That more complicated task, and as far as I know you'll get this callback after every change in your table and it would be another task to identify what rows in your table were changed. Using modern Room framework and LiveData as a query result, you can observe changes in the table in more strait-forward way, but then nevertheless you have to detect inserted rows.
To use some scheduling mechanism for syncing (Executor, Timer and so on). You've wrote that it's not your case.
I would recommend you to choose first way. At least I don't see its disadvantages.

Android: how can I lock SQLite database accessible with ContentProvider (or other way of executing atomic conditional operations)

I got two tables in my SQLite DB: entities and user_actions. Their approximate schemes:
The flow of the program is something like this (all DB accesses handled by ContentProvider):
The user performs some action which modifies one of the entities
The corresponding entity is updated in entities immediately. locally_modified value of this entity is set to 1
The information about user's action is stored in user_actions
At some point in future a sync session with the server is being initiated (I use SyncAdapter framework)
User's actions from user_actions are uploaded to the server one by one and removed from the DB in a background thread
When the uploading completed, I need to clear locally_modified flags in entities
At this point I encounter my atomicity issue: the synchronization with the server happens in a background thread, therefore the user can use the app and perform additional actions. As a consequence, right before I clear locally_modified flag for an entity, I must check that there are no records in user_actions corresponding to this entity. These three steps must be executed atomically for each entity having locally_modified set to 1:
Query user_actions for entries corresponding to entity's _id
Test whether the query from #1 returned an empty set
Clear locally_modified of that entity to 0
Given the above scenario, I have three questions:
Q1: Is there a way to lock SQLite DB accessed over ContentProvider in Android such that it can be accessed only by the locking thread?
Q2: If the answer to Q1 is positive, what happens if some other thread tries to access a locked DB? What precautions should I take to ensure reliable operation?
Q3: It is possible to execute atomic transactions with conditional logic using ContentProviderOperation? You can use "back-references" as described in this answer and this blog post to reference the result of a previous operations, but is there a way to use that result in some kind of if-else statement?
Thanks
Is there a way to lock SQLite DB in Android such that it can be accessed only by the locking thread?
Yes, have a look at SQLiteDatabase.beginTransaction() (source). I believe you need SQLite's exclusive transactions, but you need to study that a bit more for your exact usage.
If the answer to Q1 is positive, what happens if some other thread tries to access a locked DB? What precautions should I take to ensure reliable operation?
There's an SQLite.amIInTransaction() method that you could check, or just catch an SQLiteDatabaseLockedException (more SQLite exceptions that you should look up)
It is possible to execute atomic transactions with conditional logic using ContentProviderOperation? You can use "back-references" as described in this answer and this blog post to reference the result of a previous operations, but is there a way to use that result in some kind of if-else statement?
Never done that, but it seems that overriding ContentProvider's applyBatch and wrapping it in a transaction should work:
Android: SQLite transactions when using ContentResolver
The answer turned out to be pretty simple, but it is kind of a "hack" - just add additional Uri to ContentProvider.
For example: initially my ContentProvider supported the following URIs:
Uri.withAppendedPath(MyContract.CONTENT_URI, "entities")
Uri.withAppendedPath(MyContract.CONTENT_URI, "user_actions")
In order to support the atomic operation described in the question I added an additional Uri:
Uri.withAppendedPath(MyContract.CONTENT_URI, "clear_modified_flag")
When this Uri is updated through:
getContentResolver().update(
MyContract.ClearModifiedFlag.CONTENT_URI,
new ContentValues(),
null,
null);
my ContentProvider executes an SQLite transaction that locks the database for the duration of the operation and rolls it back in case of any errors (as described in this answer).
That's it.
P.S. my ContentProvider is not exported (i.e. other apps can't access and use it), therefore it is safe to add this new Uri to it. But keep in mind that if you do export your ContentProvider, then exposing functionality like this one could be problematic.

Setting initial values for SQLite in onCreate() of SQLiteOpenHelper

I am creating an Android app for which I need to create a SQLite DB and pre-populate it with some values.
The Android documentation says this about what to do in "onCreate" of the SQLiteOpenHelper:
Called when the database is created for the first time. This is where the creation of tables and the initial population of the tables should happen.
Reference - http://developer.android.com/reference/android/database/sqlite/SQLiteOpenHelper.html#onCreate(android.database.sqlite.SQLiteDatabase)
I am doubtful about the following 2 things -
What is meant by "when database is created for the first time"? Is this done on the first launch of the app or only when the first DB request (read/write etc) is done.
If it is the latter, I fear that it may take quite some time to create DB, pre-populate it with values (I have about 60 rows to be inserted into 1 table) and then read the DB to show it. Is this the best practice?
I have been doing all my DB operations in AsyncTasks. But I am doing the table creations in onCreate using "db.execSQL" statements. Is this fine (in terms of convention/ performance) or should I go for an AsyncTask here as well?
Any help is appreciated.
1) The later. It is done on the first read or write to the DB.
Your fear might be correct, this is why you can ship your app with a database that's already populated. Or you can launch an AsyncTask with a simple SELECT 1 FROM anytable query. More about shipping with DB here. (60 rows is nothing to fear about tho, and you can safely just keep using AsyncTasks).
2) Yes it is fine. The onCreate logic will run when you first read/write the DB, so it if you always use AsyncTasks onCreate will run in an AsyncTask also.
What is meant by "when database is created for the first time"? Is this done on the first launch of the app or only when the first DB request (read/write etc) is done.
It happens when you first query from database in general term. After that only Upgrade method is called that too when you change the db version.
If it is the latter, I fear that it may take quite some time to create DB, pre-populate it with values (I have about 60 rows to be inserted into 1 table) and then read the DB to show it. Is this the best practice?
60 rows insertion is not a big task. More you can read about beginTransaction(),commitTransaction and endTransaction for insertion. It will make your insertion task lighting fast.
I have been doing all my DB operations in AsyncTasks. But I am doing the table creations in onCreate using "db.execSQL" statements. Is this fine (in terms of convention/ performance) or should I go for an AsyncTask here as well?
It good you are doing you Db operation in AsyncTask and its completely fine.
Speaking of DB operations:
Performing DB operations in AsyncTask is not a good approach, generally. As you might encounter a problem called "memory leak", and it might come as a silent assassin in the night.
There's lot written on this issue. Just google "asynctask leak context" and here you go.
So how to perform DB operations?
Using Loader API in conjunction with ContentProvider is considered good approach for querying database. Loader asynchronously queries your database and delivers the result to specified subscribers. Configuration changes or other sudden stuff does not bother it.
And it is really convenient to query your data using loader API once you know how to do it.
Single inserts/updates/deletes might be done directly from the main thread via ContentResolver. These calls will be blocking (synchronous), but I bet you user would never notice anything while the amount of data is not large.
If you're operating on a large dataset, and you fear you'll be significantly blocking UI thread, I'd suggest using IntentService or any custom Service capable of doing operations in background (note that by default Service operates on main UI thread and you have to specify background operation yourself or use IntentService)
Speaking of DB initialisation:
You might create a one-time IntentService, if you're initialising a large set of data. It will handle your request asynchronously and, for example, perform a broadcast that the application is set up and ready, so you might stop a "wait a sec, performing app initialisation" screen and show user your data.
There's also nothing wrong with shipping your database along with application, though it appears to be a bit hackish solution.
Either way, you choose what is more suitable for you.

Android - SQLite Trigger vs Java SQL insert, what is more efficient?

I want to insert a row in a table, and when I do the insertion I want to insert another row in another table at the same time.
What is the more efficient way to do this in an Android app?
Is it more efficient to do a trigger when I insert one row and SQL takes care of the insertion, or do two inserts with database.insert() in Java?
The information needed to insert the second row is already in the first insertion.
Thanks!
I want to insert a row in a table, and when I do the insertion I want
to insert another row in another table at the same time.
For sure use for this TRIGGER, because it working on database layer so you only need to create it and then it will be called itself when you do something(depends on how you implement trigger).
Also it's more faster and you do not call it explicit, just let database work. All what is database able to do, let do it on database.
Update:
So after a little discussion with #Graham Borland you can use also inserts in TRANSACTION.
Have look at Android Database Transaction.
And conclusion what is faster?
I exactly don't know it because i don't have performance tests but for sure TRANSACTIONS are the safest i think but whether faster than TRIGGERS this i don't know exactly but usage of transactions is very good and mainly safe approach to prevent database get to incorrect state that is very undesirable.

Is there any efficient way to make Android database query faster?

In my Android app, I need to get 50,000 database entries (text) and compare them with a value when the activity starts (in onCreate()). I am doing this with the simplest way: I get the whole table from db to a cursor. However this way is too laggy. Are there any other ways to do it more effectively ?
Edit: The app is "scrabble solver" that is why I am not using WHERE clause in my query (Take the whole data and compare it with combination of the input letters). At first I was using a big table which contains whole possible words. Now I am using 26 tables. This reduced the lag and I am making database calls on a thread - that solved a lot problems too. It is still little bit laggy but much better.
To summarize and add a bit more
Let the database do the work of searching for you, use a WHERE clause when you perform a query!
If your where clause does not operate on the primary key column or a unique column you should create an index for that column. Here is some info on how to do that: http://web.utk.edu/~jplyon/sqlite/SQLite_optimization_FAQ.html#indexes
Google says: "Use question mark parameter markers such as 'phone=?' instead of explicit values in the selection parameter, so that queries that differ only by those values will be recognized as the same for caching purposes."
Run the query analysis using EXPLAIN QUERY PLAN http://www.sqlite.org/lang_explain.html and look for any scan operations, these are much slower than search operations. Uses indexes to avoid scan operations.
Don't perform any time consuming tasks in onCreate(), always use an AsyncTask, a Handler running on a background thread or some other non-main thread.
If you need to do full text search please read: http://www.sqlite.org/fts3.html
You should never read from the database in the UI thread. Use a background thread via AsyncTask or using regular threading. This will fix the UI lag issue your having.
Making the database read faster will help with bringing the data faster to the user but it's even more important that the fetching of the data does not block the user from using the app.
Check out the Following Links:
Painless Threading in Android
YouTube: Writing Zippy Android Apps
Use a WHERE clause in your SQL rather than reading the whole thing in. Then add an index on the columns in the WHERE clause.
At least you can put index on the field you compare and use WHERE clause. If you are comparing numerics Sqlite (db engine used by Android) supports functions such as MIN and MAX. Also if you are comparing partial strings you can use LIKE. For query optimization there are many resources such as this

Categories

Resources