How to properly close a cursor in android

25,771

Solution 1

You're not closing cursor in getDateMove, getTotalWeightLBS, loadRooms,...

Everywhere where cursor is not needed anymore close it. In those methods use try-finally, it will guarantee to execute code in finally block even when thrown exception occur.

Change code in your methods from this:

try{
  // get data from cursor
} catch (Exception e) {
    c.close();
}

to this:

try {
   // get data from cursor
} catch (Exception e) {
   // exception handling
} finally {
   if(c != null){
       c.close();
   }
} 

Solution 2

If an element is implementing AutoCloseable (like the Cursor.class does), I would recommend to do a try-with-resources, like described here. If you use Retrolambda it has the try-with-resources backported.

So your code:

Cursor cursor = db.query("tableName", columns, null, null, null, null, null);
try {
    if (cursor.moveToFirst()) return cursor.getString(3);
    else return null;
} finally {
   cursor.close();
}

would simply turn into:

try (Cursor cursor = db.query("tableName", columns, null, null, null, null, null)) {
    if (cursor.moveToFirst()) return cursor.getString(3);
    else return null;
}

Solution 3

Closing cursor in finally will guarantee it will be closed;

public void myfunc() {

    Cursor c = null;

    try {

      c = ... // Open cursor here
      return .... // maybe return something

    } finally {
       if(c != null) { c.close(); }
    } 
}

Solution 4

If you use Kotlin (also available in Java in a bit different way), and Android 4.1 and above, you can use this:

    cursor?.use { 
       //..do stuff
    }

As an example, you can check here.

If you want to use Java, do something like:

        try (Cursor cursor = ...) {
            // do something
        }
Share:
25,771
philip
Author by

philip

5 signs of laziness: 1.

Updated on August 20, 2020

