save sqlite database android after application overwrite - android

My question is that I want to restore my application's sqlite database after I overwrite the same app on my device. I don't like to add settings again and again on start up of my app.
So is it possible in android to save the database somewhere from which I can again restore it ?
I have searched for more than hours on Google and SO but couldnt ind any solution.
EDIT: Its not a fixed database. So I can't store it in Assets Folder. It is editable by user but by default it should carry the last edited values(values before the app overwrite).

This method I find very helpful:
public static void movedb(File srcdb, File destdb)
{
try
{
if (Environment.getExternalStorageDirectory().canWrite())
{
if (srcdb.exists())
{
FileChannel src = new FileInputStream(srcdb).getChannel();
FileChannel dst = new FileOutputStream(destdb).getChannel();
dst.transferFrom(src, 0, src.size());
src.close();
dst.close();
}
else
{
//ERROR: "Database file references are incorrect"
}
}
else
{
//ERROR: "Cannot write to file"
}
}
catch (Exception e)
{
//ERROR: e.getMessage()
}
}
Then I just call
movedb(this, new File(<context>.getDatabasePath("...your DB name...")), new File("... your location ..."));
To back up, and then to restore:
movedb(this, new File("... your location ..."), new File(<context>.getDatabasePath("...your DB name...")));

I'm using ORMLite and, apart from storing the database in the external public directory, after I restore the file to the database directory, I have to re-instantiate the DatabaseHelper singleton and create a new one.
Here is my version, omitting every try/catch block for the sake of simplicitiy:
public boolean restoreBackup(Context context){
String databasePath = "data/data/my.package.name/databases/myDatabase.sqlite";
String backUpPath = context.getDatabaseDir("myDatabase.sqlite");
// Copies back-up to database directory
new File(databasePath).delete();
FileInputStream streemToBackUp = new FileInputStream(new File(backUpPath));
OutputStream streamToDatabaseFile = new FileOutputStream(databasePath);
byte[] buffer = new byte[1024];
int length;
while ((length = streamToBackUp.read(buffer)) > 0) {
streamToDatabaseFile.write(buffer, 0, length);
}
streamToDatabaseFile.flush();
streamToDatabaseFile.close();
streamToBackUp.close();
// Re-instantiate DatabasHelper singleton
DatabaseHelper.closeHelper();
}
The body of closeHelper() is as follows:
public static void closeHelper() {
helper.close();
}
#Override
public void close() {
super.close();
myDao = null; // Set to null every day you have
helper = null; // Set to null the singleton instance of the helper
}
This will work as long as you don't use OpenHelperManager class to instantiate the helper, and always use getHelper() whenever you need the database instead of storing the instance returned.

Related

Crash after attempting to restore realm

