每次尝试显示DialogFragment时,我都会遇到内存泄漏。
这是我的测试对话框(取自android开发人员页面)的样子:
public class TestDialog extends DialogFragment {
public static TestDialog newInstance(int title) {
TestDialog frag = new TestDialog();
Bundle args = new Bundle();
args.putInt("title", title);
frag.setArguments(args);
return frag;
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
int title = getArguments().getInt("title");
return new AlertDialog.Builder(getActivity())
.setIcon(R.drawable.ic_action_about)
.setTitle(title)
.setPositiveButton(R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
//((FragmentAlertDialog)getActivity()).doPositiveClick();
}
}
)
.setNegativeButton(R.string.cancel,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
//((FragmentAlertDialog)getActivity()).doNegativeClick();
}
}
)
.create();
}
}
我使用以下代码启动它,该代码在按下按钮时执行:
DialogFragment newFragment = TestDialog.newInstance(R.string.company_title);
newFragment.show(getFragmentManager(), "dialog");
如何解决此泄漏(或至少隐藏它,因为canaryleak对所有这些通知都变得非常烦人)?
答案 0 :(得分:2)
造成此泄漏的原因是DialogFragment
的源代码:
@Override
public void onActivityCreated(Bundle savedInstanceState) {
...
// other codes
...
mDialog.setCancelable(mCancelable);
// hear is the main reason
mDialog.setOnCancelListener(this);
mDialog.setOnDismissListener(this);
...
// other codes
...
}
让我们看看函数Dialog.SetOnCancelListener(DialogInterface.OnCancelListener)
中发生了什么:
/**
* Set a listener to be invoked when the dialog is canceled.
*
* <p>This will only be invoked when the dialog is canceled.
* Cancel events alone will not capture all ways that
* the dialog might be dismissed. If the creator needs
* to know when a dialog is dismissed in general, use
* {@link #setOnDismissListener}.</p>
*
* @param listener The {@link DialogInterface.OnCancelListener} to use.
*/
public void setOnCancelListener(@Nullable OnCancelListener listener) {
if (mCancelAndDismissTaken != null) {
throw new IllegalStateException(
"OnCancelListener is already taken by "
+ mCancelAndDismissTaken + " and can not be replaced.");
}
if (listener != null) {
// here
mCancelMessage = mListenersHandler.obtainMessage(CANCEL, listener);
} else {
mCancelMessage = null;
}
}
而且,这是Handler.obtainMessage(int, Object)
的源代码:
/**
*
* Same as {@link #obtainMessage()}, except that it also sets the what and obj members
* of the returned Message.
*
* @param what Value to assign to the returned Message.what field.
* @param obj Value to assign to the returned Message.obj field.
* @return A Message from the global message pool.
*/
public final Message obtainMessage(int what, Object obj)
{
return Message.obtain(this, what, obj);
}
最后,将调用函数Message.obtain(Handler, int, Object)
:
/**
* Same as {@link #obtain()}, but sets the values of the <em>target</em>, <em>what</em>, and <em>obj</em>
* members.
* @param h The <em>target</em> value to set.
* @param what The <em>what</em> value to set.
* @param obj The <em>object</em> method to set.
* @return A Message object from the global pool.
*/
public static Message obtain(Handler h, int what, Object obj) {
Message m = obtain();
m.target = h;
m.what = what;
m.obj = obj;
return m;
}
我们可以看到cancelMessage
拥有DialogFragment
的实例,这导致内存泄漏。我只想让您知道这一点,除了没有使用DialogFragment
之外,我无法避免。或有更好解决方案的人,请告诉我。
答案 1 :(得分:1)
以防万一有人仍然碰到这个问题:我通过将泄漏漏斗更新到最新版本(目前为2.4)来解决此问题。似乎是一个假阳性检测。我使用的是泄漏漏斗2.0beta-3。
答案 2 :(得分:0)
基于@EmMper的答案。如果不需要onCancelListener,可以采用以下解决方法。
import android.app.Activity
import android.os.Bundle
import androidx.annotation.MainThread
import androidx.fragment.app.DialogFragment
open class PatchedDialogFragment : DialogFragment() {
@MainThread
override fun onActivityCreated(savedInstanceState: Bundle?) {
// Fixing the issue described here
// https://stackoverflow.com/questions/53185154/leakcanary-dialogfragment-leak-detection
val initialShowsDialog = showsDialog
showsDialog = false
super.onActivityCreated(savedInstanceState)
showsDialog = initialShowsDialog
if (!showsDialog) {
return
}
val view = view
if (view != null) {
check(view.parent == null) { "DialogFragment can not be attached to a container view" }
dialog!!.setContentView(view)
}
val activity: Activity? = activity
if (activity != null) {
dialog!!.ownerActivity = activity
}
dialog!!.setCancelable(isCancelable)
if (savedInstanceState != null) {
val dialogState =
savedInstanceState.getBundle("android:savedDialogState")
if (dialogState != null) {
dialog!!.onRestoreInstanceState(dialogState)
}
}
}
}
只需扩展PatchedDialogFragment而不是DialogFragment。
答案 3 :(得分:0)
通过从我的自定义OnDismissListener
实现中同时删除OnCancelListener
和DialogFragment
来消除此泄漏。
我还必须将null传递给否定按钮侦听器:.setNegativeButton(R.string.cancel, null)
。