In some of my apps I do offer an in-app backup and restore of the Android database. The name of the database file is set in the SQLiteOpenHelper.
Now I heard that there might be additional files depending on the Android Version and/or the manufactor (e.g. HTC).
What are the names of all these files?
It depends.
Standard Android SQLite installations create just one single database file for each database. This file is located in "/data/data//databases" and is named as you instructed to do so for example in the "name" parameter in this constructor:
SQLiteOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version)
So a database file is called for example:
/data/data/<packagename>/databases/mydatabase.db
SQLite itself has a new feature called Write-Ahead-Logging (WAL). This WAL uses a different logging mechanism than those from traditional database makers. With WAL new commited data will be stored in an additional file that has "-wal" attached to your original filename. For example:
/data/data/<packagename>/databases/mydatabase.db-wal
This data is missing in the traditional database file until a configurable threadhold is reached - or the app developer instructs the database to move this data over.
However, a WAL-enabled database is not valid with the "*.db" file alone. All additional files are required to form the database. This means a WAL-enabled database will not work on an Android device with an SQLite version that does not support WAL. So a database backup from a WAL-enabled database will not work on older Android systems.
However, only few devices (from HTC for example) and some custom-ROMs enable WAL. Standard Android does not enable WAL.
Related
My Android App use SQLite database as an asset.
I deliver a .db file in /assets directory to provide both read-only tables and read/write tables.
When the user uses the App, it's datas are stored in read/write tables.
Sometimes I need to modify the schema of the database by adding a column in a table for example.
So I need to deliver a new .db file in the release.
If i do this the problem is that the user loses his data.
Is there a way to export/import the user data when he updates the App on his devise ?
I didn't find anything about this issue on the Web.
The standard way of handling this is, assuming that you are using (extending) the SQLiteOpenHelper class, is to utilise the version number in conjunction with overriding the onUpgrade method to ALTER the schema of the existing database, thus retaining the existing data.
version int: number of the database (starting at 1); if the database is older, onUpgrade(SQLiteDatabase, int, int) will be used to upgrade the database; if the database is newer, onDowngrade(SQLiteDatabase, int, int) will be used to downgrade the database
The SQLiteOpenHelper (or if using SQliteAssetHelper which extends SQLiteOPenHelper), when opening the database (if it exists) compares the coded version number against the version number stored in the header of the database. If the coded version number is greater than the stored version number then the onUpgrade method is called which is passed three parameters,
the SQLiteDatabase
the old (stored) version number as an int
the new (coded) version number as an int
You would typically check old and new and have code that makes the changes (ALTER TABLE or an alternative if the limitations of the ALTER TABLE force to do something like rename the original table, CREATE the changed table with the correct name, copy the existing data from the original table to the new table and finally DROP the renamed original table )
If the App is installed for a new user then that is when the asset will be copied. Thus covering both scenarios.
If not using an extension of SQLiteOpenHelper, then you can mimic using the stored version number by reading 4 bytes at offset 60 and comparing this to a coded version or even by copying the asset and getting the asset's version number, which would be managed with whatever tool you use to maintain the asset (e.g. via the user_version PRAGMA)
SQLite Database Header
Perhaps I am missing something because I couldn't find anything about this. I want to use Android Room to store my data locally on the device. How do I ensure that it is a single data base, not a new instance each time? Is it by class name?
When you build the Room Database and then access the built database it opens the database (or creates it if it doesn't exist (once unless the App is uninstalled)).
The database itself is a file and is typically stored in the App's data (data/data/the_package/databases), thus the data belongs to the App. The file persists (is effectively permenant).
If your concern is regarding a pre-packaged database (i.e. a database created and typically populated elsewhere (e.g. an SQLite management tool) and is supplied as part of the APK), then the App would copy the database from the APK prior to building/opening it.
As such creating the instance (build followed by access) is in effect opening the file rather than creating a new database everytime.
I have an online (postgresql) database, and a local copy of the db in an Android app. The data in these is synchronized, so the app can function offline, but can download new data and upload results when it can. Data is transfered via http GET and POST requests.
I have no problem synchronizing the data for known tables, however, I would like it to be possible to create new tables/alter tables in the online database and have that change reflected in the Android db automatically i.e. without having to release a new version with the updated synchronization code. Is there an obvious/standard way to do this that I haven't found? Google searches I have performed just refer to database migrations for two of the same system or for known schema.
Is there an obvious/standard way to do this that I haven't found?
There's no "standard" way but there are ways to do it depending on your requirements and how your app works.
It's actually a broad question but solvable if you give it a bit of thought.
My app downloads data from a server on a daily basis. Part of the download process involves downloading a DB version file (just a plain text file) similar to the following...
db_version=12345
...the string representation of the previous version (if any) is saved in SharedPreferences as an int value.
If the version in the file is greater than the one in SharedPreferences, the downloader pulls some text files which contain SQL commands for creating, dropping, altering tables etc. Only after the changes have been successfully made does the downloader pull the actual data files and update the DB data (not forgetting to update the latest version in SharedPreferences).
If the version in the file is the same as in SharedPreferences then obviously the download simply does the normal daily data download and DB data update.
I have a dilemma regarding a free app that I allow users to import their data (an sqlite file on the SD card) to a paid app.
After doing these updates; I realized that if I need to make future changes to the database, the user will need to import the same DB version (from the free app) to the existing version (of the paid app).
Real life example:
Let's say version 1 of both apps has 1 table with 5 columns. I do an upgrade to v2, now the 1 table has 6 columns.
When a potential user does an import within the paid app to bring in data from the free app, there is a chance that the free app can be on DB version 1 (if user didn't update app recently) while the paid app would be looking for DB version 2. This I assume leads to a crash on startup.
How can I do a check like this in the paid app before doing import: (pseudo code)
File olddb = oldDb.db; // get the back up file here from SD
int piadAppDBVersion = 2; // check for the current version of the database in paid app
if (olddb.getDatabaseVersion() == getPaidAppDBVersion()) { // made up functions
// allow import;
} else {
// Toast "Your databases are incompatible"
}
So the two questions:
1. If the above code is an appropriate solution to ensure compatibility, how can this be done?
2. If the above is not a standard solution to my problem, then what is?
Side note: My import consists of a simple file copy: copying a backcup db on external storage and overwriting the db on internal storage.
Consider a "version" or "feature" table in the SQLite Database. Do not be afraid to incorporate this as part of a stable schema; many of my database schemas contain journals of applied scripted changes.
However, SQLite does support PRAGMA user_version which can also be used for this purpose, albeit it can only store a single integer value:
The pragmas schema_version and user_version are used to set or get the value of the schema-version and user-version, respectively. The schema-version and the user-version are big-endian 32-bit signed integers stored in the database header at offsets 40 and 60, respectively.
The schema-version is usually only manipulated internally by SQLite ..
The user-version is not used internally by SQLite. It may be used by applications for any purpose.
The user_version PRAGMA is supported via the standard Android SQLite API which can be accessed with SQLiteDatabase.getVersion and setVersion; after the database has been opened.
See also Where does Android store SQLite's database version?
SQLiteDatabase sqlDb = SQLiteDatabase.openDatabase
(db.getPath(), null, SQLiteDatabase.OPEN_READONLY);
if (sqlDb.getVersion()>DBConnect.version)
{
//you have to update app to the last version first
}
I have a lite version of an application that uses a SQLite database. I want to copy that database over to the full version of the application when the user installs the full version.
I have written some code to perform the file copy, but the lite database file always comes up as unreadable. The file is there and I can point to it, but I can't read it to perform the copy.
In the Android documentation, we read:
You can save files directly on the
device's internal storage. By default,
files saved to the internal storage
are private to your application and
other applications cannot access them
(nor can the user).
Note the words, "by default".
Is there a way that I can override that default and make the SQLite file readable by my other application?
Thank you.
I believe you have 2 options.
Set the sql database to be world readable on creation. You can do this by setting the appropriate mode parameter in the call to openFileOutput() or openOrCreateDatabase().
Set the sharedUserId attribute in the manifest of both of your applications so that they have the same user ID. This treats both applications as the same user, giving both applications access to the same private set of files.