2013-04-26 80 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