0

SOLUTION

I had to override the getReadableDatabase() and getWritableDatabase() in my custom DBHelper class.

I am facing a unique kind of problem.

Until now, I have written Android apps that have used one SQLite DB internally on the phone memory, and it works perfectly. Now I wanted to add another DB and wanted to store it on the SD card. I did this by modifying the DatabaseHelper class that we usually use. I have also written a script that transfers the db from the assets folder to the SD card for one DB and the internal memory location for another.

Now the problem that I am facing is that when I try to access the external DB, android creates an internal DB in the /data/data/package_name/databases folder and thus I end up getting no such table exception.

Can someone tell me why I am getting this error!


Edit 1 My DB Helper Class

public class DBHelper extends SQLiteOpenHelper {
private Context myContext;
private String DB_PATH;
private String DB_NAME;
private boolean isExternal;
private SQLiteDatabase myDataBase;

private final String TAG = "ERROR";

public DBHelper(Context AppC, String DB_NAME, boolean isExternal,
        int DB_VERSION) {
    super(AppC, DB_NAME + ".sqlite", null, DB_VERSION);
    this.myContext = AppC;
    this.isExternal = isExternal;
    this.DB_NAME = DB_NAME;
    setDbPath();
}

private void setDbPath() {
    if (this.isExternal) {
        try {
            DB_PATH = Environment.getExternalStorageDirectory()
                    + "/myappname/";
        } catch (Exception e) {
            DB_PATH = "/mnt/sdcard/myappname/";
        }
    } else {
        try {
            DB_PATH = Environment.getDataDirectory()
                    + "/data/my.app.package/databases/";
        } catch (Exception e) {
            DB_PATH = "data/data/my.app.package/databases/";
        }
    }
}

/**
 * Creates a empty database on the system and rewrites it with your own
 * database.
 * */
public void createDataBase() throws IOException {
    boolean dbExist = checkDataBase();
    if (!dbExist) {
        try {
            copyDataBase();
        } catch (Exception E) {
            // MRC
        }
    }
}

/**
 * Copies your database from your local assets-folder to the just created
 * empty database in the system folder, from where it can be accessed and
 * handled. This is done by transferring byte streams.
 * */
private void copyDataBase() throws IOException {
    String filePath = DB_PATH + this.DB_NAME + ".sqlite";

    if (this.isExternal) {
        File file = new File(filePath);
        if (!file.exists()) {
            File dir = new File(DB_PATH);
            if (dir.exists() || dir.mkdirs()) {
                transferFromAssets(this.DB_NAME + ".zip", filePath);
            } else {
                Log.d(TAG, "Unable to create dir " + DB_PATH);
            }
        }
    } else {
        // By calling this method and empty database will be created
        // into the default system path of your application so we
        // will be able to overwrite that database with our
        // database.

        SQLiteDatabase db_Read = this.getReadableDatabase();
        db_Read.close();
        transferFromAssets(this.DB_NAME + ".zip", filePath);
    }
}

private void transferFromAssets(String from, String to) throws IOException {
    ZipInputStream myInput = new ZipInputStream(myContext.getAssets().open(
            from));
    myInput.getNextEntry(); // Open the empty db as the output stream
    OutputStream myOutput = new FileOutputStream(to);
    // transfer bytes from the inputfile to the outputfile
    AndroidUtil.copyStream(myInput, myOutput);
}

/**
 * Check if the database already exist to avoid re-copying the file each
 * time you open the application.
 * 
 * @return true if it exists, false if it doesn't
 */
private boolean checkDataBase() {
    File dbFile = new File(DB_PATH + this.DB_NAME + ".sqlite");
    return dbFile.exists();
}

public void openDataBase() throws SQLException {
    String myPath = DB_PATH + this.DB_NAME + ".sqlite";
    Log.d(TAG, "Opening DB:" + myPath);
    myDataBase = SQLiteDatabase.openDatabase(myPath, null,
            SQLiteDatabase.OPEN_READWRITE);
}

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

public void onCreate(SQLiteDatabase db) {

}

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

}

}

