J'ai une MainActivity qui a un customView (DatePicker) dans sa mise en page. DatePicker customView a un bouton et un CustomDialogFragment. Lorsque le bouton est cliqué sur le DatePicker, il affiche le CustomDialogFragment. L'application fonctionne bien mais leakCanary montre une fuite. Voici le code (Certains codes ont été supprimés par souci de concision)
MainActivity.class
References underlined with "~~~" are likely causes. Learn more at https://squ.re/leaks. 1437 bytes retained by leaking objects Signature: 1eb8b5c7c3fd403a9a6851729c4044c8a6ce7cf6 â¬âââ â GC Root: System class â ââ android.view.inputmethod.InputMethodManager class â Leaking: NO (InputMethodManagerâ is not leaking and a class is never leaking) â â static InputMethodManager.sInstance ââ android.view.inputmethod.InputMethodManager instance â Leaking: NO (DecorViewâ is not leaking and InputMethodManager is a singleton) â â InputMethodManager.mNextServedView ââ com.android.internal.policy.DecorView instance â Leaking: NO (LinearLayoutâ is not leaking and View attached) â mContext instance of com.android.internal.policy.DecorContext, wrapping activity com.example.testproject.MainActivity with mDestroyed = false â Parent android.view.ViewRootImpl not a android.view.View â View#mParent is set â View#mAttachInfo is not null (view attached) â View.mWindowAttachCount = 1 â â DecorView.mContentRoot ââ android.widget.LinearLayout instance â Leaking: NO (MainActivityâ is not leaking and View attached) â mContext instance of com.example.testproject.MainActivity with mDestroyed = false â View.parent com.android.internal.policy.DecorView attached as well â View#mParent is set â View#mAttachInfo is not null (view attached) â View.mWindowAttachCount = 1 â â LinearLayout.mContext ââ com.example.testproject.MainActivity instance â Leaking: NO (DatePickerâ is not leaking and Activity#mDestroyed is false) â â MainActivity._$_findViewCache ââ java.util.HashMap instance â Leaking: NO (DatePickerâ is not leaking) â â HashMap.table ââ java.util.HashMap$Node[] array â Leaking: NO (DatePickerâ is not leaking) â â HashMap$Node[].[0] ââ java.util.HashMap$Node instance â Leaking: NO (DatePickerâ is not leaking) â â HashMap$Node.value ââ com.example.testproject.customViews.DatePicker instance â Leaking: NO (View attached) â mContext instance of com.example.testproject.MainActivity with mDestroyed = false â View.parent androidx.constraintlayout.widget.ConstraintLayout attached as well â View#mParent is set â View#mAttachInfo is not null (view attached) â View.mID = R.id.date_picker â View.mWindowAttachCount = 1 â â DatePicker.calendarDialog â ~~~~~~~~~~~~~~ â°â com.example.testproject.customViews.CalendarDialog instance â Leaking: YES (ObjectWatcher was watching this because com.example.testproject.customViews.CalendarDialog received Fragment#onDestroy() callback and Fragment#mFragmentManager is null) â key = e176896c-49c6-4b17-a21e-4a6ca7cde260 â watchDurationMillis = 11213 â retainedDurationMillis = 6208 â key = f3a2f22a-c77f-4c8e-a281-d803d110acff â watchDurationMillis = 11214 ==================================== 0 LIBRARY LEAKS Library Leaks are leaks coming from the Android Framework or Google libraries. ==================================== METADATA Please include this in bug reports and Stack Overflow questions. Build.VERSION.SDK_INT: 28 Build.MANUFACTURER: Google LeakCanary version: 2.2 App process name: com.example.testproject Analysis duration: 4191 ms Heap dump file path: /data/user/0/com.example.testproject/files/leakcanary/2020-03-11_10-15-46_729.hprof Heap dump timestamp: 1583936152876 ====================================
activity_main.xml
class CalendarDialog: DialogFragment() { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { val builder = AlertDialog.Builder(context!!) builder.setView(view) .setMessage("This is a dummy message") .setPositiveButton("OK") { dialog, which -> } .setNegativeButton("Cancel") { dialog, which -> } return builder.create() } }
DatePicker.class
class DatePicker : FrameLayout { var calendarDialog: CalendarDialog? = null init { View.inflate(context, R.layout.date_picker, this) open_calendar.setOnClickListener { calendarDialog?.show((context as MainActivity).supportFragmentManager.beginTransaction(), "Calendar") } } }
CalendarDialog.class
<androidx.constraintlayout.widget.ConstraintLayout> <com.example.testproject.customViews.DatePicker android:id="@+id/date_picker" android:layout_width="wrap_content" android:layout_height="wrap_content"> </com.example.testproject.customViews.DatePicker> </androidx.constraintlayout.widget.ConstraintLayout>
RÉSULTAT DE L'ANALYSE HEAP ===================================== 1 FUITES D'APPLICATIONS
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) date_picker.calendarDialog = getCalendarDialog() } private fun getCalendarDialog(): CalendarDialog { return CalendarDialog() } }
Voici les choses que j'ai essayées jusqu'à présent sans succès.
4 Réponses :
Lorsque le fragment CalendarDialog est ignoré, ce fragment est censé être récupéré. Cependant, ici, nous pouvons voir que la mise en page DatePicker en garde une référence, l'empêchant d'être récupérée. DatePicker est toujours attaché, il est donc logique qu'il existe toujours, mais il doit définir son champ DatePicker.calendarDialog sur null lorsque la boîte de dialogue est fermée.
J'ai pensé la même chose.J'ai ajouté un callBack à CalendarDialog et défini l'instance de CalendarDialog dans DatePicker sur null lorsque rejeté est appelé. Cela n'a pas aidé.
Vous pouvez placer CalendarDialog dans un WeakReference, de cette façon, vous n'avez pas à le définir explicitement sur null via un rappel. Le garbage collector le nettoiera automatiquement et vous pourrez éviter la fuite de mémoire.
Pour plus d'informations sur WeakReference: https://developer.android.com / reference / java / lang / ref / WeakReference
Faites de CalenderDialog la fonction utilitaire
class DatePicker(context: Context, attr: AttributeSet) : FrameLayout(context, attr) { var calendarDialog: CalendarDialog? = null init { View.inflate(context, R.layout.date_picker, this) open_calendar.setOnClickListener { calendarDialog?.onCreateDialog(context)?.show() } } override fun onDetachedFromWindow() { super.onDetachedFromWindow() calendarDialog = null open_calendar.setOnClickListener(null) } }
et
class CalendarDialog { fun onCreateDialog(context: Context): Dialog { val builder = AlertDialog.Builder(context) builder .setMessage("This is a dummy message") .setPositiveButton("OK") { dialog, which -> } .setNegativeButton("Cancel") { dialog, which -> } return builder.create() } }
L'activité n'a aucune idée que le fragment de dialogue existe dans votre cas.
J'ai vécu une expérience similaire auparavant. Tout ce que j'ai fait a été de changer supportFragmentManager
avec childFragmentManager
. Je suggère donc de changer ceci:
calendarDialog?.show((context as MainActivity).childFragmentManager.beginTransaction(), "Calendar")
en ceci:
calendarDialog?.show((context as MainActivity).supportFragmentManager.beginTransaction(), "Calendar")
Recherchez également tout autre supportFragmentManager
(s'il y en a).
Il n'y a pas de code incorrect dans les classes ci-dessus. Pourriez-vous fournir le code complet des classes?
Je n'ai plus de code. C'est tout.