39

Where should I call close() on the code?

The LogCat returns this error:

close() was never explicitly called on database android.database.sqlite.DatabaseObjectNotClosedException: Application did not close the cursor or database object that was opened here

The error is this:

> 12-16 17:24:50.886: ERROR/Database(10982): close() was never explicitly called on database '/data/data/com.psyhclo/databases/calls.db' 
12-16 17:24:50.886: ERROR/Database(10982): android.database.sqlite.DatabaseObjectNotClosedException: Application did not close the cursor or database object that was opened here
12-16 17:24:50.886: ERROR/Database(10982):     at android.database.sqlite.SQLiteDatabase.<init>(SQLiteDatabase.java:1827)
12-16 17:24:50.886: ERROR/Database(10982):     at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:820)
12-16 17:24:50.886: ERROR/Database(10982):     at android.database.sqlite.SQLiteDatabase.openOrCreateDatabase(SQLiteDatabase.java:854)
12-16 17:24:50.886: ERROR/Database(10982):     at android.database.sqlite.SQLiteDatabase.openOrCreateDatabase(SQLiteDatabase.java:847)
12-16 17:24:50.886: ERROR/Database(10982):     at android.app.ContextImpl.openOrCreateDatabase(ContextImpl.java:541)
12-16 17:24:50.886: ERROR/Database(10982):     at android.content.ContextWrapper.openOrCreateDatabase(ContextWrapper.java:203)
12-16 17:24:50.886: ERROR/Database(10982):     at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:118)
12-16 17:24:50.886: ERROR/Database(10982):     at com.psyhclo.CallDataHelper.<init>(CallDataHelper.java:27)
12-16 17:24:50.886: ERROR/Database(10982):     at com.psyhclo.RatedCalls.fillList(RatedCalls.java:66)
12-16 17:24:50.886: ERROR/Database(10982):     at com.psyhclo.RatedCalls.access$0(RatedCalls.java:63)
12-16 17:24:50.886: ERROR/Database(10982):     at com.psyhclo.RatedCalls$RatedCallsContentObserver.onChange(RatedCalls.java:58)
12-16 17:24:50.886: ERROR/Database(10982):     at android.database.ContentObserver$NotificationRunnable.run(ContentObserver.java:43)
12-16 17:24:50.886: ERROR/Database(10982):     at android.os.Handler.handleCallback(Handler.java:587)
12-16 17:24:50.886: ERROR/Database(10982):     at android.os.Handler.dispatchMessage(Handler.java:92)
12-16 17:24:50.886: ERROR/Database(10982):     at android.os.Looper.loop(Looper.java:123)
12-16 17:24:50.886: ERROR/Database(10982):     at android.app.ActivityThread.main(ActivityThread.java:3647)
12-16 17:24:50.886: ERROR/Database(10982):     at java.lang.reflect.Method.invokeNative(Native Method)
12-16 17:24:50.886: ERROR/Database(10982):     at java.lang.reflect.Method.invoke(Method.java:507)
12-16 17:24:50.886: ERROR/Database(10982):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
12-16 17:24:50.886: ERROR/Database(10982):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
12-16 17:24:50.886: ERROR/Database(10982):     at dalvik.system.NativeStart.main(Native Method)

here is my code.

The Activity:

public class RatedCalls extends ListActivity {

private static final String LOG_TAG = "RATEDCALLSOBSERVER";
private Handler handler = new Handler();
private SQLiteDatabase db;
private CallDataHelper cdh; 
StringBuilder sb = new StringBuilder();
OpenHelper openHelper = new OpenHelper(RatedCalls.this);

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    registerContentObservers();
    Log.d("FILLLIST", "calling from onCreate()");

}

class RatedCallsContentObserver extends ContentObserver {
    public RatedCallsContentObserver(Handler h) {
        super(h);
    }

    @Override
    public boolean deliverSelfNotifications() {
        return true;

    }

    @Override
    public void onChange(boolean selfChange) {
        Log.d(LOG_TAG, "RatedCallsContentObserver.onChange( " + selfChange
                + ")");
        super.onChange(selfChange);
        fillList();
        onStop();
    }
}

