RecyclerView#Adapter使用中的兩個陷阱

其實就是Adapter中可以被覆寫的兩個方法

1、onDetachedFromRecyclerView

看下方法說明

        /**
         * Called by RecyclerView when it stops observing this Adapter.
         *
         * @param recyclerView The RecyclerView instance which stopped observing this adapter.
         * @see #onAttachedToRecyclerView(RecyclerView)
         */
        public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) {
        }

在RecyclerView不再觀察這個Adapter時被調(diào)用。
與之對應的是onAttachedToRecyclerView

        /**
         * Called by RecyclerView when it starts observing this Adapter.
         * <p>
         * Keep in mind that same adapter may be observed by multiple RecyclerViews.
         *
         * @param recyclerView The RecyclerView instance which started observing this adapter.
         * @see #onDetachedFromRecyclerView(RecyclerView)
         */
        public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
        }

在RecyclerView開始觀察這個Adapter時,被調(diào)用捺萌。

通常我們的理解是這樣的:
頁面進入時,顯示RecyclerView护桦,調(diào)用onAttachedToRecyclerView廊宪,做一些注冊工作;
頁面退出時懈玻,銷毀RecyclerView巧婶,調(diào)用onDetachedFromRecyclerView,做一些解注冊和其他資源回收的操作。

而實際上艺栈,這兩個方法的調(diào)用時機是:

    public void setAdapter(Adapter adapter) {
        // bail out if layout is frozen
        ...
        setAdapterInternal(adapter, false, true);
        ...
    }

    private void setAdapterInternal(Adapter adapter, boolean compatibleWithPrevious,
            boolean removeAndRecycleViews) {
        if (mAdapter != null) {
            mAdapter.unregisterAdapterDataObserver(mObserver);
            mAdapter.onDetachedFromRecyclerView(this);//這里onDetachedFromRecyclerView
        }
        if (!compatibleWithPrevious || removeAndRecycleViews) {
            removeAndRecycleViews();
        }
        mAdapterHelper.reset();
        final Adapter oldAdapter = mAdapter;
        mAdapter = adapter;
        if (adapter != null) {
            adapter.registerAdapterDataObserver(mObserver);
            adapter.onAttachedToRecyclerView(this);//這里onAttachedToRecyclerView
        }
        if (mLayout != null) {
            mLayout.onAdapterChanged(oldAdapter, mAdapter);
        }
        mRecycler.onAdapterChanged(oldAdapter, mAdapter, compatibleWithPrevious);
        mState.mStructureChanged = true;
    }

可以看到英岭,在調(diào)用setAdapter方法時,新設置的Adapter會調(diào)用onAttachedToRecyclerView湿右,原有的Adapter會調(diào)用onDetachedFromRecyclerView巴席。
所以如果覆寫了onDetachedFromRecyclerView,為了確保被調(diào)用诅需,需要在頁面退出時漾唉,手動調(diào)用setAdapter(null)

2堰塌、onViewDetachedFromWindow

方法說明:

        /**
         * Called when a view created by this adapter has been detached from its window.
         *
         * <p>Becoming detached from the window is not necessarily a permanent condition;
         * the consumer of an Adapter's views may choose to cache views offscreen while they
         * are not visible, attaching and detaching them as appropriate.</p>
         *
         * @param holder Holder of the view being detached
         */
        public void onViewDetachedFromWindow(@NonNull VH holder) {
        }

簡言之赵刑,就是當itemView被從window上detach時調(diào)用〕⌒蹋看起來很美好般此,與之對應的方法是:

        /**
         * Called when a view created by this adapter has been attached to a window.
         *
         * <p>This can be used as a reasonable signal that the view is about to be seen
         * by the user. If the adapter previously freed any resources in
         * {@link #onViewDetachedFromWindow(RecyclerView.ViewHolder) onViewDetachedFromWindow}
         * those resources should be restored here.</p>
         *
         * @param holder Holder of the view being attached
         */
        public void onViewAttachedToWindow(@NonNull VH holder) {
        }

