具有临时类实例的垃圾收集

时间:2013-04-26 16:52:33

标签: java android sqlite memory-leaks

我对Android开发相对较新,并且来自ac #background完全有可能我的整个策略都是错误的,但是当我没有正确关闭数据库连接导致内存泄漏时,我不断收到Eclipse的警告

我有一个扩展SQLiteOpenHelper的基础数据库类:

public class MySQLiteOpenHelper extends SQLiteOpenHelper {

    public MySQLiteOpenHelper(Context context, String name,
            CursorFactory factory, int version) {
        super(context, name, factory, version);
    }

    public MySQLiteOpenHelper(Context context) {
        this(context, "myDb", null, 1);
    }
    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE MyTable (A INT)");
    }
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }

    public Cursor executeSelect(String sql, String[] parameters) {
        return getReadableDatabase().rawQuery(sql, parameters);
    }

}

通用实体:


public class MyClass {
    private int a;

    public void setA(int value) {
        this.a = value;
    }

    public int getA() {
        return this.a;
    }
}

本质上是MyClass的服务(虽然实际上这扩展了一个通用的抽象类以实现可重用性)


public class MyClassService {

    private MySQLiteOpenHelper helper;
    private Context context;

    public MyClassService(Context context) {
        this.context = context;
    }

    private MySQLiteOpenHelper getHelper() {
        if (helper == null) {
            helper = new MySQLiteOpenHelper(this.context);
        }
        return helper;
    }

    public void dispose() {
        if (helper != null) {
            helper.close();
            helper = null;
        }
    }
    public ArrayList<MyClass> getAll()
    {
        ArrayList<MyClass> list = new ArrayList<MyClass>();
        Cursor cursor = getHelper().executeSelect("SELECT A FROM MyTable", new String[0]);

        while (cursor.moveToNext()) {
            MyClass item = new MyClass()
            item.setA(cursor.getInt(0));
            list.add(item);
        }
        cursor.close();
        return list;
    }
}   

所以,我的问题是当我在Activity中使用这样的一行代码时:

ArrayList<MyClass> list = new MyClassService(this).getAll();

MyClassService的实例立即处理,或者这可能是我内存泄漏的来源。

我是否可以更好地调用完整代码以确保使用dispose方法关闭数据库?

MyClassService svc = new MyClassService(this);
ArrayList<MyClass> list = svc.getAll();
svc.dispose();

3 个答案:

答案 0 :(得分:1)

您应该显式调用dispose - 不这样做不会导致内存泄漏(可以在new MyClassService(this).getAll()之后立即收集对象,因为没有对该对象的实时引用),但是可能导致您的数据库用完可用的连接。

答案 1 :(得分:1)

垃圾收集器将能够收集您的类以及Helper类,因为它们不再是对象链的一部分(不是技术术语 - 只是我编造的东西)。但是,你仍然需要显式关闭数据库(如果你不这样做肯定是你的内存泄漏的罪魁祸首)。就目前而言,您可以在对象的finalize()方法中执行此操作,该方法在垃圾回收期间调用:

@Override
public void finalize() {
    dispose();
}
然而,我通常更喜欢做一些不同的事情。像这样的数据存储通常最好写成 Singletons ,因为它们可能被多个类访问,如果创建了不同的实例,它们仍然会打开一个新的访问点来阅读写作,并可能导致许多的问题。您的代码中有一个helper变量,但您可能希望简单地将Helper类设为单例。您可以通过删除构造函数并添加以下内容来完成此操作:

private static MySQLiteOpenHelper self;

private MySQLiteOpenHelper(Context context, String name, CursorFactory factory, int version) {
    super(context, name, factory, version);
}

private MySQLiteOpenHelper(Context context) {
    this(context, "myDb", null, 1);
}

public static MySQLiteOpenHelper sharedHelper(Context context) {
    if (self == null)
        self = new MySQLiteOpenHelper(context);
    return self;
}

然后,不是使用MyClassService跟踪getHelper()中的帮助对象,而是使用以下方式获取帮助程序:

MySQLiteOpenHelper.sharedHelper(context);

这样做的好处意味着您只需要在整个应用程序中跟踪一个Helper,并且在帮助程序的finalize()方法中,您现在可以关闭数据库。这将在应用程序进程被终止时调用,并防止任何内存泄漏:

public void finalize()
{
    close();
}

答案 2 :(得分:0)

您忘记在close返回的数据库对象上调用getReadableDatabase()