Comments

  • philip
    philip almost 4 years

    I have this database using sqlite, and I'm having problem with closing the cursor its saying that Application did not close the cursor or database object that was opened here here's the logcat

    10-18 08:40:56.354: E/Cursor(331): android.database.sqlite.DatabaseObjectNotClosedException: Application did not close the cursor or database object that was opened here
    10-18 08:40:56.354: E/Cursor(331):  at android.database.sqlite.SQLiteCursor.<init>(SQLiteCursor.java:210)
    10-18 08:40:56.354: E/Cursor(331):  at android.database.sqlite.SQLiteDirectCursorDriver.query(SQLiteDirectCursorDriver.java:53)
    10-18 08:40:56.354: E/Cursor(331):  at android.database.sqlite.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:1345)
    10-18 08:40:56.354: E/Cursor(331):  at android.database.sqlite.SQLiteDatabase.queryWithFactory(SQLiteDatabase.java:1229)
    10-18 08:40:56.354: E/Cursor(331):  at android.database.sqlite.SQLiteDatabase.query(SQLiteDatabase.java:1184)
    10-18 08:40:56.354: E/Cursor(331):  at android.database.sqlite.SQLiteDatabase.query(SQLiteDatabase.java:1264)
    10-18 08:40:56.354: E/Cursor(331):  at standard.internet.marketing.mymovingfriend.SQLHandler.checkMove(SQLHandler.java:1094)
    10-18 08:40:56.354: E/Cursor(331):  at standard.internet.marketing.mymovingfriend.ListMovingNames$3.onKey(ListMovingNames.java:98)
    10-18 08:40:56.354: E/Cursor(331):  at android.view.View.dispatchKeyEvent(View.java:3735)
    10-18 08:40:56.354: E/Cursor(331):  at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:788)
    10-18 08:40:56.354: E/Cursor(331):  at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:788)
    10-18 08:40:56.354: E/Cursor(331):  at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:788)
    10-18 08:40:56.354: E/Cursor(331):  at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:788)
    10-18 08:40:56.354: E/Cursor(331):  at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchKeyEvent(PhoneWindow.java:1667)
    10-18 08:40:56.354: E/Cursor(331):  at com.android.internal.policy.impl.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1102)
    10-18 08:40:56.354: E/Cursor(331):  at android.app.Activity.dispatchKeyEvent(Activity.java:2063)
    10-18 08:40:56.354: E/Cursor(331):  at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1643)
    10-18 08:40:56.354: E/Cursor(331):  at android.view.ViewRoot.deliverKeyEventToViewHierarchy(ViewRoot.java:2471)
    10-18 08:40:56.354: E/Cursor(331):  at android.view.ViewRoot.handleFinishedEvent(ViewRoot.java:2441)
    10-18 08:40:56.354: E/Cursor(331):  at android.view.ViewRoot.handleMessage(ViewRoot.java:1735)
    10-18 08:40:56.354: E/Cursor(331):  at android.os.Handler.dispatchMessage(Handler.java:99)
    10-18 08:40:56.354: E/Cursor(331):  at android.os.Looper.loop(Looper.java:123)
    10-18 08:40:56.354: E/Cursor(331):  at android.app.ActivityThread.main(ActivityThread.java:4627)
    10-18 08:40:56.354: E/Cursor(331):  at java.lang.reflect.Method.invokeNative(Native Method)
    10-18 08:40:56.354: E/Cursor(331):  at java.lang.reflect.Method.invoke(Method.java:521)
    10-18 08:40:56.354: E/Cursor(331):  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
    10-18 08:40:56.354: E/Cursor(331):  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)
    10-18 08:40:56.354: E/Cursor(331):  at dalvik.system.NativeStart.main(Native Method)
    

    this issue's been bugging me for 3 days.

    here some relevant code:

    public class SQLHandler {
    
        public static final String KEY_MOVENAME = "movename";
        public static final String KEY_ID1 = "_id";
        public static final String KEY_ID5 = "_id";
        public static final String KEY_MOVEDATE = "movedate";
        public static final String KEY_TOTALMOVEWEIGHT = "totalmoveweight";
        public static final String KEY_TOTALITEM = "totalitem";
    
        private static final String DATABASE_NAME = "mymovingfriend";
        private static final int DATABASE_VERSION = 1;
    
        public static final String KEY_LISTITEMNAME = "listitemname";
        public static final String KEY_LISTITEMWEIGHT = "listitemweight";
        public static final String KEY_LISTITEMROOM = "listitemroom";
    
        private static final String DATABASE_TABLE1 = "movingname";
        private static final String DATABASE_TABLE5 = "listitem";
    
        public static final String CREATE_TABLE_1 = "CREATE TABLE " + DATABASE_TABLE1 + " (" + 
                KEY_ID1 + " INTEGER PRIMARY KEY AUTOINCREMENT," + 
                KEY_MOVEDATE + " TEXT NOT NULL, " + 
                KEY_TOTALMOVEWEIGHT + " TEXT NOT NULL, " + 
                KEY_TOTALITEM + " INTEGER NOT NULL, " +
                KEY_MOVENAME + " TEXT NOT NULL);";
    
        public static final String CREATE_TABLE_2 = "CREATE TABLE " + DATABASE_TABLE2 + " (" + 
                KEY_ID2 + " INTEGER PRIMARY KEY AUTOINCREMENT," + 
                KEY_ROOMMOVEHOLDER + " TEXT NOT NULL, " + 
                KEY_ROOMWEIGHT + " TEXT NOT NULL, " + 
                KEY_ROOM + " TEXT NOT NULL);";
    
        public static final String CREATE_TABLE_5 = "CREATE TABLE " + DATABASE_TABLE5 + " (" + 
                KEY_ID5 + " INTEGER PRIMARY KEY AUTOINCREMENT," + 
                KEY_LISTITEMNAME + " TEXT NOT NULL, " + 
                KEY_LISTITEMWEIGHT + " TEXT NOT NULL, " +
                KEY_LISTITEMROOM + " TEXT NOT NULL);";
    
        private DbHelper ourHelper;
        private final Context ourContext;
        private SQLiteDatabase ourDatabase;
    
        private static class DbHelper extends SQLiteOpenHelper{
    
            public DbHelper(Context context) {
                super(context, DATABASE_NAME, null, DATABASE_VERSION);
                // TODO Auto-generated constructor stub
            }
    
            @Override
            public void onCreate(SQLiteDatabase db) {
                // TODO Auto-generated method stub
                db.execSQL(CREATE_TABLE_1);
                db.execSQL(CREATE_TABLE_2);
                db.execSQL(CREATE_TABLE_3);
                db.execSQL(CREATE_TABLE_4);
                db.execSQL(CREATE_TABLE_5);
            }
    
            @Override
            public void onUpgrade(SQLiteDatabase db, int oldversion, int newversion) {
                // TODO Auto-generated method stub
                db.execSQL("DROP TABLE IF EXISTS " + DATABASE_TABLE1);
                db.execSQL("DROP TABLE IF EXISTS " + DATABASE_TABLE2);
                db.execSQL("DROP TABLE IF EXISTS " + DATABASE_TABLE3);
                db.execSQL("DROP TABLE IF EXISTS " + DATABASE_TABLE4);
                db.execSQL("DROP TABLE IF EXISTS " + DATABASE_TABLE5);
                onCreate(db);
            }
        }
    
        public SQLHandler(Context c){
            ourContext = c;
        }
    
        public SQLHandler open() throws SQLException{
            ourHelper = new DbHelper(ourContext);
            ourDatabase = ourHelper.getWritableDatabase();
            return this;
        }
    
        public void close(){
            ourDatabase.close();
            ourHelper.close();
        }
    
        public long createMove(String smovename){
            ContentValues cv = new ContentValues();
            cv.put(KEY_MOVENAME, smovename);
            cv.put(KEY_MOVEDATE, "Not yet set");
            cv.put(KEY_TOTALMOVEWEIGHT, "0");
            cv.put(KEY_TOTALITEM, 0);
            return ourDatabase.insert(DATABASE_TABLE1, null, cv);
        }
    
        public void createList(){
    
            String[] sroom = new String[]{"Kitchen", "Bedroom", "Dinning Room"};
            String[] sitem = new String[]{"Dishwasher", "Bed", "Table"};
            String[] sweight = new String[]{"40", "25", "15"};
    
            for (int i = 0; i < sroom.length; i++) {
                cv.put(KEY_LISTITEMROOM, sroom[i]);
                cv.put(KEY_LISTITEMNAME, sitem[i]);
                cv.put(KEY_LISTITEMWEIGHT, sweight[i]);
                ourDatabase.insert(DATABASE_TABLE5, null, cv);
            }
        }
    
        public void setMoveDate(String smovedate, String smovename){
            ContentValues cv = new ContentValues();
            cv.put(KEY_MOVEDATE, smovedate);
            ourDatabase.update(DATABASE_TABLE1, cv, KEY_MOVENAME + "='" + smovename + "'", null);
        }
    
        public void setMoveWeight(String smoveweight, String smovename){
            ContentValues cv = new ContentValues();
            cv.put(KEY_TOTALMOVEWEIGHT, smoveweight);
            ourDatabase.update(DATABASE_TABLE1, cv, KEY_MOVENAME + "='" + smovename + "'", null);
        }
    
        public void setTotalItem(String smovename, int imoveitem){
            ContentValues cv = new ContentValues();
            cv.put(KEY_TOTALITEM, imoveitem);
            ourDatabase.update(DATABASE_TABLE1, cv, KEY_MOVENAME + "='" + smovename + "'", null);
        }
    
        public void renameRoom(String movename, String roomname, String currentroom){
            ContentValues cv = new ContentValues();
            cv.put(KEY_ROOM, roomname);
            ourDatabase.update(DATABASE_TABLE2, cv, KEY_ROOMMOVEHOLDER + "='" + movename + "'" + " AND " + KEY_ROOM + "='" + currentroom + "'", null);
        }
    
        public void setRoomWeight(String sroomweight, String smovename, String sroomname){
            ContentValues cv = new ContentValues();
            cv.put(KEY_ROOMWEIGHT, sroomweight);
            ourDatabase.update(DATABASE_TABLE2, cv, KEY_ROOMMOVEHOLDER + "='" + smovename + "'" + " AND " + KEY_ROOM + "='" + sroomname + "'", null);
        }
    
        public long addRooms(String sroommoveholder, String sroom){
            ContentValues cv = new ContentValues();
            cv.put(KEY_ROOMMOVEHOLDER, sroommoveholder);
            cv.put(KEY_ROOM, sroom);
            cv.put(KEY_ROOMWEIGHT, "0");
            return ourDatabase.insert(DATABASE_TABLE2, null, cv);
        }
    
        public long addNewItems(String sitemmoveholder, String sroomholder, String sitemname, String sitemvalue, String sitemweight){
            ContentValues cv = new ContentValues();
            cv.put(KEY_ITEMMOVEHOLDER, sitemmoveholder);
            cv.put(KEY_ROOMHOLDER, sroomholder);
            cv.put(KEY_ITEMNAME, sitemname);
            cv.put(KEY_ITEMVALUE, sitemvalue);
            cv.put(KEY_ITEMWEIGHT, sitemweight);
            return ourDatabase.insert(DATABASE_TABLE3, null, cv);
        }
    
        public void updateItems(String sitemmoveholder, String sroomholder, String sitemname, String sitemvalue, String sitemweight){
            ContentValues cv = new ContentValues();
            cv.put(KEY_ITEMVALUE, sitemvalue);
            cv.put(KEY_ITEMWEIGHT, sitemweight);
            ourDatabase.update(DATABASE_TABLE3, cv, KEY_ITEMMOVEHOLDER + "='" + sitemmoveholder + "'" + " AND " + 
                    KEY_ROOMHOLDER + "='" + sroomholder + "'" + " AND " + KEY_ITEMNAME + "='" + sitemname + "'", null);
        }
    
        public Cursor getMove(){
            String[] columns = new String[]{KEY_ID1, KEY_MOVENAME};
    
            Cursor c = null;
            try {
                c = ourDatabase.query(DATABASE_TABLE1, columns, null, null, null, null, null);
            } catch (Exception e) {
                c.close();
            }
            return c;
        }
    
        public String getTotalWeight(String m) throws SQLException{
            String[] columns = new String[]{KEY_ID1, KEY_MOVENAME, KEY_MOVEDATE, KEY_TOTALMOVEWEIGHT};
    
            Cursor c = null;
            try {
                c = ourDatabase.query(DATABASE_TABLE1, columns, KEY_MOVENAME + "= '" + m + "'", null, null, null, null);
                if (c != null) {
                    c.moveToFirst();
                    String totalWeight = c.getString(3);
                    return totalWeight;
                }
            } catch (Exception e) {
                c.close();
            }
            return null;
    
        }
    
        public String getTotalWeightLBS(String m) throws SQLException{
            String[] columns = new String[]{KEY_ID1, KEY_MOVENAME, KEY_MOVEDATE, KEY_TOTALMOVEWEIGHT};
    
            Cursor c = null;
            try {
                c = ourDatabase.query(DATABASE_TABLE1, columns, KEY_MOVENAME + "= '" + m + "'", null, null, null, null);
                if (c != null) {
                    c.moveToFirst();
                    int x = Integer.parseInt(c.getString(3)) * 7;
                    String totalWeight = "" + x + " lbs";
                    return totalWeight;
                }
            } catch (Exception e) {
                c.close();
            }
            return null;
    
        }
    
        public String getDateMove(String md){
            String[] columns = new String[]{KEY_ID1, KEY_MOVENAME, KEY_MOVEDATE};
    
            Cursor c = null;try {
                c = ourDatabase.query(DATABASE_TABLE1, columns, KEY_MOVENAME + "= '" + md + "'", null, null, null, null);
    
                if (c != null) {
                    c.moveToFirst();
                    String moveDate = c.getString(2);
                    return moveDate;
                }
            } catch (Exception e) {
                c.close();
            }
            return null;
        }
    
        public ArrayList<String> loadRooms(String mn) throws SQLException{
            String[] columns = new String[]{KEY_ID2, KEY_ROOMMOVEHOLDER, KEY_ROOM};
    
            ArrayList<String> array = new ArrayList<String>();
    
            Cursor c = null;
            try {
                c = ourDatabase.query(DATABASE_TABLE2, columns,KEY_ROOMMOVEHOLDER + "='" + mn + "'", 
                        null, null, null, null);
                int iroom = c.getColumnIndex(KEY_ROOM);
    
                for (c.moveToFirst(); !c.isAfterLast(); c.moveToNext()) {
                    array.add(c.getString(iroom));
                }
            } catch (Exception e) {
                c.close();
            }
            return array;
        }
    
        public void deleteMove(String m) throws SQLException{
            ourDatabase.delete(DATABASE_TABLE1, KEY_MOVENAME + "='" + m + "'", null);
            ourDatabase.delete(DATABASE_TABLE2, KEY_ROOMMOVEHOLDER + "='" + m + "'", null);
            ourDatabase.delete(DATABASE_TABLE3, KEY_ITEMMOVEHOLDER + "='" + m + "'", null);
            ourDatabase.delete(DATABASE_TABLE4, KEY_TODOMOVE + "='" + m + "'", null);
        }
    
        public ArrayList<String> getitems(){
            String[] columns = new String[]{KEY_ID5, KEY_ITEMNAME};
            ArrayList<String> items;
            items = new ArrayList<String>();
    
            Cursor c = null;
            try {
                c = ourDatabase.query(DATABASE_TABLE5, columns, null, null, null, null, null);
    
                for (c.moveToFirst(); !c.isAfterLast(); c.moveToNext()) {
                    items.add(c.getString(1));
                }
            } catch (Exception e) {
                c.close();
            }
    
            return items;
        }
    
        public ArrayList<String> getitemweight(){
            String[] columns = new String[]{KEY_ID5, KEY_ITEMWEIGHT};
            ArrayList<String> items = new ArrayList<String>();
    
            Cursor c = null;
            try {
                c = ourDatabase.query(DATABASE_TABLE5, columns, null, null, null, null, null);
                for (c.moveToFirst(); !c.isAfterLast(); c.moveToNext()) {
                    items.add(c.getString(1));
                }
            } catch (Exception e) {
                c.close();
            }
    
            return items;
        }
    
        public ArrayList<String> getitemclass(){
            String[] columns = new String[]{KEY_ID5, KEY_LISTITEMROOM};
            ArrayList<String> items = new ArrayList<String>();
    
            Cursor c = null;
            try {
                c = ourDatabase.query(DATABASE_TABLE5, columns, null, null, null, null, null);
    
                for (c.moveToFirst(); !c.isAfterLast(); c.moveToNext()) {
                    items.add(c.getString(1));
                }
            } catch (Exception e) {
                c.close();
            }
    
            return items;
        }
    
    }
    
  • vinothp
    vinothp over 11 years
    Oh i see. Try closing you database object as well
  • philip
    philip over 11 years
    wait i made a typo, fix it just a sec
  • subodh
    subodh over 11 years
    oh, then try to call startManagingCursor() after you get the Cursor from your query.
  • philip
    philip over 11 years
    already did that, the cursor that's been giving an error seams to be from the database and not the cursor from the activity
  • pawelzieba
    pawelzieba over 11 years
    The exception was thrown from checkMove method which is not included in your code.
  • pawelzieba
    pawelzieba over 11 years
    If you've finished reading from Cursor, close it - that's all.
  • Jonathan Muller
    Jonathan Muller over 9 years
    As a note, this method requires API 19
  • Paul Woitaschek
    Paul Woitaschek over 9 years
    Im using it with API 11, and never received a crash while having about 3k users the last months.
  • Murphy
    Murphy almost 8 years
    Android Studio's inspector highlights the try in red with "Try-with-resources requires API level 19 (current min is 15)", however I tested and it compiles and runs fine on a phone API level 17. Apparently it's not supposed to be supported on minAPI<19. stackoverflow.com/questions/20480090/… While it might work on some devices, it should NOT be recommended.
  • Malachiasz
    Malachiasz almost 8 years
    why to open cursor inside of try block ?
  • Malachiasz
    Malachiasz almost 8 years
    why to open cursor inside of try block ?
  • Paul Woitaschek
    Paul Woitaschek almost 8 years
    @Malachiasz Because its a good practice to minimize the scope of local variables (Effective Java item #45). Also delegating the cursor closing is a good idea as its less error prone and you can't forget to call .close.
  • Malachiasz
    Malachiasz almost 8 years
    @PaulWoitaschek: I have actually meant the 1st version, because the 2nd is not available under older Android OS versions. So the scope of variable is not smaller there, because declaration of variable happens before try block. If I close cursor in finally then I won't forget about closing. Only problem would be if somebody added some code between getting cursor from the DB and starting try - finally block.
  • Paul Woitaschek
    Paul Woitaschek almost 8 years
    @Malachiasz You're absolutely right about the first version. I just simplified it. Btw, retrolambda backported try-with-resources so you can use it in earlier versions of android too.
  • mbonness
    mbonness about 6 years
    try-with-resources is much more elegant