private void fillList() {

    db = openHelper.getWritableDatabase();
    cdh = new CallDataHelper(this);
    String lastDate = cdh.selectDate();

    Cursor cursor = getContentResolver().query(
            android.provider.CallLog.Calls.CONTENT_URI, null,
            android.provider.CallLog.Calls.DATE + " < ?",
            new String[] { lastDate },
            android.provider.CallLog.Calls.DATE + " DESC ");

    Log.d("FILLLIST", "Calling from filllist");

    startManagingCursor(cursor);
    int numberColumnId = cursor
            .getColumnIndex(android.provider.CallLog.Calls.NUMBER);
    int durationId = cursor
            .getColumnIndex(android.provider.CallLog.Calls.DURATION);
    int contactNameId = cursor
            .getColumnIndex(android.provider.CallLog.Calls.CACHED_NAME);        
    int numTypeId = cursor
            .getColumnIndex(android.provider.CallLog.Calls.CACHED_NUMBER_TYPE);

    Date dt = new Date();
    int hours = dt.getHours();
    int minutes = dt.getMinutes();
    int seconds = dt.getSeconds();
    String currTime = hours + ":" + minutes + ":" + seconds;

    SimpleDateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy");
    Date date = new Date();

    ArrayList<String> callList = new ArrayList<String>();
    cursor.moveToFirst();

    String contactNumber = cursor.getString(numberColumnId);
    String contactName = cursor.getString(contactNameId);
    String duration = cursor.getString(durationId);
    // String callDate = DateFormat.getDateInstance().format(dateId);
    String numType = cursor.getString(numTypeId);

    ContentValues values = new ContentValues();

    values.put("contact_id", 1);
    values.put("contact_name", contactName);
    values.put("number_type", numType);
    values.put("contact_number", contactNumber);
    values.put("duration", duration);
    values.put("date", dateFormat.format(date));
    values.put("current_time", currTime);
    values.put("cont", 1);
    getBaseContext().getContentResolver().notifyChange(
            android.provider.CallLog.Calls.CONTENT_URI,
            new RatedCallsContentObserver(handler));
    db.insert(CallDataHelper.TABLE_NAME, null, values);
    Toast.makeText(getBaseContext(), "Inserted!", Toast.LENGTH_LONG);
    callList.add("Contact Number: " + contactNumber + "\nContact Name: "
            + contactName + "\nDuration: " + duration + "\nDate: "
            + dateFormat.format(date));

    setListAdapter(new ArrayAdapter<String>(this, R.layout.listitem,
            callList));
    ListView lv = getListView();
    lv.setTextFilterEnabled(true);

    lv.setOnItemClickListener(new android.widget.AdapterView.OnItemClickListener() {

        @Override
        public void onItemClick(AdapterView<?> parent, View view,
                int position, long id) {

            Toast.makeText(getApplicationContext(),
                    ((TextView) view).getText(), Toast.LENGTH_SHORT).show();

        }
    });
}

public void registerContentObservers() {

    this.getApplicationContext()
            .getContentResolver()
            .registerContentObserver(
                    android.provider.CallLog.Calls.CONTENT_URI, true,
                    new RatedCallsContentObserver(handler));

}

public void unregisterContentObservers() {

    this.getApplicationContext()
            .getContentResolver()
            .unregisterContentObserver(
                    new RatedCallsContentObserver(handler));

}
    }

And the DatabaseHelper

