添加一个片段并调用 .replace() 导致添加的片段内存泄漏
Adding a fragment and calling .replace() causing a memory leak in the added fragment
我有一个带有 3 个选项卡的 BottomNavigationView。在每次单击选项卡时,它都会调用 .replace()
private void initBottomNavigation() {
mBottomNavigation.setOnNavigationItemSelectedListener(menuItem -> {
switch (menuItem.getItemId()) {
case R.id.bottomnav_admin_home: <---- Tab 1
getSupportFragmentManager().beginTransaction()
.replace(
R.id.admin_fragment_container,
new AdminHomeFragment(),
Constants.FRAGMENT_ADMIN_HOME
)
.commit();
}
...Tab two
....Tab three
return true;
```
在我的第 3 个选项卡中,我有一个在片段容器顶部添加片段的按钮:
当我点击按钮时,它会调用它在片段容器的顶部添加另一个片段:
getParentFragmentManager().beginTransaction()
.add(
R.id.admin_fragment_container,
new DeactivateUserFragment(),
Constants.FRAGMENT_ADMIN_DEACTIVATE_USER
)
.addToBackStack(null)
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.commit();
当我在 added 片段上时,我单击调用 .replace()
的第二个选项卡,这导致我的 added 内存泄漏 片段。
我已经减少了添加的片段中的所有代码,但我不明白为什么它仍然泄漏。
添加片段的完整代码:
public class DeactivateUserFragment extends Fragment{
private static final String TAG = "FragmentDeactivatedUser";
@BindView(R.id.recycler_view) RecyclerView mRecycler;
@BindView(R.id.parent_layout) ViewGroup mParentLayout;
@BindView(R.id.progress_bar) ProgressBar mProgressBar;
private Context mContext;
private SimpleListUsersAdapter mAdapter;
private ArrayList<UserInfo> mList = new ArrayList<>();
private DeactivateViewModel mViewModel;
private AuthStateManager mAuthStateManager;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = getContext();
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_admin_deactivate_user, container, false);
ButterKnife.bind(this, view);
return view;
}
@OnClick(R.id.btn_back)
public void onBackBtnClick(){
getParentFragmentManager().popBackStack();
}
}
泄漏:
androidx.coordinatorlayout.widget.CoordinatorLayout instance
Leaking: YES (ObjectWatcher was watching this because com.example.
testproject.ui.admin.deactivate.DeactivateUserFragment received
Fragment#onDestroyView() callback (references to its views should be
cleared to prevent leaks))
Retaining 106.7 kB in 1469 objects
key = e50280e9-58bd-4ecd-bc1b-a2d6cc188639
watchDurationMillis = 108539
retainedDurationMillis = 103532
View not part of a window view hierarchy
View.mAttachInfo is null (view detached)
View.mID = R.id.parent_layout
View.mWindowAttachCount = 1
mContext instance of com.example.testproject.ui.admin.AdminActivity
with mDestroyed = false
编辑1:________________________________
为什么删除这三行不再导致内存泄漏?
@BindView(R.id.recycler_view) RecyclerView mRecycler;
@BindView(R.id.parent_layout) ViewGroup mParentLayout;
@BindView(R.id.progress_bar) ProgressBar mProgressBar;
我没有在任何代码中使用变量,只是绑定了它们。
我非常困惑这是怎么导致泄漏的
编辑 2:__________________________________
我已经尝试在 onDestroyView()
中将视图设置为 null 并解除 Butterknife 的绑定,但仍然会泄漏。
https://github.com/JakeWharton/butterknife/issues/585
@Override
public void onDestroyView() {
Log.d(TAG, "onDestroyView: Called");
unbinder.unbind();
mRecycler = null;
mParentLayout = null;
mProgressBar = null;
super.onDestroyView();
}
我已经尝试一个一个地删除视图...进度条、回收器和 mParentLayout。直到我把3个都去掉,它才不会再漏了
我从片段中删除了 Butterknife 并且只使用 findViewById 但仍然有同样的问题
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_admin_deactivate_user, container, false);
unbinder=ButterKnife.bind(this, view);//initialise the unbinder here
return view;
}
Override
public void onDestroyView() {
Log.d(TAG, "onDestroyView: Called");
if(unbinder!=null){
unbinder.unbind();
unbinder = null;
}
super.onDestroyView();
}
尝试将此添加到您的回收器适配器代码中:
@Override
public void onDestroyView() {
recyclerView.setAdapter(null);
super.onDestroyView();
}
请尝试删除在 onDestroy
和 onDestroyView
中分别在 onCreate
或 onCreateView
中初始化的所有引用。
所以如果你有:
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = getContext();
}
你还应该有:
@Override
public void onDestroy() {
mContext = null;
super.onDestroy();
}
我建议您不要将上下文存储在属性中,因为您可以在 android Fragment
子类的任何地方访问方法 getContext()
。
上下文是一个习惯性的泄漏源。
我不知道你的其他属性到底是什么类型,但你应该保持相同的理念。
解决方法是在我的 Fragment 中为 butterknife 的 Binder
设置一个全局变量,并在 onDestroyView()
中调用 Unbind
private Unbinder unbinder;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_admin_link_family, container, false);
return view;
}
我也绑定了适配器
private SimpleListUsersAdapter adapter;
Inside Adapter class
class ViewHolder extends RecyclerView.ViewHolder {
@BindView(R.id.action_type)
TextView userAction;
@BindView(R.id.date)
TextView date;
@BindView(R.id.amount)
TextView amount;
ViewHolder(@NonNull View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}
所以我还需要在 onDestroyView
中将适配器设置为 null
Fragment Class
@Override
public void onDestroyView() {
super.onDestroyView();
adapter = null;
unbinder.unbind();
}
需要两者都做才能阻止泄漏
我有一个带有 3 个选项卡的 BottomNavigationView。在每次单击选项卡时,它都会调用 .replace()
private void initBottomNavigation() {
mBottomNavigation.setOnNavigationItemSelectedListener(menuItem -> {
switch (menuItem.getItemId()) {
case R.id.bottomnav_admin_home: <---- Tab 1
getSupportFragmentManager().beginTransaction()
.replace(
R.id.admin_fragment_container,
new AdminHomeFragment(),
Constants.FRAGMENT_ADMIN_HOME
)
.commit();
}
...Tab two
....Tab three
return true;
```
在我的第 3 个选项卡中,我有一个在片段容器顶部添加片段的按钮:
当我点击按钮时,它会调用它在片段容器的顶部添加另一个片段:
getParentFragmentManager().beginTransaction()
.add(
R.id.admin_fragment_container,
new DeactivateUserFragment(),
Constants.FRAGMENT_ADMIN_DEACTIVATE_USER
)
.addToBackStack(null)
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.commit();
当我在 added 片段上时,我单击调用 .replace()
的第二个选项卡,这导致我的 added 内存泄漏 片段。
我已经减少了添加的片段中的所有代码,但我不明白为什么它仍然泄漏。
添加片段的完整代码:
public class DeactivateUserFragment extends Fragment{
private static final String TAG = "FragmentDeactivatedUser";
@BindView(R.id.recycler_view) RecyclerView mRecycler;
@BindView(R.id.parent_layout) ViewGroup mParentLayout;
@BindView(R.id.progress_bar) ProgressBar mProgressBar;
private Context mContext;
private SimpleListUsersAdapter mAdapter;
private ArrayList<UserInfo> mList = new ArrayList<>();
private DeactivateViewModel mViewModel;
private AuthStateManager mAuthStateManager;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = getContext();
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_admin_deactivate_user, container, false);
ButterKnife.bind(this, view);
return view;
}
@OnClick(R.id.btn_back)
public void onBackBtnClick(){
getParentFragmentManager().popBackStack();
}
}
泄漏:
androidx.coordinatorlayout.widget.CoordinatorLayout instance
Leaking: YES (ObjectWatcher was watching this because com.example.
testproject.ui.admin.deactivate.DeactivateUserFragment received
Fragment#onDestroyView() callback (references to its views should be
cleared to prevent leaks))
Retaining 106.7 kB in 1469 objects
key = e50280e9-58bd-4ecd-bc1b-a2d6cc188639
watchDurationMillis = 108539
retainedDurationMillis = 103532
View not part of a window view hierarchy
View.mAttachInfo is null (view detached)
View.mID = R.id.parent_layout
View.mWindowAttachCount = 1
mContext instance of com.example.testproject.ui.admin.AdminActivity
with mDestroyed = false
编辑1:________________________________
为什么删除这三行不再导致内存泄漏?
@BindView(R.id.recycler_view) RecyclerView mRecycler;
@BindView(R.id.parent_layout) ViewGroup mParentLayout;
@BindView(R.id.progress_bar) ProgressBar mProgressBar;
我没有在任何代码中使用变量,只是绑定了它们。 我非常困惑这是怎么导致泄漏的
编辑 2:__________________________________
我已经尝试在 onDestroyView()
中将视图设置为 null 并解除 Butterknife 的绑定,但仍然会泄漏。
https://github.com/JakeWharton/butterknife/issues/585
@Override
public void onDestroyView() {
Log.d(TAG, "onDestroyView: Called");
unbinder.unbind();
mRecycler = null;
mParentLayout = null;
mProgressBar = null;
super.onDestroyView();
}
我已经尝试一个一个地删除视图...进度条、回收器和 mParentLayout。直到我把3个都去掉,它才不会再漏了
我从片段中删除了 Butterknife 并且只使用 findViewById 但仍然有同样的问题
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_admin_deactivate_user, container, false);
unbinder=ButterKnife.bind(this, view);//initialise the unbinder here
return view;
}
Override
public void onDestroyView() {
Log.d(TAG, "onDestroyView: Called");
if(unbinder!=null){
unbinder.unbind();
unbinder = null;
}
super.onDestroyView();
}
尝试将此添加到您的回收器适配器代码中:
@Override
public void onDestroyView() {
recyclerView.setAdapter(null);
super.onDestroyView();
}
请尝试删除在 onDestroy
和 onDestroyView
中分别在 onCreate
或 onCreateView
中初始化的所有引用。
所以如果你有:
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = getContext();
}
你还应该有:
@Override
public void onDestroy() {
mContext = null;
super.onDestroy();
}
我建议您不要将上下文存储在属性中,因为您可以在 android Fragment
子类的任何地方访问方法 getContext()
。
上下文是一个习惯性的泄漏源。
我不知道你的其他属性到底是什么类型,但你应该保持相同的理念。
解决方法是在我的 Fragment 中为 butterknife 的 Binder
设置一个全局变量,并在 onDestroyView()
Unbind
private Unbinder unbinder;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_admin_link_family, container, false);
return view;
}
我也绑定了适配器
private SimpleListUsersAdapter adapter;
Inside Adapter class
class ViewHolder extends RecyclerView.ViewHolder {
@BindView(R.id.action_type)
TextView userAction;
@BindView(R.id.date)
TextView date;
@BindView(R.id.amount)
TextView amount;
ViewHolder(@NonNull View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}
所以我还需要在 onDestroyView
Fragment Class
@Override
public void onDestroyView() {
super.onDestroyView();
adapter = null;
unbinder.unbind();
}
需要两者都做才能阻止泄漏