I'm attempting to implement a backup/restore functionality for my android application using Realm. After I restore the data from the backup, if I try to access any information (that would request data from realm), the app crashes A/libc: Fatal signal 7 (SIGBUS), code 2, fault addr 0x9694d000 in tid 5047 (...), pid 5047 (...)
Keep in mind that the after reopening the app, data is restored (so backup/restore works as expected apart from the crash)
This is my RealmBackup class
public class RealmBackup {
private final static String TAG = RealmBackup.class.getName();
private Context context;
private Realm realm;
public RealmBackup(Context context) {
this.realm = Realm.getInstance(Realm.getDefaultConfiguration());
this.context = context;
}
public void backup() {
File exportRealmFile = null;
File exportRealmPATH = context.getExternalFilesDir(null);
String exportRealmFileName = "default.realm";
// create a backup file
exportRealmFile = new File(exportRealmPATH, exportRealmFileName);
// if backup file already exists, delete it
exportRealmFile.delete();
// copy current realm to backup file
realm.writeCopyTo(exportRealmFile);
String msg = "File exported to Path: " + context.getExternalFilesDir(null);
realm.close();
}
public void restore() {
//Restore
File exportRealmPATH = context.getExternalFilesDir(null);
String FileName = "default.realm";
String restoreFilePath = context.getExternalFilesDir(null) + "/" + FileName;
copyBundledRealmFile(restoreFilePath, FileName);
}
private void copyBundledRealmFile(String oldFilePath, String outFileName) {
try {
File file = new File(context.getFilesDir(), outFileName);
FileOutputStream outputStream = new FileOutputStream(file);
FileInputStream inputStream = new FileInputStream(new File(oldFilePath));
byte[] buf = new byte[1024];
int bytesRead;
while ((bytesRead = inputStream.read(buf)) > 0) {
outputStream.write(buf, 0, bytesRead);
}
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private String dbPath() {
return realm.getPath();
}
}
And whenever I'm trying to backup/restore the data I'm simply calling realmBackup.backup() or restore. The problem, I believe is that the realm must be reopened after the restore occured with the new data, however, I'm unsure how to do that, I've tried using whether
Realm.init(this);
RealmConfiguration config = new RealmConfiguration
.Builder()
.deleteRealmIfMigrationNeeded()
.build();
Realm.setDefaultConfiguration(config);
or simply realm = Realm.getDefaultInstance(); but that didn't quite work.
You can't just delete and overwrite an open Realm as "restore", you must ensure that Realm.getGlobalInstanceCount(Realm.getDefaultConfiguration()) == 0 first.
To do that, you must call realm.close() on the instances that are opened.
Also, if the app was updated with a new schema, your backup would be deleted by deleteIfMigrationNeeded, so look out for that
Clean the project, uninstall and reinstall the app.

android file system persistance

Hi Iam having serious issues try to persist some serializable objects to a file on the local android file system. Iam getting a Bad file descriptor error and I think it is to do with my methods for creating the file. the file and checking if the file exists. i create a private file object in the class. Then, on write or read. I check file existance with the following code.
#Override
public boolean fileExists() {
File file = context.getFileStreamPath(filename);
return file.exists();
}
this doesnt instantiate my file object called "objectfile"!! but does check the "filename" exists.
to create the file I call this method if "filename" doesnt exist.
public void createFile()
{
objectfile = new File(context.getFilesDir(), filename);
objectfile.setReadable(true);
objectfile.setWritable(true);
}
Iam not sure if this will give me back my previously created file which would be ideally what I want to do. Is there a way i can just get the old file or create a new one and pass it to "objectfile" variable in the constructor??
Iam also wondering what the best way to do this is??
Or should i just use the mysqlite db? using object file persistance doesn't seem to be working out for me right now and iam working to a deadline. Also this method is mention in the gooogle docs so I thought it would be legit was to do it.
http://developer.android.com/training/basics/data-storage/files.html
here is my method for reading the serializable objects
public synchronized ArrayList<RoomItem> readObjects() {
final ArrayList<RoomItem> readlist = new ArrayList<>();
if(!fileExists())
return readlist;
if(objectfile == null)
createFile();
try {
finputstream = new FileInputStream(objectfile);
instream = new ObjectInputStream(finputstream);
readwritethread = new Thread(new Runnable() {
#Override
public void run() {
try {
final ArrayList<RoomItem> readitems = (ArrayList<RoomItem>) instream.readObject();
instream.close();
finputstream.close();
handler.post(new Runnable() {
#Override
public void run() {
listener.updateList(readitems);
}
});
} catch(IOException e)
{
e.printStackTrace();
}
catch(ClassNotFoundException e)
{
e.printStackTrace();
Log.d("read failed", "file read failed");
}
}
});
}
catch(Exception e)
{
e.printStackTrace();
}
timeOutReadWrite(readwritethread);
readwritethread.start();
try {
readwritethread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.d("read from file", "file read");
return readlist;
if anyone could suggest any improvements id really appreciate it. I use a handler to pass back to my activity and implement a listener interface on my activity thats call the activity when all the obj are read. Thanks again!
1#: Yes, it will return the original file you created.
2#: Depends on the thing you want to store, seems File is more flex from description
hope helpful.
We have used
FileOutputStream fos = context.openFileOutput("file.ser", Context.MODE_PRIVATE);
to write our serialized files.This will carete files in /data/data/app.package.name/files/. In fact, this path is returned by getFilesDir().
And while deserializing, use
//make sure you pass the same file that was passed to openFileOutput()..
FileInputStream fis = context.openFileInput("file.ser");
Also, to avoid confusing between file names you can use name of class that is being serialized.
Ex:
public static <T> void serialize(final Context context, final T objectToSerialize) {
....
....
Strin fileName = objectToSerialize.getClass().getSimpleName();
...
}
Do this and keep the method in util so it can be used for any type of objects (T type) to serialize.

Replacing database with another database at runtime.

I have two databases, one database is the primary. This primary DB is responsible for holding the current data which is up to date and my secondary DB is populated via a cron job, once the primary DB gets obsolete I want to replace it with the secondary DB via a file operation of just over writing the existing DB and refreshing my views. Is it possible to do this, is there a better way?
So far what I have done is:
public void writeToSD() throws IOException {
File f=new File("/mnt/sdcard/dump.db");
FileInputStream fis=null;
FileOutputStream fos=null;
try{
fis=new FileInputStream(f);
fos=new FileOutputStream("/data/data/com.one.two/databases/Bdr");
while(true){
int i=fis.read();
if(i!=-1){
fos.write(i);
}
else{
break;
}
}
fos.flush();
}
catch(Exception e){
e.printStackTrace();
}
finally{
try{
fos.close();
fis.close();
}
catch(IOException ioe){
System.out.println(ioe);
}
}
How about always using the same database files (let's say dbA, dbB) with two instances of SQLiteOpenHelper and using an utility class like this instead of using raw SQLiteOpenHelper:
class Db {
private SQLiteOpenHelper mPrimaryDb;
private SQLiteOpenHelper mSecondaryDb;
public Db(Context context) {
mPrimaryDb = new MyDbHelper(context, "db_a");
mSecondaryDb = new MyDbHelper(context, "db_b");
}
public SQLiteOpenHelper getPrimaryDb() {
return mPrimaryDb;
}
public SQLiteOpenHelper getSecondaryDb() {
return mSecondaryDb;
}
public void swapDb() {
SQLiteOpenHelper tmp = mPrimaryDb;
mPrimaryDb = mSecondaryDb;
mSecondaryDb = tmp;
// TODO: notify data users that data has changed, cleanup the old primary database, etc.
}
{
If you want to use file operations, renaming the data base files is faster. But during file operations all connections have to be closed before any action.
If insertion is too slow, I would not overwrite the database file. I would generate the new database with a temp name and the same table and view structure. After finishing writing to the temp file I would rename the file to the same name as the invariant part of the old database plus a version number or a timestamp . And in my application I would look periodically for a new version, if found I would close all connections to the old file and open the new database.

android database returning no records but sqlitebrowser contains data

I have a problem with a database file not being read
I have added the database file in assets called mydb but when i run my code it says its not being located. It is calling this toast Toast.makeText(this, "No contact found", Toast.LENGTH_LONG).show(); This is being called because no records are being returned. Also I know it is finding the file as there is no FileNotFoundException exception. This is an example form Android Application Development book.
public class DatabaseActivity extends Activity {
/** Called when the activity is first created. */
TextView quest, response1, response2;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
TextView quest = (TextView) findViewById(R.id.quest);
try {
String destPath = "/data/data/" + getPackageName() + "/databases/MyDB";
File f = new File(destPath);
if (!f.exists()) {
CopyDB( getBaseContext().getAssets().open("mydb"),
new FileOutputStream(destPath));
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
DBAdapter db = new DBAdapter(this);
//---get a contact---
db.open();
Cursor c = db.getContact(2);
if (c.moveToFirst())
DisplayContact(c);
else
Toast.makeText(this, "No contact found", Toast.LENGTH_LONG).show();
db.close();
}
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();
}
public void DisplayContact(Cursor c)
{
quest.setText(String.valueOf(c.getString(1)));
//quest.setText(String.valueOf("this is a text string"));
}
}
Is there a better way to upload data.
A couple of things come to mind here...
because of the !f.exists() check, then once the database exists (and maybe empty) then it will never copy it again. So maybe for now, copy it all the time, until you work out kinks and then add in the !f.exists()
I've had mixed results with e.printStackTrace(), maybe change to Log.e(TAG, "message", e) and see if you start seeing errors showing up in LogCat
As for a better way... I've done this a couple different ways...
1. Is to create a file (json, cvs, etc) and then process and load it, if the database is empty
2. Similar to the first, except that I create a java serialized object array and load it to the database, if the database is empty.
Also I don't know what DBAdapter looks like, and since it wraps the database the issue may be there.

Performance SQLite Issue - Can a codechange speed up things?

I use the following code to add rows to my database :
public void insert(String kern, String woord) {
SQLiteDatabase db = getWritableDatabase();
ContentValues values = new ContentValues();
values.put(KERN, kern);
values.put(WOORD, woord);
db.insertOrThrow(TABLE_NAME, null, values);
return;
Currently, I'm invoking this insert() 3.455 times, to add all words to the database, using : insert("Fruits", "Banana"); It takes forever.
How can I change this code to work faster? I'm thinking in the line of foreach, but don't know how to implement.. Thanks!
/Edit; The solution provided by #hovanessyan works and will do the job. AND.. note that if you have a lot of lines that have to be put in, you might be confronted with the method exceeds max byte limit error. In that case, review the other solution, that suggests packing the database in the actual .APK file.
You can wrap-up those inserts into transaction.
db.beginTransaction();
try {
// do all the inserts here
//method call here, that does 1 insert; For example
addOneEntry(kern,woord);
...
db.setTransactionSuccessful();
} catch (SQLException e) {
//catch exceptions
} finally {
db.endTransaction();
}
private void addOneEntry(String kern, String woord) {
//prepare ContentValues
//do Insert
}
You can use bulkInsert:
ContentValues[] cvArr = new ContentValues[rows.size()];
int i = 0;
for (MyObject row : rows) {
ContentValues values = new ContentValues();
values.put(KERN, myObject.getKern());
values.put(WOORD, myObject.getWoord);
cvArr[i++] = values;
}// end for
resolver.bulkInsert(Tasks.CONTENT_URI, cvArr);
Using the tips of both hovanessyan and Damian (remind me to rep+1 you as soon as I reach 15 ;), I came up with the following solution:
For relatively small databases (<1,5Mb)
I created the database using SQLite Database Browser, and put it in my Assets folder.
Then, the following code copies the database to the device, if it's not already there:
boolean initialiseDatabase = (new File(DB_DESTINATION)).exists();
public void copyDB() throws IOException{
final String DB_DESTINATION = "/data/data/happyworx.nl.Flitswoorden/databases/WoordData.db";
// Check if the database exists before copying
Log.d("Database exist", "" + initialiseDatabase);
Log.d("Base Context", "" + getBaseContext());
if (initialiseDatabase == false) {
// Open the .db file in your assets directory
InputStream is = getBaseContext().getAssets().open("WoordData.db");
// Copy the database into the destination
OutputStream os = new FileOutputStream(DB_DESTINATION);
byte[] buffer = new byte[1024];
int length;
while ((length = is.read(buffer)) > 0){
os.write(buffer, 0, length);
}
os.flush();
os.close();
is.close();
}}
In my app, a portion of the database is User-customizable.
I call the code above in onStart() with :
try {
copyDB();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
So, when the user presses "reset database to standard" (in preferences screen), I just set the Boolean initialiseDatabase to "false" and wait for the user to go back to the main activity. (thus calling onstart and copying the original database).
I tried to call the Activity.copyDB() from the preferences.java. It's neater, because it doesn't require the user to go back to the main activity to rebuild the database. However, I get an error about not being able to call static references to non-static methods. I don't understand that, but will look into it.

Categories

Resources