public class CallDataHelper {

private static final String DATABASE_NAME = "calls.db";
private static final int DATABASE_VERSION = 1;
protected static final String TABLE_NAME = "contact_data";

private Context context;
private SQLiteDatabase db;

public CallDataHelper(Context context) {
    this.context = context;
    OpenHelper openHelper = new OpenHelper(this.context);
    this.db = openHelper.getWritableDatabase();
}

public boolean insert(Integer cId, String cName, String numType,
        String cNum, String dur, String date, String currTime) {
    this.db.execSQL("insert into "
            + TABLE_NAME
            + " (contact_id, contact_name, number_type, contact_number, duration, date, current_time, cont) "
            + " values( " + cId + ", " + cName + ", " + numType + ", "
            + cNum + ", " + dur + ", " + date + ", " + currTime + ", 1)");
    return true;        
}   

public void update(String word) {
    this.db.execSQL("UPDATE words SET cont = cont + 1 WHERE (word= ?)",
            new String[] { word });
}

public void deleteAll() {
    this.db.delete(TABLE_NAME, null, null);
}

public String selectDate() {

    String date = "";
    Cursor cursor = this.db.query(TABLE_NAME, new String[] { "date" },
            "id = ?", new String[] { "max(id)" }, null, null, null);
    if (cursor.moveToFirst()) {
        do {
            date = cursor.getString(0);
        } while (cursor.moveToNext());
    }
    if (cursor != null && !cursor.isClosed()) {
        cursor.close();
        return date;
    } else {
        return "";
    }
}

public List<String> selectAll() {
    List<String> list = new ArrayList<String>();
    Cursor cursor = this.db.query(TABLE_NAME, new String[] { "word" },
            null, null, null, null, "cont desc");
    if (cursor.moveToFirst()) {
        do {
            list.add(cursor.getString(0).toUpperCase());
        } while (cursor.moveToNext());
    }
    if (cursor != null && !cursor.isClosed()) {
        cursor.close();
    }
    return list;
}

public static class OpenHelper extends SQLiteOpenHelper {

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

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE "
                + TABLE_NAME
                + "(id INTEGER PRIMARY KEY AUTOINCREMENT, contact_id INTEGER, contact_name VARCHAR(50), number_type VARCHAR(50), contact_number VARCHAR(50), duration TIME, date DATE, current_time TIME, cont INTEGER)");

    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        Log.w("RatedCalls Database",
                "Upgrading database, this will drop tables and recreate.");
        db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
        onCreate(db);
    }
}
    }

____(Edited)_______

This is what I get now. When I make a new call, the Logcat returns this.

12-16 18:55:27.365: ERROR/AndroidRuntime(767): FATAL EXCEPTION: main
12-16 18:55:27.365: ERROR/AndroidRuntime(767): java.lang.IllegalStateException: database not open
12-16 18:55:27.365: ERROR/AndroidRuntime(767):     at android.database.sqlite.SQLiteDatabase.queryWithFactory(SQLiteDatabase.java:1230)
12-16 18:55:27.365: ERROR/AndroidRuntime(767):     at android.database.sqlite.SQLiteDatabase.query(SQLiteDatabase.java:1189)
12-16 18:55:27.365: ERROR/AndroidRuntime(767):     at android.database.sqlite.SQLiteDatabase.query(SQLiteDatabase.java:1271)
12-16 18:55:27.365: ERROR/AndroidRuntime(767):     at com.psyhclo.CallDataHelper.selectDate(CallDataHelper.java:61)
12-16 18:55:27.365: ERROR/AndroidRuntime(767):     at com.psyhclo.RatedCalls.fillList(RatedCalls.java:98)
12-16 18:55:27.365: ERROR/AndroidRuntime(767):     at com.psyhclo.RatedCalls.access$0(RatedCalls.java:96)
12-16 18:55:27.365: ERROR/AndroidRuntime(767):     at com.psyhclo.RatedCalls$RatedCallsContentObserver.onChange(RatedCalls.java:91)
12-16 18:55:27.365: ERROR/AndroidRuntime(767):     at android.database.ContentObserver$NotificationRunnable.run(ContentObserver.java:43)
12-16 18:55:27.365: ERROR/AndroidRuntime(767):     at android.os.Handler.handleCallback(Handler.java:587)
12-16 18:55:27.365: ERROR/AndroidRuntime(767):     at android.os.Handler.dispatchMessage(Handler.java:92)
12-16 18:55:27.365: ERROR/AndroidRuntime(767):     at android.os.Looper.loop(Looper.java:123)
12-16 18:55:27.365: ERROR/AndroidRuntime(767):     at android.app.ActivityThread.main(ActivityThread.java:3647)
12-16 18:55:27.365: ERROR/AndroidRuntime(767):     at java.lang.reflect.Method.invokeNative(Native Method)
12-16 18:55:27.365: ERROR/AndroidRuntime(767):     at java.lang.reflect.Method.invoke(Method.java:507)
12-16 18:55:27.365: ERROR/AndroidRuntime(767):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
12-16 18:55:27.365: ERROR/AndroidRuntime(767):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
12-16 18:55:27.365: ERROR/AndroidRuntime(767):     at dalvik.system.NativeStart.main(Native Method)

And this is my activity:

    public class RatedCalls extends ListActivity {

private static final String LOG_TAG = "RATEDCALLSOBSERVER";
private Handler handler = new Handler();
private SQLiteDatabase db;
private CallDataHelper cdh;
StringBuilder sb = new StringBuilder();
OpenHelper openHelper = new OpenHelper(RatedCalls.this);

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    registerContentObservers();
    Log.d("FILLLIST", "calling from onCreate()");
    cdh = new CallDataHelper(this);
    db = openHelper.getWritableDatabase();

}

