几天来,我一直在努力寻找ActionMode内存泄漏的来源,但一直没有成功。我有一个包含多个片段的活动,当我将片段保留为ActionMode (同时自动取消它)时,LeakCanary会检测到内存泄漏。
我已经在destroy()上清空了ActionMode和ActionMode.Callback,甚至还尝试过在onDestroyActionMode()上这样做。
这是我的LeakCanary截图:
https://i.imgur.com/RUbdqj3.png
我希望有人给我指明了正确的方向。
另外,我怀疑这和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();
}发布于 2019-09-15 23:16:35
根据我的经验,如果您的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;
}
}发布于 2018-11-25 18:59:02
活动中的ActionMode似乎引用了片段的布局,这导致了内存泄漏,并阻止了片段的GC。我找不到删除引用的方法。
在我的用例中,我在激活活动的ActionMode的片段中使用了ListView (通过listener.setMultiChoiceModeListener)。
我的解决方案是:在片段的onDestroyView中,从布局中删除listView (或任何激活了ActionMode的视图),并删除列表视图的所有侦听器。我为它做了一个kotlin扩展方法:
fun ListView.removeViewAndClearListeners() {
setMultiChoiceModeListener(null)
setOnScrollListener(null)
onItemClickListener = null
(parent as? ViewGroup)?.removeView(this)
}这样做之后,泄漏就消失了。
发布于 2018-11-27 12:30:01
我仍然想知道你为什么要依赖ActionMode.Callback。我有一个应用程序,我应该在长按时创建一个自定义菜单,但我在这个问题上浪费了几乎两个月的时间:
ActionModeCallback does not work
我不确定你是否意识到这一点,ActionMode回调几乎不能在所有设备上工作。经过大量的研究,我了解到,过于关注电池消耗和优化的设备不会让你的后台服务和一些回调按预期工作。
尝试在MI或Oppo/Vivo设备上测试代码。它将直接跳转到onDestroyActionMode,而不是调用onActionItemClicked
https://stackoverflow.com/questions/53261839
复制相似问题