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);
}

}
erad
  • 1,766
  • 2
  • 17
  • 26
  • show your database class – Raghunandan Jan 11 '14 at 03:49
  • I edited the question to include database class. – erad Jan 11 '14 at 03:56
  • 1
    post adapter code also. the error is clear you are closing the cursor and remove this `onCreate(_db);` – Raghunandan Jan 11 '14 at 03:59
  • can you show `InventoryAdapter.INVENTORY_TABLE_CREATE`? – Raghunandan Jan 11 '14 at 04:17
  • 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? – erad Jan 11 '14 at 04:24

4 Answers4

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.

Pratik Butani
  • 60,504
  • 58
  • 273
  • 437
  • 1
    [SQLiteOpenHelper](https://developer.android.com/reference/android/database/sqlite/SQLiteOpenHelper.html) has not an `open()` method, where your's comes from? – Cliff Burton Sep 01 '16 at 12:59
  • My `open` and `close` method doing just Open and Close Database. so you can write directly with your db instance, – Pratik Butani Sep 02 '16 at 04:26
  • 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 `DatabaseObjectNotClosedException` – Cliff Burton Sep 02 '16 at 11:16
  • @CliffBurton I have edited my answer please check it out. – Pratik Butani Sep 02 '16 at 11:20
  • 1
    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!! – Cliff Burton Sep 02 '16 at 11:39
  • @CliffBurton Welcome :) Anytime :) – Pratik Butani Sep 02 '16 at 12:05
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];
erad
  • 1,766
  • 2
  • 17
  • 26
  • but the `searchEntry` returns not found if count is <1 if count is >1 what does it return? – Raghunandan Jan 11 '14 at 04:34
  • 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. – erad Jan 11 '14 at 04:51
  • This not is a solution, you need a synchronized !!!, or you can get in a future the same error –  Feb 29 '16 at 09:36
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;
  } 
Raghunandan
  • 132,755
  • 26
  • 225
  • 256
  • 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". – erad Jan 11 '14 at 04:19
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

PsyGik
  • 3,535
  • 1
  • 27
  • 44
  • 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 SQLiteOpenHelper – erad Jan 11 '14 at 04:06
  • @user3152800 your problem is you close the cursor and then do the operation. check my post – Raghunandan Jan 11 '14 at 04:10
  • Use a `Context` and then use. Or try with `getActivity().getWritableDatabse` – PsyGik Jan 11 '14 at 04:11