@Override
protected void onDestroy() {
    super.onDestroy();
    if (openHelper != null) {
        openHelper.close();
    }
    if (cdh != null) {
        cdh.close();
    }
}

@Override
protected void onPause() {
    super.onPause();
    if (openHelper != null) {
        openHelper.close();
    }
    if (cdh != null) {
        cdh.close();
    }
}

@Override
public void onResume() {

    super.onResume();
    openOrCreateDatabase("calls.db", SQLiteDatabase.CREATE_IF_NECESSARY,
            null);

}

class RatedCallsContentObserver extends ContentObserver {
    public RatedCallsContentObserver(Handler h) {
        super(h);
    }

    @Override
    public boolean deliverSelfNotifications() {
        return true;

    }

    @Override
    public void onChange(boolean selfChange) {
        Log.d(LOG_TAG, "RatedCallsContentObserver.onChange( " + selfChange
                + ")");
        super.onChange(selfChange);
        fillList();

    }
}

private void fillList() {

    String lastDate = cdh.selectDate();

    Cursor cursor = getContentResolver().query(
            android.provider.CallLog.Calls.CONTENT_URI, null,
            android.provider.CallLog.Calls.DATE + " < ?",
            new String[] { lastDate },
            android.provider.CallLog.Calls.DATE + " DESC ");

    Log.d("FILLLIST", "Calling from filllist");

    startManagingCursor(cursor);
    int numberColumnId = cursor
            .getColumnIndex(android.provider.CallLog.Calls.NUMBER);
    int durationId = cursor
            .getColumnIndex(android.provider.CallLog.Calls.DURATION);
    int contactNameId = cursor
            .getColumnIndex(android.provider.CallLog.Calls.CACHED_NAME);
    int numTypeId = cursor
            .getColumnIndex(android.provider.CallLog.Calls.CACHED_NUMBER_TYPE);

    Date dt = new Date();
    int hours = dt.getHours();
    int minutes = dt.getMinutes();
    int seconds = dt.getSeconds();
    String currTime = hours + ":" + minutes + ":" + seconds;

    SimpleDateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy");
    Date date = new Date();

    ArrayList<String> callList = new ArrayList<String>();
    cursor.moveToFirst();

    String contactNumber = cursor.getString(numberColumnId);
    String contactName = cursor.getString(contactNameId);
    String duration = cursor.getString(durationId);
    // String callDate = DateFormat.getDateInstance().format(dateId);
    String numType = cursor.getString(numTypeId);
    stopManagingCursor(cursor);
    ContentValues values = new ContentValues();

    values.put("contact_id", 1);
    values.put("contact_name", contactName);
    values.put("number_type", numType);
    values.put("contact_number", contactNumber);
    values.put("duration", duration);
    values.put("date", dateFormat.format(date));
    values.put("current_time", currTime);
    values.put("cont", 1);
    getBaseContext().getContentResolver().notifyChange(
            android.provider.CallLog.Calls.CONTENT_URI,
            new RatedCallsContentObserver(handler));
    db.insert(CallDataHelper.TABLE_NAME, null, values);
    Toast.makeText(getBaseContext(), "Inserted!", Toast.LENGTH_LONG);
    callList.add("Contact Number: " + contactNumber + "\nContact Name: "
            + contactName + "\nDuration: " + duration + "\nDate: "
            + dateFormat.format(date));

    setListAdapter(new ArrayAdapter<String>(this, R.layout.listitem,
            callList));
    ListView lv = getListView();
    lv.setTextFilterEnabled(true);

    lv.setOnItemClickListener(new android.widget.AdapterView.OnItemClickListener() {

        @Override
        public void onItemClick(AdapterView<?> parent, View view,
                int position, long id) {

            Toast.makeText(getApplicationContext(),
                    ((TextView) view).getText(), Toast.LENGTH_SHORT).show();

        }
    });
}

public void registerContentObservers() {

    this.getApplicationContext()
            .getContentResolver()
            .registerContentObserver(
                    android.provider.CallLog.Calls.CONTENT_URI, true,
                    new RatedCallsContentObserver(handler));

}

public void unregisterContentObservers() {

    this.getApplicationContext()
            .getContentResolver()
            .unregisterContentObserver(
                    new RatedCallsContentObserver(handler));

}
 }
