There are a few questions here on stackoverflow where people use BackupAgents to synchronize the apps data with googles cloud (see here). In my specific case the requirements are much more restrictive due to the nature of the more or less sensitive data. Everything must only be stored on the device itself and the app must not connect to the internet.
The main Activity contains a ViewPager which hosts a few ListFragments. Each ListFragment has its own Loader which swaps the cursor in a SimpleCursorAdapter class.
Before both backing up and restoring the database I destroy the loaders with:
getLoaderManager().destroy(LOADER_ID);
My backup function is similar to this anwser:
final String inFileName = "/data/data/<your.app.package>/databases/foo.db";
File dbFile = new File(inFileName);
FileInputStream fis = new FileInputStream(dbFile);
String outFileName = Environment.getExternalStorageDirectory()+"/database_copy.db";
// Open the empty db as the output stream
OutputStream output = new FileOutputStream(outFileName);
// Transfer bytes from the inputfile to the outputfile
byte[] buffer = new byte[1024];
int length;
while ((length = fis.read(buffer))>0){
output.write(buffer, 0, length);
}
// Close the streams
output.flush();
output.close();
fis.close();
The restore function is similar in that it copies the database from the sdcard back into the internal app folder, stops the loaders and overwrites the database file.
public static void restore(Context context) throws IOException {
if (externalStorageIsWriteable()) {
// getBackupDir() is a path to the folder on the sdcard
File fileBackup = new File(getBackupDir(), WineSQLiteHelper.DB_NAME);
if (!fileBackup.exists()) {
throw new IOException("File not found");
}
File importFile = getImportDatabase();
try {
FileUtils.copyFile(fileBackup, importFile);
MySQLiteHelper dbHelper = new MySQLiteHelper(context);
dbHelper.close();
dbHelper = null;
File file = new File(Environment.getDataDirectory() + "/data/"
+ PACKAGE + "/databases/" + WineSQLiteHelper.DB_NAME);
FileUtils.copyFile(importFile, file);
// Remove temporary import file.
importFile.delete();
} catch (IOException e) {
throw e;
}
} else {
throw new IOException("External Storage not writeable");
}
}
But somehow the MainActivity gets recreated after I've overwritten the database file and I recieve a few
SQLiteException: no such column
My guess is that perhaps there are still open connections to the database, but I'm not sure about that since this is the first time I have to work with databases this closely.
How to properly close all database connections of a ContentProvider? I can't find anything in the documentation about this. And is this necessary?
How do I properly restore the database?
Related
I'm having a problem with deleting the SQLite file from my application.
I'm using the code as follows:
File file = new File("data/data/" + context.getPackageName() + "/databases/*****");
d = file.delete();
DbAdapter dba = new DbAdapter(context);
dba.open();
dba.close();
Before this, I was using the method context.deleteDatabase(DATABASE_NAME) to do this.
The problem is: after I run this method, the application does not refresh, I have the old database and I am able to do deletes, inserts, etc.
On both cases I have to restart my application to see the new database.
Sometimes, after to run the method, when I try to access some methods that need the database, the app crashes with an error that I have no database and after restart, the new database is created.
What do I do?
EDIT
I have a big system. My database have 26 tables and a lot of constraingns.
It's easier to delete the database and recreate it.
EDIT2
DbAdapter dba = new DbAdapter(context);
SQLiteDatabase sqlite = dba.open();
dba.close();
boolean d = context.deleteDatabase("CarroramaBD");
dba.open();
dba.close();
Im did close the db.
Why doing this
instead you can drop the tables in your db and recreate them which is far more practical than deleting the file.
And as for your views that store old values you can refresh the whole activity by calling this code
public void refrehs_me()
{
Intent intent = getIntent();
finish();
startActivity(intent);
}
Edit : Just a thought passed my mind regarding the creation of the new DB, On the creation of your app a DB file will be created, you make a copy of that file like DB2, and when you delete the DB instead of trying to create a new one just copy back DB2 as DB and so on with each deletion process.
And for the copy - here is the code by #Rakshi
public void copy(File src, File dst) throws IOException {
InputStream in = new FileInputStream(src);
OutputStream out = new FileOutputStream(dst);
// Transfer bytes from in to out
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
in.close();
out.close();
}
I am copying a database file from my assets folder to the databases folder on install. I have a shared preference named firstRun with a default value of true. If this is true then I copy the database file and set the firstRun value to false. Immediately following this process I then query a database table for some data. On an older Android version (2.1) an SQLite Exception occurs (no such table) and the application crashes, on Android 4.2.1 the dbHelper logs the same message but continues to run and returns the values from the table it just failed to find. With the earlier Android version, if I launch the application again, the database table is found and all operations proceed normally. After the application crashes I can inspect the copied database and the table and rows are present. This does seem to be different from other issues where tables genuinely don't exist as I can see that they do. I wonder if it's some kind of synchronisation issue where the table doesn't exist immediately after the copy process, but does at some point when a process has finished. To my knowledge this is not done asynchronously so I'm not sure why.
Here is some code to show the problem:
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
boolean firstRun = prefs.getBoolean(getString(R.string.first_time_run), true);
if (firstRun) {
SharedPreferences.Editor edit = prefs.edit();
edit.putBoolean(getString(R.string.first_time_run), Boolean.FALSE);
edit.commit();
try {
dbHelper.createDatabase();
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
// This method will fire an exception the first time the app is run - no such table
Bundle metaData = dbHelper.fetchAppMetaData();
this.appTitle = metaData.getString("app_title");
this.normalId = Integer.parseInt(metaData.getString("normal_id"));
The fetchAppMetaData method is a basic sqlite.query:
Bundle metaData = new Bundle();
Cursor dbCursor = null;
SQLiteDatabase database = getReadableDatabase();
String[] cols = new String[] {"app_title", "normal_id"};
dbCursor = database.query(true, TBL_METADATA, cols, null, null, null, null, null, null);
if (dbCursor != null) {
dbCursor.moveToFirst();
which would eventually return a bundle.
The database creation method is:
//Open the local db as the input stream
InputStream dbFromAssets = myContext.getAssets().open(DB_NAME);
// Path to the just created empty db
String outFileName = DB_PATH + DB_NAME;
// Check that the directory now exists
File f = new File(DB_PATH);
if (!f.exists()) {
f.mkdir();
}
// Open the empty db as the output stream
OutputStream appDatabase = new FileOutputStream(outFileName);
// Transfer bytes from the inputfile to the outputfile
byte[] buffer = new byte[1024];
int length;
while ((length = dbFromAssets.read(buffer)) > 0){
appDatabase.write(buffer, 0, length);
}
// Close the streams - don't cross them!
appDatabase.flush();
appDatabase.close();
dbFromAssets.close();
Would be grateful for any ideas please.
Below is a cut and paste from working code. I use this on the launch of the MainActivity each time the application loads. Tested and working with versions 2.3 - 4.2:
Here is the code I'm using that does the check:
try
{
String destPath = "/data/data/" + getPackageName() + "/databases/(...your db...)";
File f = new File(destPath);
File c = new File("/data/data/" + getPackageName() + "/databases/");
// ---if directory doesn't exist then create it---
if (!c.exists())
{
c.mkdir();
}
// ---if db file doesn't exist then create it---
if (!f.exists())
{
CopyDB(getBaseContext().getAssets().open("...name of db from assets foleder..."), new FileOutputStream(destPath));
}
}
catch (FileNotFoundException e)
{
e.printStackTrace();
}
catch (IOException e)
{
e.printStackTrace();
}
Here is the code I'm using that does the copying if not found:
public void CopyDB(InputStream inputStream, OutputStream outputStream) throws IOException
{
//---copy 1K bytes at a time---
byte[] buffer = new byte[1024];
int length;
while ((length = inputStream.read(buffer)) > 0)
{
outputStream.write(buffer, 0, length);
}
inputStream.close();
outputStream.close();
}
Hope this is hopeful...
The solution for me was to close the dbHelper after the database is created and before I try to use it again.
For example:
try {
dbHelper.createDatabase();
} catch (Exception e) {
}
dbHelper.close();
dbHelper.fetchAppMetaData();
Hope this helps someone else.
I have an SQLite database in my application. When I go to check if the database is created or not in File Explorer, there is no any database file within the data folder. It didn't even show up using the adb shell command. Does this mean that the database hasn't been created yet? Any suggestions on how to solve this?
If you are using an actual device, you will not be able to see or access it from outside unless you root your phone.
If you are using the emulator, the DDMS view will let you navigate to it and pull it from there.
Or, you could have an issue with creating your DB. Without seeing your code we cannot tell.
EDIT
If you want to get the file off of a real device, you'll need to implement a method to copy it from your data directory to someplace where you do have access (such as your SD card). This would do the trick:
public void backup() {
try {
File sdcard = Environment.getExternalStorageDirectory();
File outputFile = new File(sdcard,
"YourDB.bak");
if (!outputFile.exists())
outputFile.createNewFile();
File data = Environment.getDataDirectory();
File inputFile = new File(data, "data/your.package.name/databases/yourDB");
InputStream input = new FileInputStream(inputFile);
OutputStream output = new FileOutputStream(outputFile);
byte[] buffer = new byte[1024];
int length;
while ((length = input.read(buffer)) > 0) {
output.write(buffer, 0, length);
}
output.flush();
output.close();
input.close();
} catch (IOException e) {
e.printStackTrace();
throw new Error("Copying Failed");
}
}
I had created database in my android app, then inserted a statement. Everything worked, so i wanted to get my database fro MY_PACKAGE/databses/ and copy it to sd card to be reachable.
This worked, but when i trying to open with my sqlite Firefox plugin i get this error:
SQLiteManager: Error in opening file Datas.sqlite - either the file is encrypted or corrupt
Exception Name: NS_ERROR_FILE_CORRUPTED
Exception Message: Component returned failure code: 0x8052000b (NS_ERROR_FILE_CORRUPTED) [mozIStorageService.openUnsharedDatabase]
Maybe i have to open with something else or i can't open this so easily ?
I will give all the code i used:
Handling my db i used all this code:
Using your own SQLite database in Android applications
Copying it to sd card this method:
public static boolean backUpDataBase(Context context) {
final String DATABASE_NAME = "Data.sqlite";
final String DATABASE_NAME_FULL = "/data/data/package/databases/"
+ DATABASE_NAME;
boolean result = true;
// Source path in the application database folder
String appDbPath = DATABASE_NAME_FULL;
// Destination Path to the sdcard app folder
String sdFolder = Environment.getExternalStorageDirectory()
.getAbsolutePath() + "/" + "Datas.sqlite";
File f = new File(sdFolder);
// if (!f.exists()) {
// f.mkdir();
// }
InputStream myInput = null;
OutputStream myOutput = null;
try {
// Open your local db as the input stream
myInput = new FileInputStream(appDbPath);
// Open the empty db as the output stream
myOutput = new FileOutputStream(f);
// transfer bytes from the inputfile to the outputfile
byte[] buffer = new byte[1024];
int length;
while ((length = myInput.read(buffer)) > 0) {
myOutput.write(buffer, 0, length);
}
} catch (IOException e) {
result = false;
e.printStackTrace();
} finally {
try {
// Close the streams
if (myOutput != null) {
myOutput.flush();
myOutput.close();
}
if (myInput != null) {
myInput.close();
}
} catch (IOException e) {
}
}
return result;
}
My database looks like this:
2 tables:
CREATE TABLE "Test" ("_id" INTEGER PRIMARY KEY NOT NULL UNIQUE , "Info" TEXT)
CREATE TABLE "android_metadata" ("locale" TEXT DEFAULT 'en_US')
And code to do all i need:
//return databse which is read and write
DataBaseHelper dataBase= Main.createOrOpenDB(mContext);
Main.backUpDataBase(mContext);
db = dataBase.myDataBase;
// Step 1: Inflate layout
setContentView(R.layout.tabs_fragment_activity);
try{
db.execSQL("INSERT INTO " +"Test" +" Values ('1','Inserted');");
}catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
So where is wrong, as insert works fine?
It sounds like there is a problem in your code to write it to the SD card (which I'm not seeing immediately).
I wonder, why are you copying it to the SDCard? It sounds like you merely want to check the file...
If that's actually your goal, then I would recommend running the emulator and simply using the DDMS view from eclipse, navigate to the file and click the button in the upper right corner whose tool-tip says "Pull a file from the device". What you get in the emulator should be exactly what you get on your phone.
try to use SQLiteOpenHelper | Android Developers
I'm currently writing a phonebook-type application for the Android OS. I'm currently using a SQLiteDatabase to hold my contact information. I know that the database is held in
data/data/package-name/databases
However, I'd need root access to be able to find that directory on an Android phone. Is there a way to put the database somewhere I can find it without having to root the phone?
Just copy your DB to SD card programatically.
This code is an example how to access your DB =)
try {
input = new FileInputStream("/data/data/my_package_name/databases/" + DATABASE_NAME);
File dir = new File("/sdcard/database_dump");
dir.mkdir();
OutputStream output = new FileOutputStream("/sdcard/debugdump/myDb.db");
byte[] buffer = new byte[1024];
int length;
while ((length = input.read(buffer))>0) {
output.write(buffer, 0, length);
}
output.flush();
output.close();
input.close();
} catch (Exception e) {
e.printStackTrace();
}
After your DB is copied to the sd card you can take it and work with it on your PC using whatever SQLite editor you like. For example: http://www.softpedia.com/get/Internet/Servers/Database-Utils/SqlPro.shtml