android內存泄露:2、非靜態的內部類錯誤使用_情形二

目錄

一、前言

二、非靜態的內部類錯誤使用-情形二

情形二:在Activity中,使用單例工廠類引用 Activity內部類

1、新建一個 Module,寫主界面 MainActivity,佈局 activity_main

2、寫業務邏輯

3、效果展示

4、解決方案


一、前言

上篇文章我們介紹了:LeakCanary內存泄漏檢測庫、內存泄露_內存溢出_內存抖動、非靜態的內部類錯誤使用以及解決方式,詳細可參考博文:原創 android內存泄露:1、LeakCanary內存泄漏檢測庫、內存泄露_內存溢出_內存抖動、非靜態的內部類錯誤使用,這篇文章我們將介紹:非靜態的內部類錯誤使用-情形二

二、非靜態的內部類錯誤使用-情形二

情形二:在Activity中,使用單例工廠類引用 Activity內部類

1、新建一個 Module,寫主界面 MainActivity,佈局 activity_main

MainActivity

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

    }

    public void innerClass(View view) {
        startActivity(new Intent(MainActivity.this,InnerClassActivity.class));
    }


}

activity_main

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    tools:ignore="MissingConstraints">


    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="innerClass"
        android:text="非靜態內部類的錯誤使用-User" />

</androidx.constraintlayout.widget.ConstraintLayout>

 

2、寫業務邏輯

工具類 UserUtils 

public class UserUtils {
    private static InnerClassActivity.User sUser;

    public static void setsUser(InnerClassActivity.User user) {
        sUser = user;
    }

}

InnerClassActivity

public class InnerClassActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_inner_class);

        //創建的非靜態內部類User對象,對外部類(Activity)有隱式的強引用,
        //同時該內部類對象又永久被UserUtils靜態變量給引用上了,
        //從而導致GC想回收該activity的堆內存,發現sUser還在引用,GC無法回收
        UserUtils.setsUser(new User("張三"));

    }

    class User{
        private String username;

        public User(String username){
            this.username = username;
        }
    }


}

 

3、效果展示

使用LeakCanary測試的結果如下:

 

4、解決方案

/**
 * 如果Activity中有非靜態的內部類,我們創建了這個類的實例對象,
 * 但是我們把這個實例對象賦值給了一個單例工廠類,導致該內部類對象的生命週期永遠都被引用,
 * 同時該對象又引用了Activity,那麼就導致了Activity的內存泄露。
 */

解決方式:只需要將內部類User類改爲靜態內部類即可

靜態類對象一旦被jvm(虛擬機)加載,一般是不會被移除掉的,

除非這個虛擬機是我們自定義的虛擬機,也就是我們自己去定義一個類加載器,它繼承於ClassLoader,

當我們自定義的類加載器被卸載掉後,所加載的靜態類對象纔會被移除掉。

默認情況下,我們都使用系統自帶的類加載器,它加載的靜態對象,是不會被GC回收掉的。

所以讓User成爲靜態類,靜態類屬於字節碼級別的,不再是Activity裏面的內部類

 

當然用靜態會有影響

因爲這個對象是靜態的,那麼這個類被加載了,就不會被GC回收釋放掉。

但最起碼它不會影響 Activity的生命週期,不會影響GC去回收我們這個Activity,

所以應該去慎用,內部類我們也應該去慎用,

這個就需要我們去平衡他們相互之間的關係了。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章