Question

I wrote a sqldb helper class to handle some webservice info related data I wanted to keep locally I kept all the sqlite refernces closed after use however I still get a "java.lang.IllegalStateException: attempt to re-open an already-closed object" once calling to the "AddNonceForMethod" method here is my code -

public class WebServicesDataManager extends SQLiteOpenHelper {

    /* Class Variables */
    private final String TAG = WebServicesDataManager.class.getSimpleName();

    // Database Version
    private static final int DATABASE_VERSION = 1;

    // Database Name
    public static final String DATABASE_NAME = "dmx";

    // Tables
    private static final String TABLE_WEBSERVICES = "webservices";
    private static final String TABLE_COOKIE = "cookie";
    private static final String TABLE_INFORMATION = "information";

    // Tables and table columns names
    private String CREATE_WEBSERVICES_TABLE;
    private static final String COLUMN_METHOD_ID = "method_id";
    private static final String COLUMN_METHOD_NAME = "method_name";
    private static final String COLUMN_METHOD_NONCE = "method_nonce";
    private static final String COLUMN_METHOD_NONCE_TIME_STAMP = "nonce_time_stamp";
    private static final String COLUMN_USER_COOKIE = "user_cookie";

    private String CREATE_COOKIE_TABLE;
    private static final String COLUMN_COOKIE_STRING = "cookie_string";
    private static final String COLUMN_COOKIE_TIME_STAMP = "cookie_time_stamp";

    private String CREATE_INFORMATION_TABLE;
    private static final String COLUMN_INFORMATION_ID = "info_id";
    private static final String COLUMN_INFO_VERSION_NUMBER = "info_version_number";

