2

I have gone through other similar questions but I did not get an answer.

In my android app I'm opening a pre-built database. It is in the assets folder and copied using SQLiteOpenHelper, if not already present. This the class:

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import android.content.Context;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class ExternalDbOpenHelper extends SQLiteOpenHelper {

public static String DB_PATH;
public static String DB_NAME;
public SQLiteDatabase database;
public final Context context;

public SQLiteDatabase getDb() {
    return database;
}

public ExternalDbOpenHelper(Context context, String databaseName) {
    super(context, databaseName, null, 1);
    this.context = context;

    String packageName = context.getPackageName();
    DB_PATH = String.format("//data//data//%s//databases//", packageName);
    DB_NAME = databaseName;
    openDataBase();
}

public void createDataBase() {
    boolean dbExist = checkDataBase();
    if (!dbExist) {
        this.getReadableDatabase();
        try {
            copyDataBase();
        } catch (IOException e) {
            // Log.e(this.getClass().toString(), "Copying error");
            //throw new Error("Error copying database!");
        }
    } else {
        // Log.i(this.getClass().toString(), "Database already exists");
    }
}

private boolean checkDataBase() {
    SQLiteDatabase checkDb = null;
    try {
        String path = DB_PATH + DB_NAME;
        checkDb = SQLiteDatabase.openDatabase(path, null,
                SQLiteDatabase.OPEN_READONLY); 
    } catch (SQLException e) {
        // Log.e(this.getClass().toString(), "Error while checking db");
    }

    if (checkDb != null) {
        checkDb.close();
    }
    return checkDb != null;
}

private void copyDataBase() throws IOException {

    InputStream externalDbStream = context.getAssets().open(DB_NAME);

    String outFileName = DB_PATH + DB_NAME;

    OutputStream localDbStream = new FileOutputStream(outFileName);

    byte[] buffer = new byte[1024];
    int bytesRead;
    while ((bytesRead = externalDbStream.read(buffer)) > 0) {
        localDbStream.write(buffer, 0, bytesRead);
    }

    localDbStream.close();
    externalDbStream.close();

}

public SQLiteDatabase openDataBase() throws SQLException {
    String path = DB_PATH + DB_NAME;
    if (database == null) {
        createDataBase();
        database = SQLiteDatabase.openDatabase(path, null,
                SQLiteDatabase.OPEN_READWRITE); //caused by this line
    }
    return database;
}

@Override
public synchronized void close() {
    if (database != null) {
        database.close();
    }
    super.close();
}

@Override
public void onCreate(SQLiteDatabase db) {
}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}

And this is in my activity:

private static final String DB_NAME = "myDB.sqlite";
private SQLiteDatabase database;
private ExternalDbOpenHelper dbOpenHelper;

//in onCreate()
dbOpenHelper = new ExternalDbOpenHelper(this,DB_NAME);
database = dbOpenHelper.openDataBase();

In my app I repeatedly query the database using a cursor and rawQuery. I open the database OPEN_READONLY as I never modify it. So so wasn't closing it before. Added this now:

@Override
protected void onDestroy()
{
    dbOpenHelper.close();
    super.onDestroy();

}

Was this the problem? I had never experienced SQLite disk IO exception and can't open database exception before(without dbOpenHelper.close();. It was reported once each in crash report). Both times apps crashed on launch and was caused by the line

database = SQLiteDatabase.openDatabase(path, null, SQLiteDatabase.OPEN_READWRITE);

in openDataBase() in theExternalDbOpenHelper class.

I'm unable to reproduce the errors. The two devices that reported it have device names as "Other" and blank.

android.database.sqlite.SQLiteCantOpenDatabaseException
in android.database.sqlite.SQLiteDatabase.dbopen:

java.lang.RuntimeException: Unable to start activity        ComponentInfo{com.technicosa.unjumble/com.technicosa.unjumble.MainActivity}: android.database.sqlite.SQLiteCantOpenDatabaseException: unable to open database file
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1956)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1981)
at android.app.ActivityThread.access$600(ActivityThread.java:123)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1147)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4424)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
at dalvik.system.NativeStart.main(Native Method)
Caused by: android.database.sqlite.SQLiteCantOpenDatabaseException: unable to open database file
at android.database.sqlite.SQLiteDatabase.dbopen(Native Method)
at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:1013)
at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:986)
at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:1024)
at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:986)
at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:962)
at com.technicosa.unjumble.dbhelper.ExternalDbOpenHelper.openDataBase(ExternalDbOpenHelper.java:90)
at com.technicosa.unjumble.dbhelper.ExternalDbOpenHelper.<init>(ExternalDbOpenHelper.java:33)
at com.technicosa.unjumble.MainActivity.onCreate(MainActivity.java:131)
at android.app.Activity.performCreate(Activity.java:4465)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1049)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1920)
... 11 more
Sanket Shah
  • 4,352
  • 3
  • 21
  • 41
Frozen Crayon
  • 5,172
  • 8
  • 36
  • 71

2 Answers2

0

Look at This this is working like a charm

public class SmartOpenHelper extends SQLiteOpenHelper {

