什麼是記憶體洩漏?
一個已經廢棄的物件因為一些原因無法被回收,導致它一直占用記憶體造成浪費,即稱為記憶體洩漏。當一個物件持有一個生命週期比它短的物件的引用,就有可能引起記憶體洩漏。例如單例模式引起的記憶體洩漏。因為單例擁有靜態特性,其生命週期跟整個app一樣長,在實例化一個需要context的單例對象時,若傳入activity的context,會使該單例一直持有該activity的引用,導致activity即便退出了也無法被回收,產生記憶體洩漏。
class MainActivity : AppCompatActivity() {
companion object Hi{
lateinit var a:A
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
if(a==null) a = A()
}
inner class A{
}
有時候我們會在Activity內部建立了一個非靜態內部類別的單例,每次啟動Activity時都會使用該單例的資料,雖然避免了資源的重複建立,卻會造成記憶體洩漏,因為非靜態內部類別A會持有外部類別MainActivity的引用,而我們又建立了一個A靜態實例a,a的生命週期和app的一樣長,所以a會一直持有MainActivity的引用,導致MainActivity不能被回收。
正確的做法為:將該內部類設為靜態內部類或將該內部類抽取出來封裝成一個單例。
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
inner class h:Handler(){
}
}
實際上你在建立內部類別handler時,IDE就提醒你會出事了。
class AppManager(val context: Context) {
companion object {
private var instance: AppManager? = null
fun getInstance(context: Context): AppManager? {
if (instance != null) {
instance = AppManager(context)
}
return instance
}
}
}
若呼叫getInstance方法的時候,帶進來的參數是activity的context就會出事
一般Context造成的記憶體洩漏,幾乎都是當Context銷燬的時候,卻因為被引用導致銷燬失敗,而Application的Context物件可以理解為隨著程序存在的,所以我們總結出使用Context的正確姿勢:
1:當Application的Context能搞定的情況下,並且生命週期長的物件,優先使用Application的Context。
2:不要讓生命週期長於Activity的物件持有到Activity的引用。
3:儘量不要在Activity中使用非靜態內部類,因為非靜態內部類會隱式持有外部類例項的引用,如果使用靜態內部類,將外部例項引用作為弱引用持有。