然后我們也如第一個方法般調(diào)用了:
onViewAttachedToWindow中做一些注冊工作;
onViewDetachedFromWindow中做一些解注冊和釋放資源的工作牵现。

在RecyclerView正常滾動時铐懊,這兩個方法都會被調(diào)用。然而頁面退出時瞎疼,onViewDetachedFromWindow并不會被調(diào)用科乎!

追根溯源,會發(fā)現(xiàn)癥結在LinearLayoutManager中:

    @Override
    public void onDetachedFromWindow(RecyclerView view, RecyclerView.Recycler recycler) {
        super.onDetachedFromWindow(view, recycler);
        if (mRecycleChildrenOnDetach) {
            removeAndRecycleAllViews(recycler);
            recycler.clear();
        }
    }

默認mRecycleChildrenOnDetach=false贼急。我們需要調(diào)用setRecycleChildrenOnDetach(true)才能實現(xiàn)在頁面退出時茅茂,依然調(diào)用onViewDetachedFromWindow方法。

整合RecyclerView

可以設計一個RecyclerView的基類太抓,在基類中做如下處理:

public class BaseRecyclerView extends RecyclerView implements LifecycleObserver {
    public BaseRecyclerView(@NonNull Context context) {
        super(context);
        init(context);
    }

    public BaseRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public BaseRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(context);
    }

    private void init(Context context) {
        if (context instanceof LifecycleOwner) {
            ((LifecycleOwner) context).getLifecycle().addObserver(this);
        }
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    public void onDestory() {
        /*
         確保Adapter#onDetachedFromRecyclerView被調(diào)用
         */
        setAdapter(null);
    }

    @Override
    public void setLayoutManager(LayoutManager layoutManager) {
        super.setLayoutManager(layoutManager);
        if (layoutManager instanceof LinearLayoutManager) {
            /*
            確保Adapter#onViewDetachedFromWindow被調(diào)用
             */
            ((LinearLayoutManager) layoutManager).setRecycleChildrenOnDetach(true);
        }
    }
}

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末空闲,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子走敌,更是在濱河造成了極大的恐慌碴倾,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件掉丽,死亡現(xiàn)場離奇詭異跌榔,居然都是意外死亡,警方通過查閱死者的電腦和手機机打,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門矫户,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人残邀,你說我怎么就攤上這事皆辽「躺撸” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵驱闷,是天一觀的道長耻台。 經(jīng)常有香客問我,道長空另,這世上最難降的妖魔是什么盆耽? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮扼菠,結果婚禮上摄杂,老公的妹妹穿的比我還像新娘。我一直安慰自己循榆,他們只是感情好析恢,可當我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著秧饮,像睡著了一般映挂。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上盗尸,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天柑船,我揣著相機與錄音,去河邊找鬼泼各。 笑死鞍时,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的历恐。 我是一名探鬼主播寸癌,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼弱贼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起磷蛹,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤吮旅,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后味咳,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體庇勃,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年槽驶,在試婚紗的時候發(fā)現(xiàn)自己被綠了责嚷。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡掂铐,死狀恐怖罕拂,靈堂內(nèi)的尸體忽然破棺而出揍异,到底是詐尸還是另有隱情,我是刑警寧澤爆班,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布衷掷,位于F島的核電站,受9級特大地震影響柿菩,放射性物質(zhì)發(fā)生泄漏戚嗅。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一枢舶、第九天 我趴在偏房一處隱蔽的房頂上張望懦胞。 院中可真熱鬧,春花似錦凉泄、人聲如沸医瘫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽醇份。三九已至,卻和暖如春吼具,著一層夾襖步出監(jiān)牢的瞬間僚纷,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工拗盒, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留怖竭,地道東北人。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓陡蝇,卻偏偏與公主長得像痊臭,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子登夫,可洞房花燭夜當晚...
    茶點故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容