首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >正在尝试检测ActionMode内存泄漏

正在尝试检测ActionMode内存泄漏
EN

Stack Overflow用户
提问于 2018-11-12 20:05:39
回答 3查看 615关注 0票数 10

几天来,我一直在努力寻找ActionMode内存泄漏的来源,但一直没有成功。我有一个包含多个片段的活动,当我将片段保留为ActionMode (同时自动取消它)时,LeakCanary会检测到内存泄漏。

我已经在destroy()上清空了ActionMode和ActionMode.Callback,甚至还尝试过在onDestroyActionMode()上这样做。

这是我的LeakCanary截图:

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

我希望有人给我指明了正确的方向。

另外,我怀疑这和ActionMode.Callback有关。但是,我找不到任何用于销毁它的CallBack的方法。我使用startSupportActionMode(mActionModeCallback)启动ActionMode。我也尝试过找到一种方法来删除mActionModeCallback,但是没有找到方法。

下面是我的完整ActionMode代码:

代码语言:javascript
复制
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;
}

包含片段的父活动:

代码语言:javascript
复制
@Override
public void onTabUnselected(TabLayout.Tab tab)
{
    clearCAB();
}

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

回答 3

Stack Overflow用户

发布于 2019-09-15 23:16:35

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

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

代码语言:javascript
复制
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:

代码语言:javascript
复制
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;
    }
}
票数 4
EN

Stack Overflow用户

发布于 2018-11-25 18:59:02

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

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

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

代码语言:javascript
复制
fun ListView.removeViewAndClearListeners() {
    setMultiChoiceModeListener(null)
    setOnScrollListener(null)
    onItemClickListener = null

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

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

票数 1
EN

Stack Overflow用户

发布于 2018-11-27 12:30:01

我仍然想知道你为什么要依赖ActionMode.Callback。我有一个应用程序,我应该在长按时创建一个自定义菜单,但我在这个问题上浪费了几乎两个月的时间:

ActionModeCallback does not work

我不确定你是否意识到这一点,ActionMode回调几乎不能在所有设备上工作。经过大量的研究,我了解到,过于关注电池消耗和优化的设备不会让你的后台服务和一些回调按预期工作。

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

票数 -1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/53261839

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档