    /**
     * Class constructor
     * 
     * @param context
     *            The context to run in
     */
    public WebServicesDataManager(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);

    }

    // Creating Tables
    @Override
    public void onCreate(SQLiteDatabase db) {
        CREATE_WEBSERVICES_TABLE = "CREATE TABLE IF NOT EXISTS "
                + TABLE_WEBSERVICES + " (" + COLUMN_METHOD_ID
                + " INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "
                + COLUMN_METHOD_NAME + " VARCHAR(64) NOT NULL, "
                + COLUMN_METHOD_NONCE + " VARCHAR(64) NULL, "
                + COLUMN_METHOD_NONCE_TIME_STAMP + " VARCHAR(64) NULL, "
                + COLUMN_USER_COOKIE + " VARCHAR(64) NULL);";

        CREATE_COOKIE_TABLE = "CREATE TABLE IF NOT EXISTS " + TABLE_COOKIE
                + " (" + COLUMN_COOKIE_STRING + " VARCHAR(64) NULL, "
                + COLUMN_COOKIE_TIME_STAMP + " VARCHAR(64) NOT NULL);";

        CREATE_INFORMATION_TABLE = "CREATE TABLE IF NOT EXISTS "
                + TABLE_INFORMATION + " (" + COLUMN_INFORMATION_ID
                + " INTEGER NOT NULL, " + COLUMN_INFO_VERSION_NUMBER
                + " VARCHAR(64) NOT NULL" + ");";

        // create the tables
        db.execSQL(CREATE_WEBSERVICES_TABLE);
        db.execSQL(CREATE_COOKIE_TABLE);
        db.execSQL(CREATE_INFORMATION_TABLE);
    }

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

        // Drop older table if existed
        db.execSQL("DROP TABLE IF EXISTS " + TABLE_WEBSERVICES);
        db.execSQL("DROP TABLE IF EXISTS " + TABLE_COOKIE);
        db.execSQL("DROP TABLE IF EXISTS " + TABLE_INFORMATION);

        // Create tables again
        onCreate(db);
    }

    /**
     * Adds a nonce value for an exisiting method in the database or create a
     * new method and add a nonce for it
     * 
     * @param method
     *            A mtehod defined on the Method enum
     * @param nonce
     *            The nonce value to add for the method
     * @return True if the process succeed false otherwise
     */
    public boolean AddNonceForMethod(Method method, String nonce) {
        // method variables
        String now = null;
        String methodTimeStamp;
        boolean pass = true;
        SQLiteDatabase db = null;
        ContentValues values = null;
        long hours = 20l;
        long rowsAffected;


        // try to add a nonce for a method
        try {
            if (nonce != null) {
                String oldNonce = GetNonceForMethod(method);
                if (!nonce.equals(oldNonce)) {
                    RemoveNonceForMethod(method);
                    db = this.getWritableDatabase();
                    now = getDateTime();
                    values = new ContentValues();
                    values.put(COLUMN_METHOD_NAME, method.name());
                    values.put(COLUMN_METHOD_NONCE, nonce);
                    values.put(COLUMN_METHOD_NONCE_TIME_STAMP, now);
                    rowsAffected = db.insert(TABLE_WEBSERVICES, null, values);
                    if (rowsAffected == -1) {
                        pass = false;
                    }
                }
            }

        } catch (SQLException exception) {
            Log.e(TAG, exception.getMessage());
            pass = false;
        } finally {
            if (db != null) {
                // close database connection
                db.close();
                db = null;
            }
        }
        return pass;
    }

    /**
     * Gets a cookie from the databse in case there is one else, return NAN in
     * case of failure return null
     * 
     * @return The cookie as string format
     */
    public String GetNonceForMethod(Method method) {
        // method variables
        int columnTimeStampIndex = -1;
        int columnNonceIndex = -1;
        String nonce = null;
        String timeStamp = Constants.NAN;
        SQLiteDatabase db = null;
        String now = null;
        long hours = 48l;
        Cursor cursor = null;

        // try to get the method id from the database
        try {

            db = this.getReadableDatabase();
            cursor = db.query(TABLE_WEBSERVICES, new String[] {
                    COLUMN_METHOD_NAME, COLUMN_METHOD_NONCE,
                    COLUMN_METHOD_NONCE_TIME_STAMP }, COLUMN_METHOD_NAME
                    + " = ?", new String[] { method.name() }, null, null, null);
            if (cursor != null) {
                boolean moved = cursor.moveToFirst();
                if (moved) {
                    columnNonceIndex = cursor
                            .getColumnIndex(COLUMN_METHOD_NONCE);
                    columnTimeStampIndex = cursor
                            .getColumnIndex(COLUMN_METHOD_NONCE_TIME_STAMP);
                    if (columnTimeStampIndex > -1) {
                        timeStamp = cursor.getString(columnTimeStampIndex);
                        now = getDateTime();
                        if (timeStamp != null && timeStamp != Constants.NAN
                                && isTimeDiffLower(timeStamp, now, hours)) {
                            nonce = cursor.getString(columnNonceIndex);
                        }
                    }
                }
            }
        } catch (SQLException exception) {
            nonce = null;
            Log.e(TAG, exception.getMessage());
        } finally {
            if (cursor != null) {
                // close cursor stream
                cursor.close();
            }
            if (db != null) {
                // close database connection
                db.close();
                db = null;
            }
        }
        return nonce;
    }

    private boolean RemoveNonceForMethod(Method method) {
        // method variables
        boolean pass = true;
        SQLiteDatabase db = null;
        long rowsAffected = 0;

        // try to add a nonce for a method
        try {
            db = this.getWritableDatabase();
            rowsAffected = db.delete(TABLE_WEBSERVICES, COLUMN_METHOD_NAME
                    + "=?", new String[] { method.name() });
            if (rowsAffected == 0) {
                pass = false;
            }

        } catch (SQLException exception) {
            Log.e(TAG, exception.getMessage());
            pass = false;
        } finally {
            if (db != null) {
                // close database connection
                db.close();
                db = null;
            }
        }
        return pass;
    }

    /**
     * Add a new cookie for the database
     * 
     * @param cookie
     *            The cookie to add
     * @param timeStamp
     *            The cookie time stamp
     * @return True if the process succeed false otherwise
     */
    public boolean AddCookie(String cookie) {

        // method variables
        long rowId;
        boolean pass = false;
        SQLiteDatabase db = null;
        ContentValues row = null;

        // try to add the cookie to the db
        try {
            row = new ContentValues();
            db = this.getWritableDatabase();
            row.put(COLUMN_COOKIE_STRING, cookie);
            row.put(COLUMN_COOKIE_TIME_STAMP, getDateTime());
            rowId = db.insert(TABLE_COOKIE, null, row);
            if (rowId > -1) {
                pass = true;
            }
        } catch (SQLException exception) {
            Log.e(TAG, exception.getMessage());
        } finally {
            if (db != null) {
                // close database connection
                db.close();
            }
        }
        return pass;
    }

    /**
     * Gets a cookie from the databse in case there is one else, return NAN in
     * case of failure return null
     * 
     * @return The cookie as string format
     */
    public String GetCookie() {
        // method variables
        int columnIndex = -1;
        String cookie = Constants.NAN;
        SQLiteDatabase db = null;
        Cursor cursor = null;

        // try to get the method id from the database
        try {
            db = this.getReadableDatabase();
            cursor = db.query(TABLE_COOKIE, null, null, null, null, null, null);
            if (cursor != null) {
                boolean moved = cursor.moveToFirst();
                if (moved) {
                    columnIndex = cursor.getColumnIndex(COLUMN_COOKIE_STRING);
                    if (columnIndex > -1) {
                        cookie = cursor.getString(columnIndex);
                    }
                }
            }
        } catch (SQLException exception) {
            cookie = null;
            Log.e(TAG, exception.getMessage());
        } finally {
            if (cursor != null) {
                // close cursor stream
                cursor.close();
            }
            if (db != null) {
                // close database connection
                db.close();
            }
        }
        return cookie;
    }

}

