Android SQLite: attempt to re-open an already-closed object
Asked Answered
B

4

12

I'm trying to get certain book data from my Inventory table based on the ISBN. However, I'm getting an error: "attempt to re-open an already-closed object". The error only occurs when I click a listView object, go to a different screen, go back to this page via "finish()", and then try to click on another listView object. I moved the String searchEntries[] = InventoryAdapter.getInventoryEntriesByISBN(searchQuery, isbn[position]); from the onClickListener to the previous for loop before the onClickListener and now it works.

Why does it not work if I try to getInventoryEntriesByISBN after returning to this activity from another activity via "finish()"?

The error occurs at SearchResultsScreen:

String searchEntries[] = InventoryAdapter.getInventoryEntriesByISBN(searchQuery, isbn[position]);

and by extension, occurs at InventoryAdapter:

Cursor cursor = db.rawQuery(query, new String[] {ISBN});

SearchResultsScreen.java

// Set up search array
    for(int i = 0; i < isbn.length; i++)
    {
        searchArray.add(new InventoryItem(isbn[i], InventoryAdapter.getTitleAndAuthorByISBN(isbn[i])));
    }
    Toast.makeText(getApplicationContext(), "searchArray.size()="+searchArray.size(), Toast.LENGTH_LONG).show();

    // add data in custom adapter
    adapter = new CustomAdapter(this, R.layout.list, searchArray);
    ListView dataList = (ListView) findViewById(R.id.list);
    dataList.setAdapter(adapter);

    // On Click ========================================================
    dataList.setOnItemClickListener(new OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position,
                long id) {
            String searchEntries[] = InventoryAdapter.getInventoryEntriesByISBN(searchQuery, isbn[position]);

InventoryAdapter.java (Most relevant parts)

public String[] getInventoryEntriesByISBN(String search, String ISBN)
{
    String[] searchEntry = new String [9];
    //Query
    String query = "select * from INVENTORY where ISBN = ?";
    Cursor cursor = db.rawQuery(query, new String[] {ISBN});
    if(cursor.getCount()<1) // title Not Exist
    {
        cursor.close();
        for(int i = 0; i < 9; i++)
            searchEntry[i] = "Not Found";
        return searchEntry;
    }
    cursor.moveToFirst();

    //put data into respective variable
    int publish = cursor.getInt(cursor.getColumnIndex("PUBLISH_DATE"));
    String publishdate = ((Integer)publish).toString();
    String title = cursor.getString(cursor.getColumnIndex("TITLE"));
    String author = cursor.getString(cursor.getColumnIndex("AUTHOR"));
    String callNumber = cursor.getString(cursor.getColumnIndex("CALL_NUMBER"));
    int available = cursor.getInt(cursor.getColumnIndex("AVAILABLE_COUNT"));
    String availablecount = ((Integer)available).toString();
    int inventory = cursor.getInt(cursor.getColumnIndex("INVENTORY_COUNT"));
    String inventorycount = ((Integer)inventory).toString();
    int due = cursor.getInt(cursor.getColumnIndex("DUE_PERIOD"));
    String dueperiod = ((Integer)due).toString();
    int checkoutcount = cursor.getInt(cursor.getColumnIndex("COUNT"));
    String count = ((Integer)checkoutcount).toString();
    //combine variables into one array
    searchEntry[0] = ISBN;
    searchEntry[1] = title;
    searchEntry[2] = author;
    searchEntry[3] = publishdate;
    searchEntry[4] = callNumber;
    searchEntry[5] = availablecount;
    searchEntry[6] = inventorycount;
    searchEntry[7] = dueperiod;
    searchEntry[8] = count;

    cursor.close();
    return searchEntry;
}

public String getTitleAndAuthorByISBN(String ISBN)
    {
        int entriesFound = getNumSearchEntries(ISBN);
        if(entriesFound==0)
            entriesFound = 1;
        String searchEntry;
        //Query
        String query = "select * from INVENTORY where ISBN = ?";
        Cursor cursor = db.rawQuery(query, new String[] {ISBN});
        if(cursor.getCount()<1) // title Not Exist
        {
            cursor.close();
            searchEntry = "Not Found";
            return searchEntry;
        }
        cursor.moveToFirst();
        //put data into respective variable
        String title = cursor.getString(cursor.getColumnIndex("TITLE"));
        String author = cursor.getString(cursor.getColumnIndex("AUTHOR"));
        //combine variables into one String
        searchEntry = title + " / " + author;
        //close cursor and return
        cursor.close();
        return searchEntry;
    }

DataBaseHelper.java

public class DataBaseHelper extends SQLiteOpenHelper
{   
// Database Version
    private static final int DATABASE_VERSION = 1;

// Database Name
private static final String DATABASE_NAME = "database.db";

// ============================ End Variables ===========================

public DataBaseHelper(Context context, String name, CursorFactory factory, int version) 
{
           super(context, name, factory, version);
}

public DataBaseHelper(Context context) {
    super(context, DATABASE_NAME, null, DATABASE_VERSION);
}

// Called when no database exists in disk and the helper class needs
// to create a new one.
@Override
public void onCreate(SQLiteDatabase _db) 
{
        _db.execSQL(LoginDataBaseAdapter.USER_TABLE_CREATE);
        _db.execSQL(CheckOutDataBaseAdapter.CHECKOUT_TABLE_CREATE);
        _db.execSQL(InventoryAdapter.INVENTORY_TABLE_CREATE);
        _db.execSQL(StatisticsAdapter.STATISTICS_TABLE_CREATE);
}
// Called when there is a database version mismatch meaning that the version
// of the database on disk needs to be upgraded to the current version.
@Override
public void onUpgrade(SQLiteDatabase _db, int _oldVersion, int _newVersion) 
{
        // Log the version upgrade.
        Log.w("TaskDBAdapter", "Upgrading from version " +_oldVersion + " to " +_newVersion + ", which will destroy all old data");


        // Upgrade the existing database to conform to the new version. Multiple
        // previous versions can be handled by comparing _oldVersion and _newVersion
        // values.
        // on upgrade drop older tables
        _db.execSQL("DROP TABLE IF EXISTS " + LoginDataBaseAdapter.USER_TABLE_CREATE);
        _db.execSQL("DROP TABLE IF EXISTS " + CheckOutDataBaseAdapter.CHECKOUT_TABLE_CREATE);
        _db.execSQL("DROP TABLE IF EXISTS " + InventoryAdapter.INVENTORY_TABLE_CREATE);
        _db.execSQL("DROP TABLE IF EXISTS " + StatisticsAdapter.STATISTICS_TABLE_CREATE);

        // Create a new one.
        onCreate(_db);
}

}
Bankruptcy answered 11/1, 2014 at 3:44 Comment(5)
show your database classCookie
I edited the question to include database class.Bankruptcy
post adapter code also. the error is clear you are closing the cursor and remove this onCreate(_db);Cookie
can you show InventoryAdapter.INVENTORY_TABLE_CREATE?Cookie
It turns out that the error only occurs when I click an item, go to a different screen, go back to this page via "finish()", and then try to click on another listView object. I moved the String searchEntries[] = InventoryAdapter.getInventoryEntriesByISBN(searchQuery, isbn[position]); from the onClickListener to the previous for loop before the onClickListener and now it works. Why is that?Bankruptcy
B
3

The error only occurs when I click an item, go to a different screen, go back to this page via "finish()", and then try to click on another listView object.

I moved the String searchEntries[] = InventoryAdapter.getInventoryEntriesByISBN(searchQuery, isbn[position]); from the onClickListener to the previous for loop before the onClickListener and now it works.

The correct SearchResultsScreen is below:

SearchResultsScreen.java

// Set up search array
    final String Entries[][] = new String[isbn.length][9];
    for(int i = 0; i < isbn.length; i++)
    {
        searchArray.add(new InventoryItem(isbn[i], InventoryAdapter.getTitleAndAuthorByISBN(isbn[i])));
        Entries[i] = InventoryAdapter.getInventoryEntriesByISBN(searchQuery, isbn[i]);
    }
    Toast.makeText(getApplicationContext(), "searchArray.size()="+searchArray.size(), Toast.LENGTH_LONG).show();

    // add data in custom adapter
    adapter = new CustomAdapter(this, R.layout.list, searchArray);
    ListView dataList = (ListView) findViewById(R.id.list);
    dataList.setAdapter(adapter);

    // On Click ========================================================
    dataList.setOnItemClickListener(new OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position,
                long id) {
            String searchEntries[] = Entries[position];
Bankruptcy answered 11/1, 2014 at 4:28 Comment(3)
but the searchEntry returns not found if count is <1 if count is >1 what does it return?Cookie
I edited the question to show the rest of the original code, which just gets the data from the table and stores it in the array. It works fine for me.Bankruptcy
This not is a solution, you need a synchronized !!!, or you can get in a future the same errorCacogenics
S
17

Check Database Connection before executing query:

if (!dbHelper.db.isOpen()) dbHelper.open();

you can also use cursor.requery(); for again same query.

and in last you have to close the cursor and database also.

cursor.close();
db.close();

Edited:

I have created DBHelper class which extends SQLiteOpenHelper, this class is inner class of DatabaseHelper class and that class have following methods.

/** For OPEN database **/
public synchronized DatabaseHelper open() throws SQLiteException {
    dbHelper = new DBHelper(context);
    db = dbHelper.getWritableDatabase();
    return this;
}

/** For CLOSE database **/
public void close() {
    dbHelper.close();
}

If you have still doubt then feel free to ping me. Thank you.

Splenius answered 11/1, 2014 at 4:51 Comment(6)
SQLiteOpenHelper has not an open() method, where your's comes from?Heterophyllous
My open and close method doing just Open and Close Database. so you can write directly with your db instance,Splenius
I'm sorry but I still don't understand.. I have 3 instances MyDbHelper dbHelper = new MyDbHelper(); SQLiteDatabase dbr = dbHelper.getReadableDatabase(), dbw = dbHelper.getWriteableDatabase(); and none of them have an open() method but each of them have a close() method... Sorry if I bother you but I am new on Android and I ran into this problem...as of now I solved leaving open (not closing) the helper and the Cursor in the finally block of the try-catch-finally but I feel this is not the solution as early the SDK thrown an exception DatabaseObjectNotClosedExceptionHeterophyllous
@CliffBurton I have edited my answer please check it out.Splenius
Ok now I understand! So "opening a database" means just istantiate a new SQLiteOpenHelper and call one of getWritable/ReadableDatabase() methods...now that I read better Adroid docs on these methods states "Create and/or open a database" Thank you so much for your time!!Heterophyllous
@CliffBurton Welcome :) Anytime :)Splenius
B
3

