40

我发现了很多类似close the connectionand的东西close the cursor,但我做所有这些东西。SQLite 连接仍然泄漏,我收到如下警告:

A SQLiteConnection object for database was leaked!

我有一个数据库管理器,我使用以下代码在我的活动中调用它:

DatabaseManager dbm = new DatabaseManager(this);

我的数据库管理器类的代码现在如下:

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

当我调用数据库方法时,我会做以下事情:

public Object getData() {

    open();

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

    close();

    return data;
}

但正如我所说,我仍然收到此 SQLite 连接泄漏警告。

我究竟做错了什么?

4

3 回答 3

144

引用中的粗体字体对应于代码中的这一部分:

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

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

方法 #1:使用抽象工厂实例化 SQLiteOpenHelper

将您的数据库助手声明为静态实例变量并使用抽象工厂模式来保证单例属性。下面的示例代码应该让您了解如何正确设计 DatabaseHelper 类。

静态工厂 getInstance 方法确保在任何给定时间只存在一个 DatabaseHelper。如果 mInstance 对象尚未初始化,则将创建一个。如果已经创建了一个,那么它将被简单地返回。

您不应该使用 with 初始化您的辅助对象new DatabaseHelper(context)
相反,请始终使用 DatabaseHelper.getInstance(context),因为它保证在整个应用程序的生命周期中只存在一个数据库助手。

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);
  }
}
于 2013-08-09T14:12:11.163 回答
3

上述接受答案的完整示例:它可能对某人有所帮助。

助手类:

public class DatabaseHelper extends SQLiteOpenHelper {

private static final String DATABASE_NAME = "sample.db";
private static final int DATABASE_VERSION = 1;

private static DatabaseHelper mInstance;

private DatabaseHelper(@Nullable Context context) {
    super(context, DATABASE_NAME, null, DATABASE_VERSION);
}

public static synchronized DatabaseHelper getInstance(Context context) {

    if (mInstance == null) {
        mInstance = new DatabaseHelper(context.getApplicationContext());
    }
    return mInstance;
}

@Override
public void onCreate(SQLiteDatabase db) {

    // create table stuff


}

@Override
public void onUpgrade(SQLiteDatabase db, int i, int i1) {

 // drop table stuff

    onCreate(db);
 }
}

活动:

SQLiteDatabase database = DatabaseHelper.getInstance(getApplicationContext()).getWritableDatabase();

Cursor cursor = database.query("query");

if (cursor != null) {
   while (cursor.moveToNext()) {
    // stuff
     }
   cursor.close();
   database.close();
 }
于 2019-11-08T08:46:31.050 回答
1
private void method() {
        Cursor cursor = query();
        if (flag == false) {  // WRONG: return before close()
            return;
        }
        cursor.close();
   }

好的做法应该是这样的:

    private void method() {
        Cursor cursor = null;
        try {
            cursor = query();
        } finally {
            if (cursor != null)
                cursor.close();  // RIGHT: ensure resource is always recovered
        }
    }
于 2018-01-19T06:28:27.320 回答