尝试检测ActionMode内存泄漏

时间:2018-11-12 12:05:39

标签: java android

几天来,我一直在试图寻找ActionMode内存泄漏的根源,但是没有运气。我有一个包含多个片段的活动,当我将片段保留为ActionMode时(自动取消),LeakCanary会检测到内存泄漏。

我已经在destroy()上使ActionMode和ActionMode.Callback都为空,甚至尝试在onDestroyActionMode()上这样做。

这是我的LeakCanary屏幕截图:

https://i.imgur.com/RUbdqj3.png

我希望有人指出我正确的方向。

P.S。我怀疑它与ActionMode.Callback有关。不过,我找不到用于销毁它的CallBack的任何方法。我使用startSupportActionMode(mActionModeCallback)启动ActionMode。我也试图找到一种方法来从中删除mActionModeCallback,但是没有方法。

这是我完整的ActionMode代码:

private ActionMode mActionMode;
private ActionMode.Callback mActionModeCallback;

public void startCAB()
{
    if (mActionMode == null)
        mActionMode = ((AppCompatActivity) getActivity()).startSupportActionMode(mActionModeCallback);
}


private void buildActionModeCallBack()
{
    mActionModeCallback = new ActionMode.Callback() {
        @Override
        public boolean onCreateActionMode(ActionMode mode, Menu menu) {
            MenuInflater inflater = mode.getMenuInflater();
            inflater.inflate(R.menu.menu_cab, menu);
            return true;
        }

        @Override
        public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
            return false;
        }

        @Override
        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
            switch (item.getItemId()) {
                ... Some Code ...
            }
        }

        @Override
        public void onDestroyActionMode(ActionMode mode) {
            mActionMode = null;
    mActionModeCallback = null; // Tried with and without this.
        }
    };
}

public void finishActionMode()
{
    mActionMode.finish();
}

@Override
public void onDestroy()
{
    super.onDestroy();
    mActionMode = null;
    mActionModeCallback = null;
}

包含片段的父母活动:

@Override
public void onTabUnselected(TabLayout.Tab tab)
{
    clearCAB();
}

private void clearCAB()
{
    int index = mPagerAdapter.getCurrentFragmentIndex();
    FragmentOne fragmentOne = (FragmentOne) mPagerAdapter.instantiateItem(mViewPager, index);
    fragmentOne.finishActionMode();
}

3 个答案:

答案 0 :(得分:1)

似乎活动中的ActionMode引用了片段的布局,这会导致内存泄漏并阻止片段进行GC处理。我找不到删除引用的方法。

在我的用例中,我在正在激活活动的ActionMode(通过listener.setMultiChoiceModeListener)的片段内使用ListView。

我的hacky解决方案:在片段的onDestroyView中,从布局中删除listView(或激活ActionMode的任何视图),并删除列表视图的所有侦听器。我为此做了一个kotlin扩展方法:

fun ListView.removeViewAndClearListeners() {
    setMultiChoiceModeListener(null)
    setOnScrollListener(null)
    onItemClickListener = null

    (parent as? ViewGroup)?.removeView(this)
}

这样做之后,泄漏消失了。

答案 1 :(得分:0)

根据我的经验,如果您的ActionMode.Callback对象使用匿名内部类,则可能会导致片段内存泄漏。

也许您可以创建一个新类并实现ActionMode.Callback,然后使用它来放入startSupportActionMode()参数:

public class YourFragment extends skip implements skip, ActionMode.Callback {

    private ActionMode mActionMode;

    public void startCAB()
    {
        if (mActionMode == null)
            mActionMode = ((AppCompatActivity) getActivity()).startSupportActionMode(new SafeActionModeCallback(this));
    }

    public void finishActionMode()
    {
        mActionMode.finish();
    }

    @Override
    public boolean onCreateActionMode(ActionMode mode, Menu menu) {
        MenuInflater inflater = mode.getMenuInflater();
        inflater.inflate(R.menu.menu_cab, menu);
        return true;
    }

    @Override
    public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
        return false;
    }

    @Override
    public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
        switch (item.getItemId()) {
            // ... Some Code ...
        }
    }

    @Override
    public void onDestroyActionMode(ActionMode mode) {
        mActionMode = null;
    }
}

SafeActionModeCallback:

public class SafeActionModeCallback implements ActionMode.Callback {

    // you can also use the WeakReference
    private ActionMode.Callback callback;

    public SafeActionModeCallback(ActionMode.Callback callback) {
        this.callback = callback;
    }

    @Override
    public boolean onCreateActionMode(ActionMode mode, Menu menu) {
        return callback.onCreateActionMode(mode, menu);
    }

    @Override
    public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
        return callback.onPrepareActionMode(mode, menu);
    }

    @Override
    public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
        return callback.onActionItemClicked(mode, item);
    }

    @Override
    public void onDestroyActionMode(ActionMode mode) {
        callback.onDestroyActionMode(mode);
        callback = null;
    }
}

答案 2 :(得分:-1)

我仍然想知道为什么您要依赖ActionMode.Callback。我有一个应用程序,应该长按它可以创建“自定义菜单”,而我在此问题上浪费了将近2个月:

ActionModeCallback does not work

我不确定是否知道这一点,所以ActionMode回调几乎不能在所有设备上运行。经过大量研究,我发现那些过于关注电池消耗和优化的设备将无法让您的后台服务和某些回调正常工作。

尝试在MI或Oppo / Vivo设备上测试代码。它将直接跳转到 onDestroyActionMode ,而不是调用 onActionItemClicked