surhidamatya
  • 2,419
  • 32
  • 56
rogcg
  • 10,451
  • 20
  • 91
  • 133

3 Answers3

47

I think the problem is that you need to close the db when your activity is destroyed. Try adding this to your activity:

@Override
protected void onDestroy() {
    super.onDestroy();
    if (openHelper != null) {
        openHelper.close();
    }
    if (cdh != null) {
        cdh.close();
    }
}

and add this to your CallDataHelper class:

public void close() {
    // NOTE: openHelper must now be a member of CallDataHelper;
    // you currently have it as a local in your constructor
    if (openHelper != null) {
        openHelper.close();
    }
}

EDIT 2: Also change the CallDataHelper constructor to:

// Declare openHelper as a member variable
OpenHelper openHelper = null;

public CallDataHelper(Context context) {
    this.context = context;
    openHelper = new OpenHelper(this.context);
    this.db = openHelper.getWritableDatabase();
}

This should successfully close the database connection that was created by both of your OpenHelper instances.

( EDITED to reflect the fact that you use two OpenHelper instances.)

Dan Breslau
  • 11,472
  • 2
  • 35
  • 44
  • I did it, and after a time the application was running, the error is: 12-16 18:09:47.406: ERROR/Database(21184): android.database.sqlite.DatabaseObjectNotClosedException: Application did not close the cursor or database object that was opened here I think it is the cursor that has to be closed. How can I close it? – rogcg Dec 16 '10 at 20:49
  • @psyhclo: Did you see my edit? You have two OpenHelper instances, each of which are used to get a DB connection, so both of them must be closed. (You might want to combine those two OpenHelpers; that's your call.) – Dan Breslau Dec 16 '10 at 20:50
  • The method close() must be inside what class, the OpenHelper or CallDataHelper. The openHelper field is on CallDataHelper, but when I try to declare the method close, it says this: openHelper cannot be resolved to a variable. And offers to create the field. Obs: I'm declaring the method close() on CallDataHelper. And if I try to do like this: public void close() { // NOTE: openHelper must now be a member of CallDataHelper; // you currently have it as a local in your constructor if (this.db != null) { this.db.close(); } – rogcg Dec 16 '10 at 20:59
  • @psyhclo: re `openHelper`, see EDIT 2 above. That edit includes a re-write of the constructor, as well as a declaration of the openHelper member. The close() method is also for `CallDataHelper`, to close its own `OpenHelper` instance. (`OpenHelper` extends `SQLiteOpenHelper `, which defines `close()` ) – Dan Breslau Dec 16 '10 at 21:03
  • And the onResume() method. What should I do? Can you edit your answer with the code for this? Because it returns this error:12-16 18:36:19.115: ERROR/AndroidRuntime(26848): java.lang.IllegalStateException: database not open – rogcg Dec 16 '10 at 21:17
  • @psyhclo: As Arhimed observed, you're calling `onStop()` directly, which you probably shouldn't be doing. Remove that call, and see if you still get the `IllegalStateException`. – Dan Breslau Dec 16 '10 at 21:31
  • you say calling onStop() after fillList() inside the onChange() method? – rogcg Dec 16 '10 at 21:32
  • I edited the code. Check whats happening now. The error, and how the code is. Thanks a lot! – rogcg Dec 16 '10 at 21:37
  • You shouldn't be calling onStop() yourself. (I saw your earlier question, so I know you added that call recently; but I don't really understand that question, so I'm not sure what you were trying to achieve.) – Dan Breslau Dec 16 '10 at 21:39
  • @psyhclo: The IllegalStateException is almost certainly a different problem, and belongs in a different question. Please don't modify your questions to raise new problems to be addressed -- it makes the question and answers much less useful to other readers. – Dan Breslau Dec 16 '10 at 21:41
  • @psyhclo: OK, I took another look, and saw that you're calling the `close()` methods from `onPause()`. Don't do that. `onPause()` is not the same as `onDestroy()`. Not even close. – Dan Breslau Dec 16 '10 at 22:16
  • The call to `openOrCreateDatabase()` from `onResume()` is more than a little suspicious as well. – Dan Breslau Dec 16 '10 at 22:25
  • So how to open the database. Because, now for, onResume Im calling openOrCreateDatabase() but it keep saying database not open. So what would be the implementation for onResume(). – rogcg Dec 17 '10 at 01:29
  • @psyhclo: The answer there is not to close the database in onPause(). As far as I know, an activity never needs to do that. (I'm far from an expert on this, however.) – Dan Breslau Dec 17 '10 at 01:37
  • In fact I think I should close the database in onPause(), because I've commented onPause, and just left onDestroy() closing, the database, and when I tried to go to another app, it shows the error that says the database was not closed, so I think onPause() needs it too.________ I just want to know how to open the database. I'm trying to use this method public static SQLiteDatabase openDatabase (String path, SQLiteDatabase.CursorFactory factory, int flags) but I dont know what is the CursorFactory parameter, and the flags parameter. – rogcg Dec 17 '10 at 01:47
  • As I understand it, onPause() will always be called as part of the process of stopping or shutting down your app. Hence it doesn't need to repeat the code that belongs in onStop(). And usually, all that onPause() needs to do is to preserve the state of the UI (text entered by the user, etc.) So onResume() should already have a good DB connection to work with, provided that the DB connection was valid before onPause() was called. See http://developer.android.com/reference/android/app/Activity.html – Dan Breslau Dec 17 '10 at 02:39
  • @DanBreslau I have automatic service which is called on every 3mins and updating data of database. how to manage both activity and service? – Pratik Butani Jan 10 '14 at 10:09
2

Literally understand database is not normally closed, the actual because repeated instantiation your database, or connect you have been set up, and you and try to open another connection there will be no exception. So the solution is, make sure that you open only a connection.

1

I suspect fillList() is called multiple times during a RatedCalls "session". So each time you create a new Cursor instance and call startManagingCursor() on it. Doing so you accumulate a list of Cursors - ArrayList<ManagedCursor> to be precise that is declared in ListActivity. Normally they (cursors that are being managed) will be closed automatically in ListActivity.onDestroy(). Could you confirm your RatedCalls actually passes onDestroy().

Do you override somehow the ListActivity.onDestroy() in RatedCalls. If yes, then do you call super.onDestroy() (you should call it) ?

I don't get the purpose of calling onStop() in RatedCallsContentObserver.onChange() after the fillList();. I believe Activity life-cycle callbacks should be called by OS only. So maybe calling a life-cycle callback manually you somehow prevent from onDestroy() to be called by OS (just a guess)?

UPDATE:

Dan Breslau is right - it's db object that is not closed. Sorry for misleading.

Vit Khudenko
  • 28,288
  • 10
  • 63
  • 91
  • The stack traces shows that the unclosed object is the database connection itself, which is returned via `SQLiteOpenHelper.getWritableDatabase`. Although `startManagingCursor` ensures that the cursor will be properly disposed of, I'm not aware of anything that will automagically close database connections when the activity is destroyed. (My lack of awareness is not definitive, however :-) – Dan Breslau Dec 16 '10 at 21:15
  • @Dan Breslau: OMG, you are right!!! it's 'call.db' that is not closed! First time I opened the question the stack trace was not nicely formatted, so probably I missed that. – Vit Khudenko Dec 16 '10 at 21:24
  • @Arhimed your answer is totally right. I knew that was happening, I just didn't knew how to solve that. In fact, the original question is this http://stackoverflow.com/questions/4450778/android-contentobserver-never-stoped , and this question we are now came from that answer for that question. What should I do for stop that fillList() is called multiple times during a RatedCalls "session"? Man you opened my mind! Thanks. – rogcg Dec 16 '10 at 21:25
  • Do you think I should Override an onDestroy() method and inside the onDestroy should i do a stopManagingCursor()? So do you think I instead calling onStop() after fillList() I should call onDestroy? I'm gonna update my code. – rogcg Dec 16 '10 at 21:27
  • @psyhclo: if you **don't** override the `onDestroy()`, then you will get that automatically - all managed cursors will be closed. please, see what Dan Breslau says - it is the `private SQLiteDatabase db` that is not closed. Move your attention focus on that. – Vit Khudenko Dec 16 '10 at 21:36
  • 1
    @psyhclo: without knowing your context (business logic) it's hard to advice you here further. for instance, you close databases in `onStop()`, however you don't do anything in `onResume()`. So if your activity goes in onStop() -> onResume() scenario you will get closed databases which could be an unexpected state for you. Also I still don't get what were you trying to achieve by calling onStop() after fillList()... – Vit Khudenko Dec 16 '10 at 21:48