Idiom to close a cursor

2019-03-19 00:23发布

问题:

Which of the following two should I be using to make sure that all the cursors are closed?

    Cursor c = getCursor(); 

    if(c!=null && c.getCount()>0){ 
        try{ 
            // read values from cursor 
        }catch(..){} 
        finally{ 
            c.close(); 
        } 
    }//end if

    OR

    Cursor c = getCursor(); 
    try{ 
        if(c!=null && c.getCount()>0){ 
            // read values from cursor 
        }//end if 
    }catch(..){

    }finally{ 
        c.close(); 
    } 

EDIT:
A few questions:
1. Do we need to call close() on a cursor which has count of 0?
2. Because in that case for the first idiom, close() will never be called. It assumes that for a cursor having no elements, cursor will never be opened. Is this a valid assumption?

Please advise.

回答1:

Neither, but the second one was closest.

  • Option 1 doesn't properly close the Cursor when getCount() == 0
  • Option 2 leaves the finally block exposed to a null pointer exception

I would use:

Cursor c = getCursor(); 
try { 
    if(c!=null && c.getCount()>0){ 
         // do stuff with the cursor
    }
}
catch(..) {
    //Handle ex
}
finally { 
    if(c != null) {
        c.close(); 
    }
}

... or if you expect the cursor to be null frequently, you could turn it on its head a little bit:

Cursor c = getCursor(); 
if(c != null) {
    try { 
        if(c.getCount()>0) { 
             // do stuff with the cursor
        }
    }
    catch(..) {
        //Handle ex
    }
    finally { 
        c.close(); 
    }
}


回答2:

This is even better:

  • does not use c.getCount() - counting might require extra work for the database and is not needed
  • initialize the cursor before the query block, so failure to create the query is not followed by the finally block

The code:

Cursor c = query(....);
if (c != null) {
   try {        
       while (c.moveToNext()) {  // If empty or after last record it returns false.    
          // process row...
       }
   } 
   finally {
       c.close();
    }
}

Note that c might be null in case of error or empty cursor. See https://stackoverflow.com/a/16108435/952135. I would report null return value in case of empty cursor as a bug, though.



回答3:

Best practice is the one below:

Cursor c = null;    
try {        
   c = query(....);      
   while (c.moveToNext()) {  // If empty or next to last record it returns false.    
      // do stuff..       
   }
} finally {
   if (c != null && !c.isClosed()) {  // If cursor is empty even though should close it.       
   c.close();
   c = null;  // high chances of quick memory release.
}


回答4:

Depends on what you're catching, but I'd say the second one, just in case c.getCount() throws an exception.

Also, some indentation wouldn't go amiss :)



回答5:

I'd say the first one, mainly because the second one will try to call c.close() even if c is null. Also, according to the docs, getCount()doesn't throw any exceptions, so there's no need to include it in the try block.



回答6:

I think my answer is the best one :

    Cursor cursor = null;

    try {
        cursor = rsd.rawQuery(querySql, null);
        if (cursor.moveToFirst()) {
            do {
                // select your need data from database
            } while (cursor.moveToNext());
        }
    } finally {
        if (cursor != null && !cursor.isClosed()) {
            cursor.close();
            cursor = null;
        }
    }


回答7:

I think @skylarsutton's is a right answer for the question. However, I want to leave codes for the question (any codes in answers seems to have some flaws). Please consider to use my code.

Cursor c = query(....);
if (c != null) {
   try {        
       //You have to use moveToFirst(). There is no quarantee that a cursor is located at the beginning.
       for(c.moveToFirst();!c.isAfterLast();c.moveToNext()) {  
          // process row...
       }
   } 
   finally {
       c.close();
    }
}