泄露信息如下:
====================================
HEAP ANALYSIS RESULT
====================================
1 APPLICATION LEAKS
References underlined with "~~~" are likely causes.
Learn more at https://squ.re/leaks.
Displaying only 1 leak trace out of 4 with the same signature
Signature: 645952c17f5aa992b39740a17b9afc2f3ccf5619
┬───
│ GC Root: System class
│
├─ android.os.AsyncTask class
│ Leaking: NO (a class is never leaking)
│ ↓ static AsyncTask.SERIAL_EXECUTOR
│ ~~~~~~~~~~~~~~~
├─ android.os.AsyncTask$SerialExecutor instance
│ Leaking: UNKNOWN
│ ↓ AsyncTask$SerialExecutor.mTasks
│ ~~~~~~
├─ java.util.ArrayDeque instance
│ Leaking: UNKNOWN
│ ↓ ArrayDeque.elements
│ ~~~~~~~~
├─ java.lang.Object[] array
│ Leaking: UNKNOWN
│ ↓ Object[].[0]
│ ~~~
├─ android.os.AsyncTask$SerialExecutor$1 instance
│ Leaking: UNKNOWN
│ Anonymous class implementing java.lang.Runnable
│ ↓ AsyncTask$SerialExecutor$1.val$r
│ ~~~~~
├─ android.widget.TextView$3 instance
│ Leaking: UNKNOWN
│ Anonymous class implementing java.lang.Runnable
│ ↓ TextView$3.this$0
│ ~~~~~~
├─ androidx.appcompat.widget.AppCompatEditText instance
│ Leaking: YES (View.mContext references a destroyed activity)
│ View not part of a window view hierarchy
│ View.mAttachInfo is null (view detached)
│ View.mID = R.id.input_edit
│ View.mWindowAttachCount = 1
│ mContext instance of RoomMainActivity with mDestroyed = true
│ ↓ View.mContext
╰→ RoomMainActivity instance
Leaking: YES (ObjectWatcher was watching this because RoomMainActivity
received Activity#onDestroy() callback and Activity#mDestroyed is true)
key = cb388a5c-0ff2-452f-a9b0-6ea7d03a5105
watchDurationMillis = 37631
retainedDurationMillis = 32630
mApplication instance of ChatApplication
mBase instance of androidx.appcompat.view.ContextThemeWrapper
====================================
原因分析:
1、引用链结构:AsyncTask的SerialExecutor执行器引用了EditText对象,而EditText对象中的mContext引用到了RoomMainActivity中context,导致RoomMainActivity 无法销毁。
查看源码得知在TextView中有updateTextServicesLocaleAsync()方法,调用了AsyncTask.execute()向其中传入了匿名内部类Runnable,而持有了控件对象。
2、解决方法:因为在源码层面无法修改源码,在引用端切断引用链。
给EditText使用Application的上下文,在EditText使用的页面退出销毁时移除EditText控件,包括置空它的监听器、清除它的焦点。
import android.content.Context;
import android.os.Build;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.view.ViewGroup;
import androidx.appcompat.widget.AppCompatEditText;
/**
* 关于Android EditText导致的内存泄漏的问题
*/
public class NoMemoryLeakEditTextextends AppCompatEditText {
public NoMemoryLeakEditText(Context context) {
super(context.getApplicationContext());
}
public NoMemoryLeakEditText(Context context, AttributeSet attrs) {
super(context.getApplicationContext(), attrs);
}
public NoMemoryLeakEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context.getApplicationContext(), attrs, defStyleAttr);
}
public void clearMemoryLeak(TextWatcher watcher, ViewGroup container) {
clearFocus();
setOnTouchListener(null);
setOnClickListener(null);
setOnDragListener(null);
setOnKeyListener(null);
setOnLongClickListener(null);
setOnEditorActionListener(null);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
setOnApplyWindowInsetsListener(null);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
setOnScrollChangeListener(null);
}
setOnFocusChangeListener(null);
removeTextChangedListener(watcher);
container.removeView(this);
}
}
最后在页面销毁的地方调用clearMemoryLeak方法
网友评论