WindowManagerGlobal.sDefaultWindowManager内存泄漏

WindowManagerGlobal.sDefaultWindowManager memory leak

我正在使用导航组件,并且在退出应用程序时发生内存泄漏..

这是 LeakCanary logcat

    ┬───
    │ GC Root: System class
    │
    ├─ android.view.WindowManagerGlobal class
    │    Leaking: NO (a class is never leaking)
    │    ↓ static WindowManagerGlobal.sDefaultWindowManager
    │                                 ~~~~~~~~~~~~~~~~~~~~~
    ├─ android.view.WindowManagerGlobal instance
    │    Leaking: UNKNOWN
    │    ↓ WindowManagerGlobal.mViews
    │                          ~~~~~~
    ├─ java.util.ArrayList instance
    │    Leaking: UNKNOWN
    │    ↓ ArrayList.elementData
    │                ~~~~~~~~~~~
    ├─ java.lang.Object[] array
    │    Leaking: UNKNOWN
    │    ↓ Object[].[0]
    │               ~~~
    ├─ android.widget.LinearLayout instance
    │    Leaking: YES (View.mContext references a destroyed activity)
    │    mContext instance of .....ui.MainActivity with mDestroyed = true
    │    View#mParent is set
    │    View#mAttachInfo is not null (view attached)
    │    View.mWindowAttachCount = 1
    │    ↓ LinearLayout.mContext
    ╰→ .....ui.MainActivity instance
    ​     Leaking: YES (ObjectWatcher was watching this because .....ui.MainActivity received Activity#onDestroy() callback and Activity#mDestroyed is true)
    ​     key = 4a23bf94-97b5-4f44-87d2-6f9a59fc053f
    ​     watchDurationMillis = 5135
    ​     retainedDurationMillis = 134

我的activity代码:


public class MainActivity extends AppCompatActivity {

    private NavController mNavController;
    private AppBarConfiguration mAppBarConfiguration;

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

        // Reset the shared prefs values only for the first launch of the app.
        PreferenceManager.setDefaultValues(this, R.xml.settings, false);

    }


    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        // return for API-19+
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
            return;

        // Customize Activity to not overlap with the bottom software buttons navigation bar (back, home, & menu)
        boolean hasMenuKey = ViewConfiguration.get(this).hasPermanentMenuKey();
        boolean hasBackKey = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK);

        if (!hasMenuKey && !hasBackKey) { // check if this device has a bottom navigation bar
            try {
                ConstraintLayout rootLayout = findViewById(R.id.activity_root);
                int NAVIGATION_BAR_HEIGHT = 70;
                rootLayout.getLayoutParams().height = rootLayout.getHeight() - NAVIGATION_BAR_HEIGHT;

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    private void setupStatusBar() {
        // Making status bar transparent and overlapping with the activity
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
            getWindow().setFlags(FLAG_LAYOUT_NO_LIMITS, FLAG_LAYOUT_NO_LIMITS);
    }

    public void setupActionBar() {
        mNavController = Navigation.findNavController(this, R.id.nav_host_fragment);
        mAppBarConfiguration = new AppBarConfiguration.Builder(
                R.id.mainFragment, R.id.readFragment) // remove up button from all these fragments
                .build();
        NavigationUI.setupActionBarWithNavController(this, mNavController, mAppBarConfiguration);

    }



    @Override
    public boolean onSupportNavigateUp() {
        return NavigationUI.navigateUp(mNavController, mAppBarConfiguration) // navigateUp tries to pop the back stack
                || super.onSupportNavigateUp();
    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
        getWindow().clearFlags( WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
        mAppBarConfiguration = null;
        mNavController = null;
    }
}

我试图清除我在 onDestroy() 中设置的 window 标志,甚至试图将 mAppBarConfigurationmNavController 设置为空,但没有成功。

同样在主片段中,我在 onDestroyView()

中将 mContext 设置为 null

注意:我在 xml 布局中没有任何 LinearLayout

我设置activity在导航组件的某些片段中动态支持ActionBar;设置操作栏后,我调用 setupActionBar() 以使用导航组件调整新设置的操作栏:

片段中

Toolbar toolbar = requireView().findViewById(R.id.toolbar);
((MainActivity) requireActivity()).setSupportActionBar(toolbar);
((MainActivity) requireActivity()).setupActionBar();

我通过在 activity 的 onDestroy() 中将操作栏设置回 null 解决了 WindowManagerGlobal.sDefaultWindowManager 内存泄漏问题。

@Override
protected void onDestroy() {
    super.onDestroy();
    setSupportActionBar(null);
}