WindowManagerGlobal.sDefaultWindowManager内存泄漏

问题描述

我正在使用导航组件,并且在退出应用程序时遇到内存泄漏。

这是LeakCanary日志猫

    ┬───
    │ 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

我的活动代码


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()中设置的窗口标志,甚至试图将mAppBarConfigurationmNavController设置为null,但没有成功。

还在主片段中,我在mContext中将onDestroyView()设置为null

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

解决方法

我在导航组件的某些片段中动态设置了活动支持ActionBar。设置好动作栏后,我调用setupActionBar()以便通过导航组件调整新设置的动作栏:

片段

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

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

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