Suppose I have an app installed on my device that works on an SQLite database that is created alongside the first installation do the app. Now, suppose I make some changes on a new release that involve some changes on the DB schema. Obviously, it would be desirable to make the user know that it's DB will be wiped if the new release is installed. How can I make this advertisement prior to installation so the user can take needed steps in order to lose its data?.
In short, is there a way to open a dialog advertising the user on possible data loss and letting him decide what to do?
Thanks in advance,
Jose
Your database should have a method for updating the database
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// do something to upgrade
}
But that isn't called by default, but when your app first tries to open the database. At that point, you should be to architect a dialog that instructs your user and either do the upgrade or not.
It's also possible to alter the user's database within that onUpgrade method. For example, a string like this:
private static final String ALTER_TABLE3 = "ALTER TABLE exercises ADD COLUMN exscore REAL DEFAULT 0.0";
could be used in onUpgrade like so:
if (oldVersion == 5) {
db.execSQL(ALTER_TABLE3);
// now copy all of the values from integer to real
setUpMoveRoutine = true;
}
I did an upgrade from v4 to v5 that needed new elements within a table. And then called a method to move data as needed.
You should be able to manage just about any changes in your database
You shouldn't need to wipe the database to update the app, it's not a great user experience to do this and considering the SQLite implementation in Android is designed to allow you to upgrade the database it's also unnecessary.
Just implement the onUpgrade method in your SQLite helper class and provide upgrade steps to move between the different versions of your database.
The ALTER TABLE statements that SQLite supports are pretty limited, but you can add columns, if you need to make more complex structure changes you may need to copy data to a temporary table drop/create your table and then insert again.
Related
I am working on some android application. I have two related questions about SQLite database in android.
1) I have got a bunch of SQL queries to create database tables in my SQLiteOpenHelper class. The queries are executed in onCreate(SQLiteDatabase db) method. Are the database and database tables re-created each time I run my application, using my cellphone? or Should I do something else to create database and tables at once if they don't exist?
2) When I release my application, like to Playstore, do I remove all the SQL queries and include the created database file in my application folder so that users can download the database along with the application? or should I leave the SQL queries to create database and tables so that users download my application and when they run it, the queries are executed and create the database?
Sorry guys for asking two questions at a time, but I believe they are quite related.
Thanks!
1)Once you have changed your database structure something like adding columns or modify existing table,
You should increase your database version number. Then only your changes will be applied.
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
For more info refer this link.
https://developer.android.com/training/basics/data-storage/databases.html
2) You can't remove all the SQL queries and include the created database file in your application. Because the table only created when the user runs the application. And it is created only once by calling the database initialization method. Only the database is updated if you increase the database version number.
I publish an android app in app store. After a while I do some edits special in database and move on to next version. My problem is edits and changes in database is just in content and no table nor column are added into it. so the schema is stick and the content is changed. Must I call onUgrade? Should I not change the database version?
If you use SqliteAssetHelper , You can do like the document says :
Upgrades via overwrite
If you have a read-only database or do not care about user data loss, you can force users onto the latest version of the SQLite database each time the version number is incremented (overwriting the local database with the one in the assets) by calling the setForcedUpgrade() method in your SQLiteAsstHelper subclass constructor.
You can additionally pass an argument that is the version number below which the upgrade will be forced.
Note that this will overwrite an existing local database and all data within it.
void onUpgrade (SQLiteDatabase db,
int oldVersion,
int newVersion)
Called when the database needs to be upgraded. The implementation should use this method to drop tables, add tables, or do anything else it needs to upgrade to the new schema version.
I would like to suggest don't increase database version then not need to call DB onUpgrade methods. If have changed in the schema you should need to call onUpgrade(). And upgrade database version also.
I am working on an android application that is using SQLite database to store data. I have "Favorite" table in database for storing favorite items of particular user.
The problem is when user will get an update of this application then I want to update database except "Favorite" table, because it will contain user specific favorite items.
Edit:
Now I know that I can update records on some tables in onUpgrade() event of SQLite database, but I really don't know how to manage two databases in an application? I have searched on Google but not get any idea to do this, there are many example to copy data from asset folder to new database.
I have to select all records from a table from "existed database" and copy to table of "new database". Table name may be same of both databases.
There are 2 aspects of updating Android app and DB:
1) If you just upgrade your app and don't change your DATABASE_VERSION (last parameter when you implement constructor of your class-descendant from SQLiteOpenHelper) - old database is retained and you may use it without any additional actions
2) If you change your DATABASE_VERSION- in your implementation of SQLiteOpenHelper there is method onUpgrade() which may look like this:
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// RUN YOUR SQL SCRIPTS:
db.execSQL(DBContract.Table1.SQL_DROP_TABLE);
...
// JUST DON'T DROP YOU FAVORITE TABLE HERE
// if required call onCreate():
onCreate(db);
}
So it's up to you whether to drop table or use it further.
More than that - you may decide what to do on the value of oldVersion and newVersion
Edit
If (according to your comments) you want to move data between databases you may look at this post
If you have copied your DB in app data or sd card thn it will remain even after updating data.*** if user is not clearing data manually from app settings
If no problem to download data from internet thn you by creating API
download data and update the data in required tables
Other if you dont wants to go with above one .. than create data file
for each table which you wants to update, keep it in assets folder
and after updating app when user starts app first time you can update
data except that fav table.eg Table wise json file with new data which you wants to update.
Hope I understand you question. and if not thn pls share it will try on tht
i have created table using Sqlite3.and also inserted record in that table.its running successfully.but now i have created another table in same database..and written class for it.
now data is not inserted in another table though i have created table in same database and written respective code for it n XML as well.
i have given toast message after inserting record.it showing me record inserted successfully at position -1..and showing error like this "table is not exists or file encrypted."
and also if i tried to create table using Sqlite cmd then table is created but i am not getting where that file get stored???
can anyone tell me how to solve this problem.?
if you want code then will put here..
Thanks in Advance---
It is probably because your application is still working with the old database. If you check for an existing database before creating it, then your app would find the old one and use that. But the old database doesn't have the new table you added.
You should include some sort of versioning of your database and check that when you create your database. Then if the existing database is not up to the latest version, you can overwrite it.
A quick solution is to completely uninstall your application from the phone/emulator and then reinstall/run it, then the database will be up to date.
https://addons.mozilla.org/en-US/firefox/addon/sqlite-manager/
use this software for create table..
you can create multiple table, edit data ,operation can perform in this software..
Android uses provides an easy method of database access through custom SQLiteOpenHelper classes. This lets you give your current database a version number which you can increment every time you make a change to a schema.
When you access a database for the first time, it will call the onCreate() method and allow you to construct the database.
When you access the database in the future the SQLiteOpenHelper will check the version of the installed database against the one defined in the project and will call it's onUpgrade() method that provides the old version number. This means that you can provide custom SQL commands to update each legacy version of the database to the current version individually.
If you're just developing, it's easy to increment the number and provide a method like this one.
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
+ newVersion + ", which will destroy all old data");
db.execSQL("DROP TABLE IF EXISTS notes");
onCreate(db);
}
Note, this is taken from the Android SDK Sample Notepad Tutorial which covers this topic.
I have implemented a BackupAgentHelper using the provided FileBackupHelper to backup and restore the native database I have. This is the database you typically use along with ContentProviders and which resides in /data/data/yourpackage/databases/.
One would think this is a common case. However the docs aren't clear on what to do: http://developer.android.com/guide/topics/data/backup.html. There is no BackupHelper specifically for these typical databases. Hence I used the FileBackupHelper, pointed it to my .db file in "/databases/", introduced locks around any db operation (such as db.insert) in my ContentProviders, and even tried creating the "/databases/" directory before onRestore() because it does not exist after install.
I have implemented a similar solution for the SharedPreferences successfully in a different app in the past. However when I test my new implementation in the emulator-2.2, I see a backup being performed to LocalTransport from the logs, as well as a restore being performed (and onRestore() called). Yet, the db file itself is never created.
Note that this is all after an install, and before first launch of the app, after the restore has been performed. Apart from that my test strategy was based on http://developer.android.com/guide/topics/data/backup.html#Testing.
Please also note I'm not talking about some sqlite database I manage myself, nor about backing up to SDcard, own server or elsewhere.
I did see a mention in the docs about databases advising to use a custom BackupAgent but it does not seem related:
However, you might want to extend
BackupAgent directly if you need to:
* Back up data in a database. If you have an SQLite database that you
want to restore when the user
re-installs your application, you need
to build a custom BackupAgent that
reads the appropriate data during a
backup operation, then create your
table and insert the data during a
restore operation.
Some clarity please.
If I really need to do it myself up to the SQL level, then I'm worried about the following topics:
Open databases and transactions. I have no idea how to close them from such a singleton class outside of my app's workflow.
How to notify the user that a backup is in progress and the database is locked. It might take a long time, so I might need to show a progress bar.
How to do the same on restore. As I understand, the restore might happen just when the user has already started using the app (and inputting data into the database). So you can't presume to just restore the backupped data in place (deleting the empty or old data). You'll have to somehow join it in, which for any non-trivial database is impossible due to the id's.
How to refresh the app after the restore is done without getting the user stuck at some - now - unreachable point.
Can I be sure the database has already been upgraded on backup or restore? Otherwise the expected schema might not match.
After revisiting my question, I was able to get it to work after looking at how ConnectBot does it. Thanks Kenny and Jeffrey!
It's actually as easy as adding:
FileBackupHelper hosts = new FileBackupHelper(this,
"../databases/" + HostDatabase.DB_NAME);
addHelper(HostDatabase.DB_NAME, hosts);
to your BackupAgentHelper.
The point I was missing was the fact that you'd have to use a relative path with "../databases/".
Still, this is by no means a perfect solution. The docs for FileBackupHelper mention for instance: "FileBackupHelper should be used only with small configuration files, not large binary files.", the latter being the case with SQLite databases.
I'd like to get more suggestions, insights into what is expected of us (what is the proper solution), and advice on how this might break.
Here's yet cleaner way to backup databases as files. No hardcoded paths.
class MyBackupAgent extends BackupAgentHelper{
private static final String DB_NAME = "my_db";
#Override
public void onCreate(){
FileBackupHelper dbs = new FileBackupHelper(this, DB_NAME);
addHelper("dbs", dbs);
}
#Override
public File getFilesDir(){
File path = getDatabasePath(DB_NAME);
return path.getParentFile();
}
}
Note: it overrides getFilesDir so that FileBackupHelper works in databases dir, not files dir.
Another hint: you may also use databaseList to get all your DB's and feed names from this list (without parent path) into FileBackupHelper. Then all app's DB's would be saved in backup.
A cleaner approach would be to create a custom BackupHelper:
public class DbBackupHelper extends FileBackupHelper {
public DbBackupHelper(Context ctx, String dbName) {
super(ctx, ctx.getDatabasePath(dbName).getAbsolutePath());
}
}
and then add it to BackupAgentHelper:
public void onCreate() {
addHelper(DATABASE, new DbBackupHelper(this, DB.FILE));
}
Using FileBackupHelper to backup/restore sqlite db raises some serious questions:
1. What happens if the app uses cursor retrieved from ContentProvider.query() and backup agent tries to override the whole file?
2. The link is a nice example of perfect (low entrophy ;) testing. You uninstall app, install it again and the backup is restored. However life can be brutal. Take a look at link. Let's imagine scenario when a user buys a new device. Since it doesn't have its own set, the backup agent uses other device's set. The app is installed and your backupHelper retrieves old file with db version schema lower than the current. SQLiteOpenHelper calls onDowngrade with the default implementation:
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
throw new SQLiteException("Can't downgrade database from version " +
oldVersion + " to " + newVersion);
}
No matter what the user does he/she can't use your app on the new device.
I'd suggest using ContentResolver to get data -> serialize (without _ids) for backup and deserialize -> insert data for restore.
Note: get/insert data is done through ContentResolver thus avoiding cuncurrency issues. Serializing is done in your backupAgent. If you do your own cursor<->object mapping serializing an item can be as simple as implementing Serializable with transient field _id on the class representing your entity.
I'd also use bulk insert i.e. ContentProviderOperation example and CursorLoader.setUpdateThrottle so that the app is not stuck with restarting loader on data change during backup restore process.
If you happen do be in a situation of a downgrade, you can choose either to abort restore data or restore and update ContentResolver with fields relevant to the downgraded version.
I agree that the subject is not easy, not well explained in docs and some questions still remain like bulk data size etc.
Hope this helps.
As of Android M, there is now a full-data backup/restore API available to apps. This new API includes an XML-based specification in the app manifest that lets the developer describe which files to back up in a direct semantic way: 'back up the database called "mydata.db"'. This new API is much easier for developers to use -- you don't have to keep track of diffs or request a backup pass explicitly, and the XML description of which files to back up means you often don't need to write any code at all.
(You can get involved even in a full-data backup/restore operation to get a callback when the restore happens, for example. It's flexible that way.)
See the Configuring Auto Backup for Apps section on developer.android.com for a description of how to use the new API.
One option will be to build it in application logic above the database. It actually screams for such levell I think.
Not sure if you are doing it already but most people (despite android content manager cursor approach) will introduce some ORM mapping - either custom or some orm-lite approach. And what I would rather do in this case is:
to make sure your application works
fine when the app/data is added in
the background with new data
added/removed while the application
already started
to make some
Java->protobuf or even simply java
serialization mapping and write your
own BackupHelper to read the data
from the stream and simply add it to
database....
So in this case rather than doing it on db level do it on application level.