2013-08-09 11 views
31

Ho trovato molte cose come close the connection e close the cursor, ma faccio tutto questo. Ancora le perdite di connessione SQLite e ottengo un avvertimento come questo:SQLite Connessione trapelata anche se tutto chiuso

A SQLiteConnection object for database was leaked! 

Ho un gestore di database questo, che io chiamo le mie attività con il seguente codice:

DatabaseManager dbm = new DatabaseManager(this); 

Il codice del mio database classe Manager segue ora:

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

Quando chiamo un metodo di database, faccio la seguente cosa:

public Object getData() { 

    open(); 

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

    close(); 

    return data; 
} 

Ma come ho detto, continuo a ricevere questo avviso SQLite.

Cosa sto sbagliando?

+0

Credo che si sta chiudendo solo tuo DBHelper, ma non il database stesso – Opiatefuchs

+0

Penso che si dovrebbe chiamare db.Close () anche –

+0

Non importa, se lo faccio o no. Avrò comunque il messaggio. Ma ho letto da qualche parte, che non hai bisogno di farlo, quando chiami dbHelper.close() – flp

risposta

113

Il tipo di carattere in grassetto nella citazione corrisponde a questa parte nel codice:

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

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

Approccio # 1: Utilizzare un Abstract Factory per creare un'istanza del SQLiteOpenHelper

Dichiarare l'helper del database come variabile di istanza statica e utilizzare il modello di fabbrica astratta per garantire il puntello singleton erty. Il codice di esempio di seguito dovrebbe darti una buona idea su come andare su progettando correttamente la classe DatabaseHelper.

Il metodo statico getInstance di fabbrica garantisce che solo un DatabaseHelper esisterà in qualsiasi momento. Se l'oggetto istanza non è stato inizializzato, ne verrà creato uno. Se uno ha già , verrà semplicemente restituito.

È necessario non inizializzare l'oggetto helper utilizzando con new DatabaseHelper(context).
Invece, utilizzare sempre DatabaseHelper.getInstance(context), in quanto garantisce che solo un aiutante banca dati esisterà in tutto il ciclo di vita dell'intera applicazione.

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

sembra risolvere il problema. Farò ulteriori indagini – flp

+1

Ottima risposta. Mi sono imbattuto in questo problema mentre lavoravo con più IntentServices, tutti funzionanti contemporaneamente su due diversi database. Ho usato questa risposta tranne con due metodi di fabbrica separati. Cancellato tutti gli errori di perdita di memoria. Ha aggiunto mezzo secondo o due al tempo di esecuzione, probabilmente perché non ho più istanze di database aperte contemporaneamente. –

+1

Questo ha risolto lo stesso problema per me. –

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

buona pratica dovrebbe essere simile a questo:

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