【问题标题】:SQLite Connection leaked although everything closed尽管一切都关闭了,但 SQLite 连接泄漏了
【发布时间】:2013-08-11 10:11:53
【问题描述】:

我发现了很多类似close the connectionclose 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 连接泄漏警告。

我做错了什么?

【问题讨论】:

  • 我认为您只是关闭了 DBHelper,而不是数据库本身
  • 我认为你也应该调用 db.close()
  • 不管我是否这样做。反正我会收到消息的。但是我在某处读到,当您调用 dbHelper.close() 时,您不需要这样做
  • 为什么DatabaseHelper 是静态的?这可能会导致泄漏,因为DatabaseHelper 仍然保持与数据库的连接,而左侧没有对DatabasHelper 的引用。
  • 我删除了静态,但不幸的是它仍然泄漏

标签: android android-sqlite sqliteopenhelper


【解决方案1】:

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

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 对象尚未初始化,将创建一个。如果有 已经创建了,那么它将被简单地返回。

你应该 不要使用 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);
  }
}

【讨论】:

  • 似乎解决了这个问题。我会做进一步调查
  • 很好的答案。我在使用多个 IntentServices 时遇到了这个问题,所有这些 IntentServices 都同时在两个不同的数据库中工作。我使用了这个答案,除了两个单独的工厂方法。清除所有内存泄漏错误。它确实增加了半秒或两秒的执行时间,可能是因为我不再同时打开多个数据库实例。
  • 这为我解决了同样的问题。
  • 很好的答案,这修复了数据库的泄漏
  • 我在 Android Studio 中收到此警告:“不要将 Android 上下文类放在静态字段中;这是内存泄漏(也会破坏 Instant Run)”。
【解决方案2】:

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

助手类:

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

【讨论】:

  • 请您帮忙解决这个问题issue
【解决方案3】:
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
        }
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-26
    • 2021-07-30
    • 1970-01-01
    • 2018-09-09
    • 1970-01-01
    • 2019-05-30
    相关资源
    最近更新 更多