*My Internal Memory DB's class's constructor *

public WordsDB(Context AppC) {
    DBAPI = new DBHelper(AppC, "DB_NAME", false, 11);
    this.AppC = AppC;
    try {
        DBAPI.createDataBase();
        DBAPI.openDataBase();
    } catch (IOException e) {
        CustomToast.Toast(AppC, "", TCategory_E.CRITICAL_ERROR,
                TDuration_E.LONG, TPosition_E.CENTER);
        // MRC
    }
}

** My External DB class's constructor **

public BackupDB(Context AppC) {
    DBAPI = new DBHelper(AppC, "/mnt/sdcard/myapp/backup-db",
            true, 11);
    this.AppC = AppC;
    try {
        DBAPI.createDataBase();
        DBAPI.openDataBase();
    } catch (IOException e) {
        CustomToast.Toast(AppC, "", TCategory_E.CRITICAL_ERROR,
                TDuration_E.LONG, TPosition_E.CENTER);
        // MRC
    }
}

Edit 2

The function in the external DB's helper class that leads to the error:

public void setValue(int No, int Stars) {
    try {
        ContentValues update = new ContentValues();
        update.put(COL_STARRED, Stars);
        String args[] = { No + "" };
        SQLDB = DBAPI.getWritableDatabase();
        SQLDB.acquireReference();
        SQLDB.update(TABLE_NAME, update, COL_ID + "=?", args);
        SQLDB.releaseReference();
        SQLDB.close();
    } catch (Exception E) {
        Log.d(TAG, E.getMessage());
    }
}

If I try to use the external DB as an internal DB, there is no error which leads me to think that there is something wrong that I am doing for an external DB.

dakshbhatt21
  • 3,558
  • 3
  • 31
  • 40
Anand Sainath
  • 1,807
  • 3
  • 22
  • 48

1 Answers1

1

Can you please provide some code?

I did this by modifying the DatabaseHelper class that we usually use

Can you please describe what modification did you made? If you are using SQLiteOpenHelper, be aware that DB path, that is passed to it's constructor - is relative to /data/data/package/databases/ folder, so you cannot pass SD path there. If you will still need this - I'll suggest using ContextWrapper and redefining it's getDatabasePath method to point to your SD DB.

AlexN
  • 2,544
  • 16
  • 13
  • Hey, I'm sorry, I just edited my question. Can you have a look at it please? – Anand Sainath Sep 02 '12 at 11:22
  • Can you please provide where exactly your are getting "no such column"? I suspect that you are using getReadableDatabase() for this query, aren't you? – AlexN Sep 02 '12 at 11:45
  • I used the same code with the external flag set to false and it works, well sometimes. I am confused! – Anand Sainath Sep 02 '12 at 12:38
  • Sorry, seems you'd still didn't provided the actual Query method which caused the error :) Actually I believe that you do not need to extend SQLiteOpenHelper in your helper, it leads to a confusion. SQLiteOpenHelper itself made to work with internal /databases/ folder, and even the fact that you initialize it with external path in constructor - means the architecture mistake, I think. So I suggest either to provide more code so we'll be able to figure out the current problem, or just rewrite the helper a little, so it will just return SqlDatabase from int/ext path, without SQLiteOpenHelper – AlexN Sep 02 '12 at 12:42
  • Ha ha! I don't know what "actual Query method" there means, but this is the function that i call that leads to the error. And can you show me more about not using the SQLiteOpenHelper stuff.. – Anand Sainath Sep 02 '12 at 13:36
  • 1
    Yes, here is what I'd expected. You are using SQLDB = DBAPI.getWritableDatabase(); DBAPI, I expect, was constructed with "external" flag, right? If so, there is a mistake. getWritableDatabase() is OpenHelper's method, which will use internal DB to open. To quick-fix the issue, please try to override getReadableDatabase() and getWritebleDatabase() and return myDataBase in it. It should fix your problem for now – AlexN Sep 02 '12 at 13:42