基本情况
之前在项目上做内存泄漏优化的时候有一个关于RecyclerView内存泄漏,页面结构如图:
LeakCanary捕获的引用链如下
┬───
│ GC Root: Thread object
│
├─ java.lang.Thread instance
│ Thread name: 'main'
│ ↓ Thread.threadLocals
│ ~~~~~~~~~~~~
├─ java.lang.ThreadLocal$ThreadLocalMap instance
│ ↓ ThreadLocal$ThreadLocalMap.table
│ ~~~~~
├─ java.lang.ThreadLocal$ThreadLocalMap$Entry[] array
│ ↓ ThreadLocal$ThreadLocalMap$Entry[4]
│ ~~~
├─ java.lang.ThreadLocal$ThreadLocalMap$Entry instance
│ ↓ ThreadLocal$ThreadLocalMap$Entry.value
│ ~~~~~
├─ androidx.recyclerview.widget.GapWorker instance
│ ↓ GapWorker.mRecyclerViews
│ ~~~~~~~~~~~~~~
├─ java.util.ArrayList instance
│ ↓ ArrayList[0]
│ ~~~
╰→ androidx.recyclerview.widget.RecyclerView instance
找出问题
从引用链可以看出关键点在于GapWorker,首先看看这个GapWorker
RecyclerView在android 21及以上版本会使用GapWorker
实现预加载机制,在Recyclerview的onAttachedToWindow
方法中尝试将其实例化,并通过GapWorker的add
方法将Recyclerview自身添加到GapWoker
的成员变量mRecyclerViews
链表中去,在onDetachedFromWindow
会调用GapWorker
的remove
方法移除其对自身的引用,GapWoker
实例保存在其类静态成员变量sGapWorker
(ThreadLocal)中,确保主线程只有一个实例
RecyclerView
@Override
protected void onAttachedToWindow() {
......
if (ALLOW_THREAD_GAP_WORK) {
//从ThreadLocal中获取GapWorker实例,为null则直接创建一个
mGapWorker = GapWorker.sGapWorker.get();
if (mGapWorker == null) {
mGapWorker = new GapWorker();
Display display = ViewCompat.getDisplay(this);
float refreshRate = 60.0f;
if (!isInEditMode() && display != null) {
float displayRefreshRate = display.getRefreshRate();
if (displayRefreshRate >= 30.0f) {
refreshRate = displayRefreshRate;
}
}
mGapWorker.mFrameIntervalNs = (long) (1000000000 / refreshRate);
//将创建的GapWorker实例设置到ThreadLocal中去
GapWorker.sGapWorker.set(mGapWorker);
}
//添加自身的引用
mGapWorker.add(