2013-08-09 33 views
31

Znalazłem wiele rzeczy, takich jak close the connection i close the cursor, ale robię wszystkie te rzeczy. Nadal przecieki połączeń SQLite i dostaję taki komunikat:Połączenie SQLite wyciekło, chociaż wszystko było zamknięte

A SQLiteConnection object for database was leaked! 

mam menedżera Baza ta, którą nazywamy w moich działaniach z następującego kodu:

DatabaseManager dbm = new DatabaseManager(this); 

Kodeksu mojej bazy danych Klasa menedżer teraz następująco:

public class DatabaseManager { 

    private static final int DATABASE_VERSION = 9; 
    private static final String DATABASE_NAME = "MyApp"; 
    private Context context = null; 
    private DatabaseHelper dbHelper = null; 
    private SQLiteDatabase db = null; 


    public static class DatabaseHelper extends SQLiteOpenHelper { 

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

     @Override 
     public void onCreate(SQLiteDatabase db) { 

        //create database tables 
     } 

     @Override 
     public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 
         //destroy and recreate them 
     } 

    } 

    public DatabaseManager(Context ctx) { 
     this.context = ctx; 
    } 

    private DatabaseManager open() throws SQLException { 
     dbHelper = new DatabaseHelper(context); 
     db = dbHelper.getWritableDatabase(); 

     if (!db.isReadOnly()) { 
      db.execSQL("PRAGMA foreign_keys = ON;"); 
     } 

     return this; 
    } 

    private void close() { 
     dbHelper.close(); 
    } 
} 

Kiedy wywołać metodę bazy, zrobić następujące rzeczy:

public Object getData() { 

    open(); 

      //... database operations take place ... 

    close(); 

    return data; 
} 

Ale tak jak powiedziałem, wciąż otrzymuję ostrzeżenie o wycieku SQLite.

Co robię źle?

+0

myślę, że zamykają tylko Twój DBHelper, ale nie samej bazy danych – Opiatefuchs

+0

myślę, że należy zadzwonić db.close () też –

+0

Nie ma znaczenia, jeśli to zrobię, czy nie. I tak otrzymam wiadomość. Ale gdzieś czytam, że nie musisz tego robić, kiedy wywołujesz dbHelper.close() – flp

Odpowiedz

113

pogrubiony czcionki w cytacie odpowiada tej części w kodzie:

private DatabaseManager open() throws SQLException { 
    dbHelper = new DatabaseHelper(context); 
    db = dbHelper.getWritableDatabase(); 

od: http://www.androiddesignpatterns.com/2012/05/correctly-managing-your-sqlite-database.html

Podejście nr 1: Użyj Abstract Factory do instancji SQLiteOpenHelper

Zadeklaruj pomocnika bazy danych jako statyczną instancję zmienną i użyj wzoru fabryki abstrakcyjnej , aby zagwarantować pojedynczą propozycję erty. Poniższy przykładowy kod z serii powinien dać ci dobry pomysł, jak poprawnie zaprojektować klasę DatabaseHelper.

Statyczna metoda getInstance zapewnia, że ​​tylko jeden DatabaseHelper będzie kiedykolwiek istnieć. Jeśli obiekt mInstance nie został zainicjalizowany, zostanie utworzony jeden obiekt. Jeśli masz już utworzony , po prostu zostanie zwrócony.

Powinieneś Nie powinieneś zainicjować obiektu pomocnika przy pomocy new DatabaseHelper(context).
Zamiast tego zawsze używaj: DatabaseHelper.getInstance(context), ponieważ gwarantuje, że tylko jeden pomocnik bazy danych będzie istniał przez cały cykl życia aplikacji.

public static class DatabaseHelper extends SQLiteOpenHelper { 

    private static DatabaseHelper mInstance = null; 

    private static final String DATABASE_NAME = "database_name"; 
    private static final String DATABASE_TABLE = "table_name"; 
    private static final int DATABASE_VERSION = 1; 

    public static DatabaseHelper getInstance(Context ctx) { 

    // Use the application context, which will ensure that you 
    // don't accidentally leak an Activity's context. 
    // See this article for more information: http://bit.ly/6LRzfx 
    if (mInstance == null) { 
     mInstance = new DatabaseHelper(ctx.getApplicationContext()); 
    } 
    return mInstance; 
    } 

    /** 
    * Constructor should be private to prevent direct instantiation. 
    * make call to static factory method "getInstance()" instead. 
    */ 
    private DatabaseHelper(Context ctx) { 
    super(ctx, DATABASE_NAME, null, DATABASE_VERSION); 
    } 
} 
+1

wydaje się naprawić problem. Zrobię dalsze dochodzenie. – flp

+1

Świetna odpowiedź. Wpadłem na ten problem podczas pracy z wieloma IntentServices, wszystkie pracujące jednocześnie z dwóch różnych baz danych. Użyłem tej odpowiedzi, z wyjątkiem dwóch oddzielnych metod Factory. Usunięto wszystkie błędy wycieku pamięci. Dodał pół sekundy lub dwie do czasu wykonania, prawdopodobnie dlatego, że nie mam już otwartych jednocześnie instancji wielu baz danych. –

+1

Naprawiono ten sam problem dla mnie. –

1
private void method() { 
     Cursor cursor = query(); 
     if (flag == false) { // WRONG: return before close() 
      return; 
     } 
     cursor.close(); 
    } 

Dobra praktyka powinna być tak:

private void method() { 
     Cursor cursor = null; 
     try { 
      cursor = query(); 
     } finally { 
      if (cursor != null) 
       cursor.close(); // RIGHT: ensure resource is always recovered 
     } 
    }