In my current project's previous version we were using database for settings.
but for now we think that database is overhead for simple key value pairs
so we decided to use sharedpref for that.
Now the problem is how to handle to update functionality.
we are not using database so onUpgrade will not be work.
I am planing to do
ArrayList<String> databases = new ArrayList<String>(Arrays.asList(mContext.databaseList()));
if(databases.contains("dbname")){
copyDataToSharedPref();
mContext.deleteDatabase("dbname");
}
is there any simple way to handle this?
is there any simple way to handle this?
Not exactly but it isn't difficult. Just do something similar to the following...
When your main / launcher Activity starts get it to check SharedPreferences for a specific key, example - a boolean "update_complete".
If the key doesn't exist, there are two possibilities. The first is it's a clean (new) install, the second is it's an update.
If the key DOES exist then the app has already been updated and the database data has already been transferred to SharedPreferences so proceed to run the app.
If the key DOESN'T exist then check to see if the database exists. If the database DOESN'T exist then this is a new installation. In that case just setup SharedPreferences as a new installation.
If the key DOESN'T exist but the database DOES exist, transfer the data from the database into SharedPreferences, delete the database then put the "update_complete" boolean into SharedPreferences.
I think that covers it.
The Database entries will be there even after the App is updated (if user doesn't clear data). So in your situation I will:
check if there are is any database and its not empty.
if DB is there then read and create corresponding SharedPreferences.
delete the database using context.deleteDatabase(DATABASE_NAME);
I think that the Application class will be the best place to do so.
I am not sure if it is the best (or worst :P) method to do the same but I hope its useful.
Related
This is my first time working on a Xamarin App and I am new to the app development world so I need some help figuring out this process.
Currently I run a php web service that generates some SQL files that I run in DB Browser and I get a database file which I then put into my Assets and Resources Folder. Using each platform's API I copy the database into a writable folder and use that to run my queries.
I followed this really helpful tutorial and it worked perfectly fine.
https://medium.com/#hameedkunkanoor/creating-a-sqlite-databse-and-storing-your-data-in-your-android-and-ios-application-in-xamarin-2ebaa79cdff0 .
After the "initial" setup I store a timestamp in a local table and and the next time the user opens the app I pass that timestamp and retrieve data that is older than that timestamp. The I update that timestamp and continue the process. That data is sent back in JSON format and make the updates to the tables.
My only concern is if a new version were to come out where I add a new table or a new column which is not present in the current version of my Database, how should I take care of those update Web Service calls? Is there a way of monitoring my DB version? I read somewhere where I could just ignore the new data that is not present already, like table or columns, but I'm not really sure how to do that.
I also saw that if I call CreateTable on my current tables I could potentially update them?
Also for future reference each time I develop a new app would I need to regenerate a new database file to store in the assets/resources folder? Is there a more automated process for this? Along with monitoring the version of my database?
Any Help/Tutorials/Suggestions would be greatly appreciated.
You have to remember that CreateTable it's already doing the columns update for you, because internally it calls a method called MigrateTable which you can see here for further clarification: https://github.com/praeclarum/sqlite-net/blob/master/src/SQLite.cs#L562.
However you could have to handle more advanced modification to your database, like adding triggers or something similar.
In that case i suggest you to perform modifications manually.
In Xamarin Forms i've ended up with this:
https://gist.github.com/matpag/b2545cc22c8e22449cd7eaf6b4910396
Could not be the best strategy ever but seems to work for me.
Summarizing :
You have to save the database version in an internal flag of the SQlite database called user_version accessible with PRAGMA keyword.
Every time you get the database connection, you have to perform a check and see if the current database version is the same as the app last database version.
If not you need to perform a database update and set the new current version.
Reference here.
My app uses the SyncAdapter pattern, holding user credentials using the AccountManager and a ContentProvider to store data in a db.
When the account gets removed I can remove the db using the approach explained in this question. The db gets removed by doing:
boolean deleted = mContext.deleteDatabase(DatabaseHelper.DATABASE_NAME);
This works fine but when I do the login again everything is still there. It feels like the ContentProvider doesn't know that the db has been removed.
In this answer, inazaruk says:
You need to make sure you've killed the process that hosts
ContentProvider that uses that specific database file. And only than
delete it.
Killing the process to clear a db doesn't feel right.
Is there a better thing to do?
If I'd had to do that I would try it the following way:
add some Uri that when you insert or delete using that Uri triggers database deletion inside your ContentProvider. When deleting also clear all references to the SQLiteDatabase since it is possible that you can still access the old database file through that (If you delete a file in Linux and you have that file open you can still use it - it's just no longer accessible via the path).
By putting the deletion inside the ContentProvider you should be able to close the database connection and track the deletion state in a way that you know that you need to recreate the database file.
ContentProviders don't quit unless you kill your app so you probably have the same instance running and probably references to the old file as mentioned above
Thanks for previous replies,
is it possible to delete the stored content from sqlite once re-install the application. I am storing data in database, once i re-install the same app again, the previous data still stores in sqlite. i want to delete the stored content while re install the app. i am not sure about this.
This seems like a hack and maybe not the actual answer, but can you -- with each new version of your app -- increment an identifier (from 10 to 11) in the code and then check against a stored preferences containing that identifier. If you have a constant in your code that is higher than the identifier stored on the previous device, then you can clear the database to whatever you think its state should be. Then with each new released version of the app you increment this number..
Edit: In API level 9 and higher, you can -- whenever your app starts up -- write the date in which the app was installed (see here for an explanation on how to find the install date). If you check that it was installed after the date which is written, kill the data!
Thanks for all replies,
I maintain the Version code for all the builds, once i re-install the application, i check the version, if i found the version changes, i simply remove the DB. this case only applicable for overriding the install of APk(ie without un-installing the application). When we made a uninstall, the internal data and sqlite for the application ill automatically deleted.
sqlite databases are stored as files on your file system, to delete the data. You just need to delete the file.
What you'd want to do is setup some way of detecting if the app is being run for the first time, if this is the first time the app has been run then check the database exists, if it does delete it. Then recreate the database as empty.
Or you could go through and remove all the data in each table/drop each table in the database on the first run if the database exists.
Read here : Detect Android app upgrade and set Application class boolean for show/hide of EULA
Create a UpgradeBroadcastReceiver of your own that will run the delete instructions you want and register it in your manifest file.
When you delete the app, then the database should be deleted too. Unless you go to some trouble to keep it. If you simply update the app, then the data should be kept. If you need to delete the data upon reinstall, try this:
Every time you start an Activity, call PackageManager.getPackageInfo() and check lastUpdateTime. Compare it with a time stamp that you store in the database or a shared preference. If lastUpdateTime is newer, delete the your database.
application SharedPreferance is deleted on un-installing the app, so save a boolean to determine if the application is running for the first time or not.
My question is simple :
I have a "flag" stored in SharredPreference but when the user is doing an application update, this data is ereased. Is there a way to keep data stored even if the user is doing an application update ?
Well, as far as I know you have only two choices - write it in a file or save it in the database. I always put the data in the db, from witch they are loaded at program start. Just have a table with the prefs in it and you are sure, that your data is not deleted, even after updating. The database interface is easy to use and lean - therefor I use it.
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.