2010-09-15 42 views
1

我知道这是一个很长的帖子。请不要介意。Android SQLite内存泄露

Leak found 
E/Database(4549): java.lang.IllegalStateException: mPrograms size 1 
E/Database(4549): at android.database.sqlite.SQLiteDatabase.finalize(SQLiteDatabase.java:1668) 
E/Database(4549): at dalvik.system.NativeStart.run(Native Method) 
E/Database(4549): Caused by: java.lang.IllegalStateException: /data/data/com.rjblackbox.droid.fvt/databases/fvt.db SQLiteDatabase created and never closed 
E/Database(4549): at android.database.sqlite.SQLiteDatabase.<init>(SQLiteDatabase.java:1694) 
E/Database(4549): at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:738) 
E/Database(4549): at android.database.sqlite.SQLiteDatabase.openOrCreateDatabase(SQLiteDatabase.java:760) 
E/Database(4549): at android.database.sqlite.SQLiteDatabase.openOrCreateDatabase(SQLiteDatabase.java:753) 
E/Database(4549): at android.app.ApplicationContext.openOrCreateDatabase(ApplicationContext.java:473) 
E/Database(4549): at android.content.ContextWrapper.openOrCreateDatabase(ContextWrapper.java:193) 
E/Database(4549): at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:98) 
E/Database(4549): at com.rjblackbox.droid.fvt.FVTDataHelper.<init>(FVTDataHelper.java:37) 
E/Database(4549): at com.rjblackbox.droid.fvt.FVTNotificationService.getNextEntry(FVTNotificationService.java:91) 
E/Database(4549): at com.rjblackbox.droid.fvt.FVTNotificationService.access$2(FVTNotificationService.java:90) 
E/Database(4549): at com.rjblackbox.droid.fvt.FVTNotificationService$1.run(FVTNotificationService.java:53) 
E/Database(4549): at android.os.Handler.handleCallback(Handler.java:587) 
E/Database(4549): at android.os.Handler.dispatchMessage(Handler.java:92) 
E/Database(4549): at android.os.Looper.loop(Looper.java:123) 
E/Database(4549): at android.app.ActivityThread.main(ActivityThread.java:4363) 
E/Database(4549): at java.lang.reflect.Method.invokeNative(Native Method) 
E/Database(4549): at java.lang.reflect.Method.invoke(Method.java:521) 
E/Database(4549): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:860) 
E/Database(4549): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618) 
E/Database(4549): at dalvik.system.NativeStart.main(Native Method) 

这是我的内存泄漏的堆栈跟踪。以下是我的数据助手的源代码。除了活动,我还有一个服务访问数据。这对于有经验的开发人员来说肯定是小菜一碟。

public class FVTDataHelper { 
    private static final String DB_NAME = "fvt.db"; 
    private static final int DB_VERSION = 1; 

    private static final String TABLE_TIMERS = "timers"; 
    private static final String[] TABLE_TIMERS_COLUMNS = {"id", "name", "added", "expires"}; 
    private static final String TIMER_INSERT = "INSERT INTO " + 
    TABLE_TIMERS + "('name', 'added', 'expires') VALUES (?, ?, ?)"; 

    private SQLiteDatabase db; 
    private Cursor c; 
    private SQLiteStatement timerInsert; 

    //Constructor 
    public FVTDataHelper(Context ctx) { 
     OpenHelper oh = new OpenHelper(ctx); 
     db = oh.getWritableDatabase(); 
     timerInsert = db.compileStatement(TIMER_INSERT); 
    } 

    public void addTimerEntry(TimerEntry entry) { 
     String name = entry.getName(); 
     long added = entry.getAdded(); 
     long expires = entry.getExpires(); 

     timerInsert.bindString(1, name); 
     timerInsert.bindString(2, Long.toString(added)); 
     timerInsert.bindString(3, Long.toString(expires)); 
     timerInsert.executeInsert(); 
    } 

    public List<TimerEntry> getTimerEntries() { 
     ArrayList<TimerEntry> entries = new ArrayList<TimerEntry>(); 
     c = db.query(TABLE_TIMERS, TABLE_TIMERS_COLUMNS, null, null, null, null, "expires asc"); 

     if(c.moveToFirst()) { 
      int id; 
      String name; 
      long added; 
      long expires; 

      do { 
       id = c.getInt(0); 
       name = c.getString(1); 
       added = c.getLong(2); 
       expires = c.getLong(3); 

       if((System.currentTimeMillis() - added) >= 0) { 
        entries.add(new TimerEntry(id, name, added, expires)); 
       } 
      } while(c.moveToNext()); 
     } 
     c.close(); 
     return entries; 
    } 

    public void deleteTimerEntry(int id) { 
     db.delete(TABLE_TIMERS, "id=" + id, null); 
    } 

    //Helper class for creating and updating database 
    private static class OpenHelper extends SQLiteOpenHelper { 
     private Context ctx; 

     public OpenHelper(Context ctx) { 
      super(ctx, DB_NAME, null, DB_VERSION); 
      this.ctx = ctx; 
     } 

     @Override 
     public void onCreate(SQLiteDatabase db) { 
      String sqlDump = getSQLDump(); 

      String[] statements = sqlDump.split("\n"); 
      for(String statement : statements) { 
       if(DEBUG) Log.d(TAG, statement); 
       db.execSQL(statement); 
      } 
     } 

     @Override 
     public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 
      onCreate(db); 
     } 

     //Helper: Returns SQL statements from the dump 
     private String getSQLDump() { 
      StringBuilder sb = new StringBuilder(); 
      try { 
       InputStream is = ctx.getAssets().open("dump.sql"); 
       int c; 
       while((c = is.read()) != -1) { 
        sb.append((char) c); 
       } 
       is.close(); 
      } catch (IOException e) { 
       Log.d(TAG, e.getMessage()); 
      } 
      return sb.length() > 0 ? sb.toString() : ";"; 
     } 
    } 
} 
+0

代码效果很好。该应用程序正常工作。我只是想处理这个内存泄漏。 – 2010-09-15 17:54:59

+0

这不完全是内存泄漏 – Falmarri 2010-09-15 21:19:49

回答

9

你需要重新打开它之前关闭数据库,堆栈跟踪信息是有关的问题是什么很清楚。

一个解决方案是使用android.app.Application类,并将打开的数据库实例存储在那里。如果您这样做,请务必使用Application上下文而不是Activity上下文打开数据库,否则当您的活动被破坏时可能会泄漏上下文。

另一解决方案是关闭的数据库中的onDestroy /的onStop等,并在的onCreate /调用onStart重新打开它,等等

+0

感谢@AI的解决方案。 – 2010-09-16 09:26:24