1

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.

voncox
  • 1,191
  • 7
  • 13
  • http://stackoverflow.com/questions/9109438/how-to-use-an-existing-database-with-an-android-application/9109728#9109728 – Yaqub Ahmad Feb 13 '13 at 12:48

2 Answers2

1

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...

cking24343
  • 3,173
  • 1
  • 21
  • 23
  • Thanks for taking the time to reply. This is precisely what I'm doing too, so unfortunately it doesn't help me. – voncox Feb 13 '13 at 12:46
  • using this i never have an issue with db not being found. I'm simply just pasting a working example so that you can try this to see if you still get the same errors, if not then good, if so then you're back to where you are now...couldn't hurt to try it... – cking24343 Feb 13 '13 at 12:58
1

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.

voncox
  • 1,191
  • 7
  • 13