2013-08-09 6 views
31

Ich fand viele Sachen wie close the connection und close the cursor, aber ich mache all diese Sachen. Immer noch die SQLite Anschluss undicht und ich bekomme eine Warnung wie folgt aus:SQLite Verbindung durchgesickert, obwohl alles geschlossen

A SQLiteConnection object for database was leaked! 

ich diesen einen Datenbankmanager, die ich mit dem folgenden Code in meiner Tätigkeit nennen:

DatabaseManager dbm = new DatabaseManager(this); 

Der Code meiner Datenbank Manager-Klasse folgt jetzt:

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

Wenn ich eine Datenbank-Methode aufrufen, das tue ich das folgende:

public Object getData() { 

    open(); 

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

    close(); 

    return data; 
} 

Aber wie gesagt, ich bekomme immer noch diese SQLite-Verbindung durchgesickert Warnung.

Was mache ich falsch?

+0

Ich denke, Sie schließen nur Ihre DBHelper, aber nicht die Datenbank selbst – Opiatefuchs

+0

Ich denke, Sie sollten nennen db.close () too –

+0

Egal, ob ich das mache oder nicht. Ich werde die Nachricht trotzdem bekommen. Aber ich habe irgendwo gelesen, dass du das nicht tun musst, wenn du dbHelper.close() aufruft – flp

Antwort

113

Die fett gedruckten Schrift im Zitat entspricht in Ihrem Code zu diesem Teil:

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

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

Ansatz # 1: eine abstrakte Fabrik Verwenden Sie die SQLiteOpenHelper

zu instanziieren

Deklarieren Sie Ihren Datenbank-Helper als statische Instanzvariable und verwenden Sie das Abstract Factory-Muster , um die Singleton-Prop zu garantieren bitter. Der folgende Beispielcode sollte Ihnen eine gute Idee geben, wie Sie über die DatabaseHelper-Klasse richtig entwerfen können.

Die static factory getInstance-Methode stellt sicher, dass immer nur ein DatabaseHelper zu einem bestimmten Zeitpunkt existiert. Wenn das Objekt mInstance nicht initialisiert wurde, wird eines erstellt. Wenn man bereits erstellt hat, wird es einfach zurückgegeben.

Sie sollten initialisieren nicht Ihr Objekt Helfer mit new DatabaseHelper(context) verwenden.
Verwenden Sie stattdessen DatabaseHelper.getInstance(context), da es garantiert, dass nur eine Datenbank Helper über den gesamten Lebenszyklus der Anwendung vorhanden ist.

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

scheint das Problem zu beheben. Ich werde weitere Untersuchung machen – flp

+1

Große Antwort. Ich habe bei der Arbeit mit mehreren IntentServices, die alle gleichzeitig aus zwei verschiedenen Datenbanken arbeiten, auf dieses Problem gestoßen. Ich habe diese Antwort nur mit zwei separaten Factory-Methoden verwendet. Alle Speicherleckfehler behoben. Es hat eine halbe Sekunde oder zwei zur Ausführungszeit hinzugefügt, wahrscheinlich weil ich nicht mehr mehrere Datenbankinstanzen gleichzeitig geöffnet habe. –

+1

Das behebt das gleiche Problem für mich. –

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

Gute Praxis sollte wie folgt sein:

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