2013-04-26 59 views
1

我是相对较新的android开发,并从ac#背景它是完全可能的,我的整个策略是错误的,但我不断收到警告由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服务(虽然在现实这个范围NDS的可重用性目的的通用抽象类)


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

所以,我的问题是,当我用一行代码,这样从一个活动:

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

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

我会更好地调用完整的代码来确保使用dispose方法关闭数据库吗?

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

回答

1

垃圾收集将能够收集类,还有Helper类,因为这些都是对象链(不是一个技术术语 - 只是我编造的)不再一部分。然而,你仍然需要明确关闭数据库(如果你不这样做肯定会成为你的内存泄漏罪魁祸首)。

@Override 
public void finalize() { 
    dispose(); 
} 

我通常喜欢,但做的事情有点不同:既然这样,你可以在你的对象的finalize()方法,它是垃圾收集过程中调用做到这一点。像这样的数据存储最好写成单身人士,因为他们可能被多个类访问,并且如果创建不同的实例,他们仍然会打开一个新的访问点来阅读一篇文章,并可能导致很多问题。你有一种单身模式的设置,你的代码中有一个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; 
} 

然后,而不是使用getHelper()跟踪辅助对象的MyClassService,你可以使用得到帮助:

MySQLiteOpenHelper.sharedHelper(context); 

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

public void finalize() 
{ 
    close(); 
} 
+0

谢谢,你们都回答了我的问题,并建议改进。使用'context.getApplicationContext()'而不是'context'初始化MySQLiteOpenHelper是否有优势(使用'this'从一个活动传递过来)? – GarethD 2013-04-27 09:10:31

+0

@GarethD,很高兴我能帮忙!在Android文档中,它指出传递“Activity”上下文可能会导致内存泄漏 - 如果一个活动已关闭,则会发生这种情况,但该helper保留了一个指向其上下文的指针。另一方面,应用程序的上下文与应用程序绑定,如果被终止,也会垃圾收集你的助手类。也就是说,我从来没有遇到任何传递常规上下文的问题 - 但使用Application的上下文是最安全的(主要区别是你不能使用应用上下文来修改UI)。 – Phil 2013-04-27 18:37:07

1

你应该明确地调用dispose - 不这样做不会造成内存泄漏(该对象可以立即new MyClassService(this).getAll()后,因为没有对象的任何活动引用收集),但可能会导致你的数据库用尽可用的连接。

0

对于从getReadableDatabase()返回的数据库对象,您忘记了调用close