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类中.
所以主要的问题是 - 什么是错的以及如何正确地做到这一点?
你不应该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例如,关闭它)