The error only occurs when I click an item, go to a different screen, go back to this page via "finish()", and then try to click on another listView object.

I moved the String searchEntries[] = InventoryAdapter.getInventoryEntriesByISBN(searchQuery, isbn[position]); from the onClickListener to the previous for loop before the onClickListener and now it works.

The correct SearchResultsScreen is below:

SearchResultsScreen.java

// Set up search array
    final String Entries[][] = new String[isbn.length][9];
    for(int i = 0; i < isbn.length; i++)
    {
        searchArray.add(new InventoryItem(isbn[i], InventoryAdapter.getTitleAndAuthorByISBN(isbn[i])));
        Entries[i] = InventoryAdapter.getInventoryEntriesByISBN(searchQuery, isbn[i]);
    }
    Toast.makeText(getApplicationContext(), "searchArray.size()="+searchArray.size(), Toast.LENGTH_LONG).show();

    // add data in custom adapter
    adapter = new CustomAdapter(this, R.layout.list, searchArray);
    ListView dataList = (ListView) findViewById(R.id.list);
    dataList.setAdapter(adapter);

    // On Click ========================================================
    dataList.setOnItemClickListener(new OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position,
                long id) {
            String searchEntries[] = Entries[position];
Bankruptcy answered 11/1, 2014 at 4:28 Comment(3)
but the searchEntry returns not found if count is <1 if count is >1 what does it return?Cookie
I edited the question to show the rest of the original code, which just gets the data from the table and stores it in the array. It works fine for me.Bankruptcy
This not is a solution, you need a synchronized !!!, or you can get in a future the same errorCacogenics
C
1

This is your problem

    if(cursor.getCount()<1) // title Not Exist
    {
        cursor.close(); 
        for(int i = 0; i < 9; i++)
            searchEntry[i] = "Not Found";
        return searchEntry;
    }
    cursor.moveToFirst();
    cursor.close();

Change to

  for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
  {
            String title = cursor.getString(cursor.getColumnIndex("TITLE"));
            String author = cursor.getString(cursor.getColumnIndex("AUTHOR"));
            //combine variables into one String
            searchEntry = title + " / " + author;
  } 
Cookie answered 11/1, 2014 at 4:5 Comment(1)
Actually, I have something that's very similar to that code, but I omitted from posting because that part works. Also, I purposely want to return an array with the text "Not Found".Bankruptcy
R
0
public String[] getInventoryEntriesByISBN(String search, String ISBN)
{
    String[] searchEntry = new String [9];
    //Query
    String query = "select * from INVENTORY where ISBN = ?";
    Cursor cursor = db.rawQuery(query, new String[] {ISBN});

Add SQLiteDatabase db = this.getWritableDatabase(); in this code before executing the raw Query

Rosana answered 11/1, 2014 at 4:0 Comment(3)
I can't add 'this.getWritableDatabase()' because 'this' refers to InventoryAdapter, while my database is in DataBaseHelper. I tried 'DataBaseHelper.getWritableDatabase()' and I get the error: Cannot make a static reference to the non-static method getWritableDatabase() from the type SQLiteOpenHelperBankruptcy
@user3152800 your problem is you close the cursor and then do the operation. check my postCookie
Use a Context and then use. Or try with getActivity().getWritableDatabseRosana

© 2022 - 2024 — McMap. All rights reserved.