7

guys, I have the problem when copying database from local assets folder to /data/data/package_name/databases directory. As I use the http://www.reigndesign.com/blog/using-your-own-sqlite-database-in-android-applications/ tutorial to do it, I can only get an empty file.

I quoted the part of copyDataBase() method and there is no difference. Every time the app start, it will create the directory and empty database. So is there any way to make the copyDataBase() work?

Thank you very much!!

OMG Ponies
  • 325,700
  • 82
  • 523
  • 502
4af2e9eb6
  • 682
  • 3
  • 7
  • 20
  • How large is your database? Possibly along the lines of http://stackoverflow.com/questions/5789177/i-get-this-error-data-exceeds-uncompress-data-max-on-android-2-2-but-not-on-2-3 – olextraplus Jul 28 '11 at 22:42

5 Answers5

15

Why wouldn't you copy from assets? It's perfectly normal to do so. But you can't do it in the onCreate, at that point an empty database is already created. You need to do it prior. I usually do it in an override of getWriteableDatabase, something like

public synchronized SQLiteDatabase getWritableDatabase() {
    SQLiteDatabase db = null;

    if (!doesDatabaseExist()) {
        try {
            copyDatabase();
            db = super.getWritableDatabase();
        } catch(Exception ex) {
            Log.e("Database Log", getDatabasePath() + " failed to copy correctly. " + ex.getLocalizedMessage());
        }
    }
    else {
        db = super.getWritableDatabase();
    }

    return db;
}
Mahendra Liya
  • 12,912
  • 14
  • 88
  • 114
  • Works for me (Android 2.3.3). Clean and solid. – Horst Dehmer Aug 18 '11 at 23:16
  • 3
    I had to change it a little: `if (!doesDatabaseExist()) { try { SQLiteDatabase db = super.getReadableDatabase(); db.close(); } catch (Exception ignore) {} copyFromAssets(); return super.getReadableDatabase(); }` Otherwise it was crashing with FileNotFoundException, probably because database directory didnt exist. (Android 2.2) – Alexey Sep 20 '11 at 09:41
1

I don't know if it is still usefull but here is the solution for others that get here to see the awnser. The code you used, works for most phones, some older phones have different behaviour with the getReadableDatabase() function. Your problem therefore is not in the copyDataBase function but in the createDataBase function.

in createDataBase() there is the following check;

this.getReadableDatabase();

This checks if there is already a database with the provided name and if not creates an empty database such that it can be overwritten with the one in the assets folder. On newer devices this works flawlessly but there are some devices on which this doesn't work. Mainly older devices. I do not know exactly why, but it seems like the getReadableDatabase() function not only gets the database but also opens it. If you then copy the database from the assets folder over it, it still has the pointer to an empty database and you will get table does not exist errors.

So in order to make it work on all devices you should modify it to the following lines:

SQLiteDatabase db = this.getReadableDatabase();
if (db.isOpen()){
    db.close();
}

Even if the database is opened in the check, it is closed thereafter and it will not give you any more trouble.

Tom Groentjes
  • 1,053
  • 8
  • 7
1

I wouldn't copy any database form the assets-folder. If you need some standard entry's in your Database, you can add them using INSERTs in your onCreate()-method.


Update: Since this is getting down-voted for being wrong (which is kinda right) and I can't delete it, here is a little update.

I'd say it depends upon how many standard entries you want to add to your database. If it's just one or two, shipping a packed DB might not be worth it.

Anyways, some apps come with rather large databases (for example, a recipe collection). You can obviously not add all these in code.

  • For small test-entries, I'd still prefer simply adding them in onCreate().
  • For bigger databases, you should pre-populate them and ship em along with your app.

For the later to work, you'll need to copy the database file from assets/ to your app-folder. There is a nice library to handle that for you: android-sqlite-asset-helper

Lukas Knuth
  • 25,449
  • 15
  • 83
  • 111
  • Thank you! It's just 2 hours job. :) – 4af2e9eb6 May 27 '11 at 15:29
  • 5
    Why not copy a db file from assets? In my case it's a R/O database with 19 tables and 35k lines worth of insert statements. I see no reason why I would initialize such a database on the device. Is there something I'm missing? – Horst Dehmer Aug 18 '11 at 23:16
  • Is there any way that don't need to copy to data folder ? Could we read directly from assets folder ? – Chameron Nov 22 '11 at 17:38
0

at the right above example worked for me this way:

db = super.getWritableDatabase();
db.close;
copyDatabase();

otherwise i got an IO error;

Community
  • 1
  • 1
0

Here is the simple and convenient code which I use:

public class DataBaseImportHelper {
    private DataBaseImportHelper() {}; // Avoid instantiation

/**
 * Creates a empty database on the system and rewrites it with your own database.
 */
public static boolean importDataBase(Context context) {
    InputStream myInput = null;
    OutputStream myOutput = null;
    try {
        // Open local db from assets as the input stream
        myInput = context.getAssets().open(DATABASE_NAME);
        createEmptyDatabase(context); // See this method below
        // Open the empty db as the output stream
        myOutput = new FileOutputStream(getDatabaseFile(context));

        // 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);
        }

        // Close the streams
        myOutput.flush();
        myInput.close();
        myOutput.close();
        return true;
    } catch (IOException e) {
        e.printStackTrace();
    }
    return false;
}

/**
 * Check if the database already exists.
 * @return true if it exists, false if it doesn't
 */
public static boolean isDatabaseExists(Context context) {
    return getDatabaseFile(context).exists();
}

private static File getDatabaseFile(Context context) {
    return context.getDatabasePath(DatabaseHelper.DATABASE_NAME);
}

/**
 * Create an empty database into the default application database
 * folder.So we are gonna be able to overwrite that database with our database
 */
private static void createEmptyDatabase(Context context) {
            // use anonimous helper to create empty database
    new SQLiteOpenHelper(context, DatabaseHelper.DATABASE_NAME, null, 1) {
                    // Methods are empty. We don`t need to override them
        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        }

        @Override
        public void onCreate(SQLiteDatabase db) {
        }
    }.getReadableDatabase().close();
}

}

And using in code:

if(!DataBaseImportHelper.isDatabaseExists(this)){
    if (!DataBaseImportHelper.importDataBase(this)){
            throw new IllegalStateException("Database doesn`t exist and hasn`t been copied!");
    }
}
SILINIK
  • 988
  • 7
  • 16
  • 1
    The key is the call to createEmptyDatabase(). Without this, the call to "new FileOutputStream()" fails, even though it is supposed to create the file if it does not exist. – OldSchool4664 May 13 '13 at 19:43