and the logcat output -

04-18 21:15:24.971: E/AndroidRuntime(28060): FATAL EXCEPTION: main
04-18 21:15:24.971: E/AndroidRuntime(28060): java.lang.IllegalStateException: attempt to re-open an already-closed object: SQLiteDatabase: /data/data/com.tmc/databases/tmc
04-18 21:15:24.971: E/AndroidRuntime(28060):    at android.database.sqlite.SQLiteClosable.acquireReference(SQLiteClosable.java:55)
04-18 21:15:24.971: E/AndroidRuntime(28060):    at android.database.sqlite.SQLiteDatabase.delete(SQLiteDatabase.java:1614)
04-18 21:15:24.971: E/AndroidRuntime(28060):    at com.tmc.logic.db.WebServicesDataManager.RemoveNonceForMethod(WebServicesDataManager.java:229)
04-18 21:15:24.971: E/AndroidRuntime(28060):    at com.tmc.logic.db.WebServicesDataManager.AddNonceForMethod(WebServicesDataManager.java:134)
04-18 21:15:24.971: E/AndroidRuntime(28060):    at com.tmc.ActivityDebug$1.onClick(ActivityDebug.java:43)
04-18 21:15:24.971: E/AndroidRuntime(28060):    at android.view.View.performClick(View.java:4475)
04-18 21:15:24.971: E/AndroidRuntime(28060):    at android.view.View$PerformClick.run(View.java:18786)
04-18 21:15:24.971: E/AndroidRuntime(28060):    at android.os.Handler.handleCallback(Handler.java:730)
04-18 21:15:24.971: E/AndroidRuntime(28060):    at android.os.Handler.dispatchMessage(Handler.java:92)
04-18 21:15:24.971: E/AndroidRuntime(28060):    at android.os.Looper.loop(Looper.java:137)
04-18 21:15:24.971: E/AndroidRuntime(28060):    at android.app.ActivityThread.main(ActivityThread.java:5419)
04-18 21:15:24.971: E/AndroidRuntime(28060):    at java.lang.reflect.Method.invokeNative(Native Method)
04-18 21:15:24.971: E/AndroidRuntime(28060):    at java.lang.reflect.Method.invoke(Method.java:525)
04-18 21:15:24.971: E/AndroidRuntime(28060):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1187)
04-18 21:15:24.971: E/AndroidRuntime(28060):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
04-18 21:15:24.971: E/AndroidRuntime(28060):    at dalvik.system.NativeStart.main(Native Method)

any help???

Was it helpful?

Solution

see in method AddNonceForMethod() you are calling another method RemoveNonceForMethod(), and that is where you are getting an exception ( if u look closely at your logcat you'll get that) , now comming back to the exception :-

you are creating database helper instance using this.getWritableDatabase() by using this you are referring to current instance of your class and hence you are using same instance of database in your every method , so when your finally block is called in RemoveNonceForMethod() the instance of database u are using is getting closed.

try creating new object in your method :-

 WebServicesDataManager dataManager = new WebServicesDataManager();
 SQLiteDatabase db = dataManager.getWritableDatabase();

by the way you should look at other approach to handle database in Android like singleton approach or wrapping database using contentprovider !!

here are few links:-
http://www.androiddesignpatterns.com/2012/05/correctly-managing-your-sqlite-database.html
What are the best practices for SQLite on Android?

OTHER TIPS

I'd bet the problem is in these two lines of your AddNonceForMethod method:

...

RemoveNonceForMethod(method);
db = this.getWritableDatabase();

...

In your RemoveNonceForMethod you have this piece of code:

finally {
  if (db != null) {
    // close database connection
    db.close();
    db = null;
  }
}

As the finally block will be always executed, you'll for sure get out of this method with your DB closed. You then get out of your RemoceNonceForMethod and the next instruction is:

db = this.getWritableDatabase();

So you're first closing and afterwards trying to reopen the same instance, as getWritableDatabase() will do the following:

Create and/or open a database that will be used for reading and writing. The first time this is called, the database will be opened and onCreate(SQLiteDatabase), onUpgrade(SQLiteDatabase, int, int) and/or onOpen(SQLiteDatabase) will be called.

Once opened successfully, the database is cached, so you can call this method every time you need to write to the database. (Make sure to call close() when you no longer need the database.) Errors such as bad permissions or a full disk may cause this method to fail, but future attempts may succeed if the problem is fixed.

That's why you're getting the exception.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top