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
}
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
I have Xamarin.Forms shared project that I am testing on Android phone currently.
The sqlite db is stored in System.Environment.SpecialFolder.Personal as of now.
I want to make sure that user input data stored in db is preserved:
1 upon each next upgrade I am planning for an app, as well as
2 In additional situation where user removes his app totally and makes a fresh install
(I guess these are two totally different situations, but I might be wrong)
I would like to know whether an update of an App via Google Play Store Update will overwrite db file too, therefore all data be lost? Or will the data be preserved?
What would be the option if a user wanted to preserve his/her data even after an app Uninstall/Reinstall?
In second case, I am considering manual/automatic backup of SpecialFolder.Personal db version in use and storing it as a separate file outside of the package using External_STORAGE permissions.
Then the user will be able to Import data from external db file copy into the one that comes in the package.
Also, how should the Import look like?
Can I simply replace the file with external one?
Any advice on this topic will be very appreciated.
An update from Google Play will not overwrite the db file
If a user uninstalls the application, he will want to delete everything related with the application. So my best solution is to have a synchronize method within your API so that, when the user installs again the application, it will fill the local DB with the requested data.
I know there are a lot of similar questions and I hope not being repeating but I tried different things and I am still unable to fix the issue. I am developing an Android app that uses SQLite to store some data internally. I have been using this app in my own phone for a month and I added lot of data I do not want to lose, but at the same time I have been improving the app in my computer, but the improvements made me change some column names, adding some columns and tables. So, right now I want to update the app in my phone.
The previous version had the database set to 2 and the new one is 3. In addition, my app has the ability to create backups of the database just copying the file to the SDcard, and the ability to restore it by replacing the internal database file for one that the user selects from the sd card. Now the problem is that I did a backup of the database from the phone and I moved it to my computer to import it in the new version of the app running in an emulator to ensure the app can upgrade the database as expected before I install the new version on my phone. The problem is that onUpgrade for the SQLiteOpenHelper is never called. I did different trials but nothing is working. I can ensure the database from my phone has version 2 (I used sqliteadmin "PRAGMA user_version;" SQL query and answers 2) and my app looks for version 3. The app crashes as it is unable to find the new tables.
Here you have the code executed when importing a backup:
private boolean importDatabase(String path) {
String dbPath = mDbHelper.getDbPath();
mDbHelper.close();
boolean result = copyFile(path, dbPath);
if (result) {
mDbHelper = new DbHelper(this);
mDbHelper.getWritableDatabase().close();
}
return result;
}
I have to mention as well that when the user reaches the place to import/export the database in the new version, a new empty database with version 3 has been created as the app needs an active database from the stating activity. Then, when importing, this empty database file is replaced by the imported one. I wonder if there is any cached data I am missing to clean.
Could you give me some clues to understand why onUpgrade is never called in this scenario?
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.
I have a database that I built in SQLite browser, and it works fine. I launched an app with a prebuilt database and now I want to add more tables and data to that database.
I can get the app to launch the onUpgrade() method of the SQLiteOpenHelper. But the problem is, it's doing that EVERY time I use the helper.
I have it localized to, only on app launch, separating the upgrade command from the helper I used to retrieve data, but this is still a problem.
I have figured it out though, as I have been using the same database on my computer (the one that I'm editing) since version 1. So, whenever it writes the newer database onto the SD card it's showing version 1 even though I should be up to version 4 by now.
So, my question is, how can I manually edit the database version of the original database so that when it updates it isn't writing the old version number over the new one?
To manually update the version to 4 you execute the following SQL statement:
PRAGMA user_version = 4
Another way to change the version of your Sqlite Database. You can use DB Browser for SQLite:
Open the database file with "DB Browser for SQLite".
Change the "User Version" number to whatever number you want
Click the "Save" button
You can also set it via the setVersion SqlLiteDatabase method.
Source: http://developer.android.com/reference/android/database/sqlite/SQLiteDatabase.html#setVersion(int)