Deleting the database when an Account is removed - android

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

Related

How to properly backup a database in case a user reinstalls or switches devices (Android)

My app tracks school grades, calculates averages, etc. and stores all of this in a SQLite database. If a user has to reinstall or gets a new phone, I'd like to be able to restore their data.
It looks like most developers do this either by backing up to SD card or by using Android Backup Service through Google. I'm not sure which is the better method. I'd like restoring to be simple but reliable. I welcome any comments on this.
One thing I'm trying to understand is why Google says to extend BackupAgent instead of BackupAgentHelper if using 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.
Why can't I just back up the database as a file and then restore the file? My SQLiteOpenHelper class already handles upgrades if db versions are different. I guess I could just abort on a downgrade.
Why can't I just back up the database as a file and then restore the
file? My SQLiteOpenHelper class already handles upgrades if db
versions are different. I guess I could just abort on a downgrade.
Reason: same database file may not work on different device models(even though most of the cases, it should work, there are cases where it will fail). It depends on parameters like page size etc set at sqlite engine level. Ideal way is to backup the data rather than copying the whole file
It's suggested that you avoid backing up the whole db file all the time mostly because that's a lot of redundant data traffic, especially if you've only changed one record in a large db. Being able to write per-record updates to the backup system is much more efficient (though of course is not nearly as simple to implement).

android: is it possible to check if db is created in other app

Is it possible to check if a db is created in other app? I have to have a common db for two app. If one app is already created the db then the second one should use the first apps' db (I mean should not create new one). For this,Is there a way to check whether that particular common db is created or not? Please help me on this.
This is not as easy as you may think. Even If you would definetly know, that your other app has created the db already, there is still difficulty in querying it. As any Db belongs to the app which created it, only this app usually gets access to it.
Good news is that there is is a mechanism for that, called ContentProvider. With it you are able to share the db-infos between apps, but it takes some effort to implement this.
The database is stored under /data/data/your.applications.package/databases. Normally this location could only be access by the user the Android OS created for the app. No other user is able to access this location unless the device is rooted. Then any user can access any location on the phone and manipulate the data.
so you can check the whether the db is present in this location or not
you can directly check this Android Access Another App's Database
The best thing for you is to use the sdcard (or some other user accessible area, without root)
Look at this thread
I know it's unsecure, but it's the best thing for this kind of problem. One application (the first one installed) creates the db on sdcard. The other one, just check's if the file (db) allready exists and reads from the existing db.
You should use SQLiteOpenHelper to control versioning of your database as described in database tutorial.
Unfortunately SQLiteOpenHelper only works in local app-directory but you can tell it to use a different directory on sd-card as described in sqliteopenhelper-with-fully-qualified-db-path-name
Try the following code
DB_PATH = "/data/data/THE_PACKAGE_NAME/databases/";
DB_NAME="YOUR_DB_NAME";
File dbFile = new File(DB_PATH + DB_NAME);
if(dbFile.exists()){
/* YOUR CODE HERE*/
}
Do the above check in both of your application with the PACKAGE NAME before creating the db.
:)

Android: SQLite Database. Do we need to create it?

I've been going through some tutorials on working with sqlite databases and they all seem to create a new database, tables, etc on the first run of the application. Is this necessary? What if I already have a pre-built database sitting in the assets folder when the application is installed? Can I not simply just open the connection to said database and start using its information or is there a specific reason everyone wants to create it using sql on first launch?
This question comes up frequently. Try this tutorial to use an existing database on Android:
http://www.reigndesign.com/blog/using-your-own-sqlite-database-in-android-applications/
You can't use database file which sits in assets folder directly as SQLite database, since this file would not be usual file located in common filesystem. E.g. you can have only readonly access to it. So the only your option is to copy those database from assets folder to device's filesystem.
To handle database creation for the first time and accessing it there's special helper class SQLiteOpenHelper. Read about it here. Specifically look in SQLiteOpenHelper.onCreate() - where should sit database creation (or copying from assets folder as in your case)
It's been quite sometime since I last worked with SQLite databases (in Android) but I believe that when they write CREATE statements, they always do so with the IF NOT EXISTS condition (i.e., CREATE (DATABASE|TABLE) IF NOT EXISTS...).
I don't know what you'll use SQLite for but I believe they do that in Android "just to make sure". That is, if it's the user's first time to run the app, the DB/Tables must be created first else app goes bonkers. Otherwise, they are (probably) created already and this case will be handled by the IF NOT EXISTS clause and they just go ahead and create a connection with the existing DB. Win-win.
(If, for some reason, it is not the user's first time to use the app and the DB isn't there, it will just be created again. But that's obvious isn't it? ;) )
If you just link with preexisting database it wont bind to your system. So there may be failures. Creating db at first run is the most appropriate way to work with db.

Android backup/restore: how to backup an internal database?

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.

Clearing Application database

I have an Android application which uses sqlite database to store textual data for a particular user. The data is read/written from/to the database from a service that runs periodically after every n seconds. My requirement is to clear data depending on age. Like, if the file is more than 24 hours old it should be deleted.
Checking the age of the file seems easy, just compare current time with the File creation time. The problem is:
where should I put this check and delete the file; inside application onCreate() or when the user logs in/ logs out? What would be an ideal place to trigger this logic from? Should I schedule an Alarm when the user logs in?
Should I delete the file or simply delete the rows? If I don't trigger the Cache clearance logic from login/logout, won't deleting the file cause problems, especially if the service is still trying to read and write from the database?
Please advice.
Thanks.
Well, this all depends on your logic for the application for the second part. I cant see why you would delete a database unless its just used to store temp data that does not matter. Either way the ideal place to do this check and delete is in the Data Access class thats opening the connection to the database. Below would be my logic...
Call to open DB
Check if DB file is old
If yes, delete it
Open Database (should create one if it does not exist)

Categories

Resources