    private Context context;
    private SQLiteDatabase myDataBase;
    private String DB_SQL;
    private SmartVersionHandler smartVersionHandler;

    SmartOpenHelper(Context context, String dbname, int dbversion, String dbSqlName, SmartVersionHandler smartVersionHandler) throws IOException {
        super(context, dbname, null, dbversion);
        this.context = context;
        this.DB_SQL = dbSqlName;
        this.smartVersionHandler = smartVersionHandler;
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        try {
            BufferedInputStream inStream = new BufferedInputStream(context.getAssets().open(DB_SQL));
            String sql = "";
            int character = -2;
            do {
                character = inStream.read();
                if ((character != -1) && (character != -2))
                    sql += (char) character;
                else
                    break;
            } while (true);
            System.out.println("onCreate DB SQL = " + sql.split("\n"));
            String[] arrSQL = sql.split("\n");

            for (int i = 0; i < arrSQL.length; i++) {
                db.execSQL(arrSQL[i]);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
        if (this.smartVersionHandler != null) {
            this.smartVersionHandler.onInstalling(SmartApplication.REF_SMART_APPLICATION);
        }
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

        try {
            BufferedInputStream inStream = new BufferedInputStream(context.getAssets().open(DB_SQL));
            String sql = "";
            int character = -2;
            do {
                character = inStream.read();
                if ((character != -1) && (character != -2))
                    sql += (char) character;
                else
                    break;
            } while (true);

            System.out.println("onUpgrade DB SQL = " + sql.split("\n"));
            String[] arrSQL = sql.split("\n");
            for (int i = 0; i < arrSQL.length; i++) {
                db.execSQL(arrSQL[i]);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
        if (this.smartVersionHandler != null) {
            this.smartVersionHandler.onUpgrading(oldVersion, newVersion, SmartApplication.REF_SMART_APPLICATION);
        }
    }

    public SQLiteDatabase getOpenDatabase() {
        return myDataBase;
    }

    public synchronized void close() {
        if (myDataBase != null) {
            myDataBase.close();
        }
        super.close();
    }

}
Biraj Zalavadia
  • 28,348
  • 10
  • 61
  • 77
0

I don't really think this is a good approach to the problem. SQLiteOpenHelper it's not meant to be used like that. You should copy the DB outside the helper class and then initialize the helper which should serve you only to get the SQLiteDatabase (through getReadableDatabase and/or getWritableDatabase).

However, reading the code I don't see why you call getReadableDatabase before copyDataBase in createDataBase function. You should also check the database path as hotveryspicy suggests.

Can you please post the stack trace of the exception?

Try with this edits:

public class ExternalDbOpenHelper extends SQLiteOpenHelper {

public static String DB_PATH;
public static String DB_NAME;
public SQLiteDatabase database;
public final Context context;

public SQLiteDatabase getDb() {
    return database;
}

public ExternalDbOpenHelper(Context context, String databaseName) {
    super(context, databaseName, null, 1);
    this.context = context;

    String packageName = context.getPackageName();
    DB_PATH = String.format("%s//data//%s//databases//", Environment.getDataDirectory(), packageName); // as per hotveryspicy comment
    DB_NAME = databaseName;
    openDataBase();
}

public void createDataBase() {
    boolean dbExist = checkDataBase();
    if (!dbExist) {
        //this.getReadableDatabase(); why do you call getReadableDatabase() here?
        try {
            copyDataBase();
        } catch (IOException e) {
            // Log.e(this.getClass().toString(), "Copying error");
            //throw new Error("Error copying database!");
        }
    } else {
        // Log.i(this.getClass().toString(), "Database already exists");
    }
}

private boolean checkDataBase() {
    SQLiteDatabase checkDb = null;
    try {
        String path = DB_PATH + DB_NAME;
        checkDb = SQLiteDatabase.openDatabase(path, null,
                SQLiteDatabase.OPEN_READONLY); 
    } catch (SQLException e) {
        // Log.e(this.getClass().toString(), "Error while checking db");
    }

    if (checkDb != null) {
        checkDb.close();
    }
    return checkDb != null;
}

private void copyDataBase() throws IOException {

    InputStream externalDbStream = context.getAssets().open(DB_NAME);

    String outFileName = DB_PATH + DB_NAME;

    OutputStream localDbStream = new FileOutputStream(outFileName);

    byte[] buffer = new byte[1024];
    int bytesRead;
    while ((bytesRead = externalDbStream.read(buffer)) > 0) {
        localDbStream.write(buffer, 0, bytesRead);
    }

    localDbStream.close();
    externalDbStream.close();

}

public SQLiteDatabase openDataBase() throws SQLException {
    String path = DB_PATH + DB_NAME;
    if (database == null) {
        createDataBase();
        //database = SQLiteDatabase.openDatabase(path, null,
                //SQLiteDatabase.OPEN_READWRITE);

        database = getWritableDatabase(); // <- try with this        
    }
    return database;
}

@Override
public synchronized void close() {
    if (database != null) {
        database.close();
    }
    super.close();
}

@Override
public void onCreate(SQLiteDatabase db) {
}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
Axel GeNuS
  • 610
  • 1
  • 6
  • 11