android SQLiteOpenHelper和Leaks

Sta*_*tan 1 sqlite android memory-leaks

我想用一些从我的数据库返回数据的其他方法(比如getStreetsCursor)来创建SQLiteOpenHelper.所以我写了这样的东西:

public class DBHelper extends SQLiteOpenHelper { 
    public static final String DB_NAME="some.db"; 
    public static final String T1_NAME="streets"; 
    public static final String T1_FNAME1="name"; 
    public static final String T2_NAME="addresses"; 
    public static final String T2_FNAME1="name"; 
    public static final String T2_FNAME2="address"; 

    private Context appContext;

  public DBHelper(Context context) { 
    super(context, DB_NAME, null, 1);
    appContext=context;
  }

  public Cursor getStreetsCursor(String chars) {
  SQLiteDatabase dbReadable=this.getReadableDatabase();
      Cursor curStreets = dbReadable.query(DBHelper.T1_NAME, 
                new String[] {"_id",DBHelper.T1_FNAME1}, 
                DBHelper.T1_FNAME1+" LIKE(\""+chars.toUpperCase()+"%\")",
                null, null, null, DBHelper.T1_FNAME1);

    return curStreets;
  }
Run Code Online (Sandbox Code Playgroud)

有几种方法,如DBHelper中定义的getStreetsCursor(getAddresses,getAddress4等).我想如果它是一个DB Helper它肯定应该有这样的方法我的意思是DBHelper是它们的逻辑占位符.

我在活动中做的是创建一个新的DBHelper实例并将其存储在活动的私有字段(称为mDBHelper)中.另外在onDestroy活动方法中我有mDBHelper.close().

private DBHelper mDBHelper;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mDBHelper = new DBHelper(this);
...
    @Override
    protected void onDestroy() {
        if (mDBHelper!=null){
            mDBHelper.close();
            Log.i(APP_TAG,"mDBHelper.close() in "+this.getClass());
        }
        super.onDestroy();
    }
Run Code Online (Sandbox Code Playgroud)

这些活动仅以一种方式使用mDBHelper - 通过调用它的自定义方法,如mDBHelper.getStreetsCursor().最后我在logcat中发现了一条关于我的应用程序在使用DBHelper的此类活动中出现泄漏的异常消息.它说"数据库永远不会关闭"之类的东西.所以我决定在返回之前在我的每个自定义方法中添加对close()方法的调用.所以它看起来像:

      public Cursor getStreetsCursor(String chars) {
          SQLiteDatabase dbReadable=this.getReadableDatabase();
          Cursor curStreets = dbReadable.query(DBHelper.T1_NAME, 
                    new String[] {"_id",DBHelper.T1_FNAME1}, 
                    DBHelper.T1_FNAME1+" LIKE(\""+chars.toUpperCase()+"%\")",
                    null, null, null, DBHelper.T1_FNAME1);

            dbReadable.close();
            return curStreets;
      }
Run Code Online (Sandbox Code Playgroud)

现在我没有泄漏,但得到了下一个问题 - 只有第一次调用mDBHelper.getStreetsCursor()才真正执行.所有下一个调用都返回null.这是由于dbReadable.close(); 线.如果我删除它everythig工作正常但我再次泄漏.所以我无法弄清楚什么是错的.在每个自定义方法中,我都得到了SQLiteDatabase dbReadable = this.getReadableDatabase(); 应返回一个可读实例的行,但在执行close()方法之后却没有.我想它是关于我的自定义方法,因为它们在DBHelper的实例中调用.getReadableDatabase().如果我将这些方法直接放在活动中,一切正常 - 没有泄漏异常,每次方法返回正确的数据.但我想将这些方法放在我的DBHelper类中.

所以主要的问题是 - 什么是错的以及如何正确地做到这一点?

cch*_*son 5

你不应该SQLiteDatabase dbReadable=this.getReadableDatabase(); 在每个方法中都有一个getSomethingCursor因为请求数据库对象很昂贵(我想我在SO中读过它).

因此,您可以从构造函数中创建对象

  SQLiteDatabase dbReadable;

  public DBHelper(Context context) { 
    super(context, DB_NAME, null, 1);
    appContext=context;
    dbReadable=this.getReadableDatabase()
  }


  public Cursor getStreetsCursor(String chars) {
          Cursor curStreets = dbReadable.query(DBHelper.T1_NAME, 
                    new String[] {"_id",DBHelper.T1_FNAME1}, 
                    DBHelper.T1_FNAME1+" LIKE(\""+chars.toUpperCase()+"%\")",
                    null, null, null, DBHelper.T1_FNAME1);

            return curStreets;
  }
Run Code Online (Sandbox Code Playgroud)

创建一个关闭数据库句柄的方法:

public closeDb() {
    if (dbReadable != null) { dbReadable.close();}
}
Run Code Online (Sandbox Code Playgroud)

在你的活动中:

    @Override
    protected void onDestroy() {
        if (mDBHelper!=null){
            mDBHelper.closeDb();
            Log.i(APP_TAG,"mDBHelper.close() in "+this.getClass());
        }
        super.onDestroy();
    }
Run Code Online (Sandbox Code Playgroud)

并使用startManagingCursor(如果SDK <HoneyComb)让您的活动管理您的光标(onDestroy例如,关闭它)