v7/recyclerview/src/androidTest/java/androidx/recyclerview/widget/RecyclerViewLayoutTest.java
public void onDataSetChanged_handleCachedViews(boolean hasStableIds) throws Throwable {
        final AtomicInteger cachedRecycleCount = new AtomicInteger(0);

        final RecyclerView recyclerView = new RecyclerView(getActivity());
        recyclerView.setItemViewCacheSize(1);

        final TestAdapter adapter = new TestAdapter(2) {
            @Override
            public void onViewRecycled(@NonNull TestViewHolder holder) {
                // If the recycled holder is currently in the cache, then it's position in the
                // adapter should be RecyclerView.NO_POSITION.
                if (mRecyclerView.mRecycler.mCachedViews.contains(holder)) {
                    assertThat("ViewHolder's getAdapterPosition should be "
                                    + "RecyclerView.NO_POSITION",
                            holder.getAdapterPosition(),
                            is(RecyclerView.NO_POSITION));
                    cachedRecycleCount.incrementAndGet();
                }
                super.onViewRecycled(holder);
            }
        };
        adapter.setHasStableIds(hasStableIds);
        recyclerView.setAdapter(adapter);

        final AtomicInteger numItemsToLayout = new AtomicInteger(2);

        TestLayoutManager layoutManager = new TestLayoutManager() {
            @Override
            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
                try {
                    detachAndScrapAttachedViews(recycler);
                    layoutRange(recycler, 0, numItemsToLayout.get());
                } catch (Throwable t) {
                    postExceptionToInstrumentation(t);
                } finally {
                    this.layoutLatch.countDown();
                }
            }

            @Override
            public boolean supportsPredictiveItemAnimations() {
                return false;
            }
        };
        recyclerView.setLayoutManager(layoutManager);

        // Layout 2 items and sanity check that no items are in the recycler's cache.
        numItemsToLayout.set(2);
        layoutManager.expectLayouts(1);
        setRecyclerView(recyclerView, true, false);
        layoutManager.waitForLayout(2);
        checkForMainThreadException();
        assertThat("Sanity check, no views should be cached at this time",
                mRecyclerView.mRecycler.mCachedViews.size(),
                is(0));

        // Now only layout 1 item and assert that 1 item is cached.
        numItemsToLayout.set(1);
        layoutManager.expectLayouts(1);
        requestLayoutOnUIThread(mRecyclerView);
        layoutManager.waitForLayout(1);
        checkForMainThreadException();
        assertThat("One view should be cached.",
                mRecyclerView.mRecycler.mCachedViews.size(),
                is(1));

        // Notify data set has changed then final assert.
        layoutManager.expectLayouts(1);
        mActivityRule.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                adapter.notifyDataSetChanged();
            }
        });
        layoutManager.waitForLayout(1);
        checkForMainThreadException();
        // If hasStableIds, then no cached views should be recycled, otherwise just 1 should have
        // been recycled.
        assertThat(cachedRecycleCount.get(), is(hasStableIds ? 0 : 1));
    }
/**
     * If Adapter#setHasStableIds(boolean) is false, cached ViewHolders should be recycled in
     * response to RecyclerView.Adapter#notifyDataSetChanged() and should report a position of
     * RecyclerView#NO_POSITION inside of
     * RecyclerView.Adapter#onViewRecycled(RecyclerView.ViewHolder).
     *
     * If Adapter#setHasStableIds(boolean) is true, cached Views/ViewHolders should not be recycled.
     */


If Adapter#setHasStableIds(boolean) is true, cached Views/ViewHolders should not be recycled.
If Adapter#setHasStableIds ( boolean ) is true cached Views / ViewHolders should not be recycled .
public void onDataSetChanged_handleCachedViews(boolean hasStableIds) throws Throwable {
        final AtomicInteger cachedRecycleCount = new AtomicInteger(0);
        final RecyclerView recyclerView = new RecyclerView(getActivity());
        recyclerView.setItemViewCacheSize(1);
        final TestAdapter adapter = new TestAdapter(2) {
            @Override
            public void onViewRecycled(@NonNull TestViewHolder holder) {
                if (mRecyclerView.mRecycler.mCachedViews.contains(holder)) {
                    assertThat("ViewHolder's getAdapterPosition should be "
                                    + "RecyclerView.NO_POSITION",
                            holder.getAdapterPosition(),
                            is(RecyclerView.NO_POSITION));
                    cachedRecycleCount.incrementAndGet();
                }
                super.onViewRecycled(holder);
            }
        };
        adapter.setHasStableIds(hasStableIds);
        recyclerView.setAdapter(adapter);
        final AtomicInteger numItemsToLayout = new AtomicInteger(2);
        TestLayoutManager layoutManager = new TestLayoutManager() {
            @Override
            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
                try {
                    detachAndScrapAttachedViews(recycler);
                    layoutRange(recycler, 0, numItemsToLayout.get());
                } catch (Throwable t) {
                    postExceptionToInstrumentation(t);
                } finally {
                    this.layoutLatch.countDown();
                }
            }
            @Override
            public boolean supportsPredictiveItemAnimations() {
                return false;
            }
        };
        recyclerView.setLayoutManager(layoutManager);
        numItemsToLayout.set(2);
        layoutManager.expectLayouts(1);
        setRecyclerView(recyclerView, true, false);
        layoutManager.waitForLayout(2);
        checkForMainThreadException();
        assertThat("Sanity check, no views should be cached at this time",
                mRecyclerView.mRecycler.mCachedViews.size(),
                is(0));
        numItemsToLayout.set(1);
        layoutManager.expectLayouts(1);
        requestLayoutOnUIThread(mRecyclerView);
        layoutManager.waitForLayout(1);
        checkForMainThreadException();
        assertThat("One view should be cached.",
                mRecyclerView.mRecycler.mCachedViews.size(),
                is(1));
        layoutManager.expectLayouts(1);
        mActivityRule.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                adapter.notifyDataSetChanged();
            }
        });
        layoutManager.waitForLayout(1);
        checkForMainThreadException();
        assertThat(cachedRecycleCount.get(), is(hasStableIds ? 0 : 1));
    }
public void onDataSetChanged_handleCachedViews ( boolean hasStableIds ) throws Throwable { final AtomicInteger cachedRecycleCount = new AtomicInteger ( 0 ) ; final RecyclerView recyclerView = new RecyclerView ( getActivity ( ) ) ; recyclerView . setItemViewCacheSize ( 1 ) ; final TestAdapter adapter = new TestAdapter ( 2 ) { @ Override public void onViewRecycled ( @ NonNull TestViewHolder holder ) { if ( mRecyclerView . mRecycler . mCachedViews . contains ( holder ) ) { assertThat ( "ViewHolder's getAdapterPosition should be " + "RecyclerView.NO_POSITION" , holder . getAdapterPosition ( ) , is ( RecyclerView . NO_POSITION ) ) ; cachedRecycleCount . incrementAndGet ( ) ; } super . onViewRecycled ( holder ) ; } } ; adapter . setHasStableIds ( hasStableIds ) ; recyclerView . setAdapter ( adapter ) ; final AtomicInteger numItemsToLayout = new AtomicInteger ( 2 ) ; TestLayoutManager layoutManager = new TestLayoutManager ( ) { @ Override public void onLayoutChildren ( RecyclerView . Recycler recycler , RecyclerView . State state ) { try { detachAndScrapAttachedViews ( recycler ) ; layoutRange ( recycler , 0 , numItemsToLayout . get ( ) ) ; } catch ( Throwable t ) { postExceptionToInstrumentation ( t ) ; } finally { this . layoutLatch . countDown ( ) ; } } @ Override public boolean supportsPredictiveItemAnimations ( ) { return false ; } } ; recyclerView . setLayoutManager ( layoutManager ) ; numItemsToLayout . set ( 2 ) ; layoutManager . expectLayouts ( 1 ) ; setRecyclerView ( recyclerView , true , false ) ; layoutManager . waitForLayout ( 2 ) ; checkForMainThreadException ( ) ; assertThat ( "Sanity check, no views should be cached at this time" , mRecyclerView . mRecycler . mCachedViews . size ( ) , is ( 0 ) ) ; numItemsToLayout . set ( 1 ) ; layoutManager . expectLayouts ( 1 ) ; requestLayoutOnUIThread ( mRecyclerView ) ; layoutManager . waitForLayout ( 1 ) ; checkForMainThreadException ( ) ; assertThat ( "One view should be cached." , mRecyclerView . mRecycler . mCachedViews . size ( ) , is ( 1 ) ) ; layoutManager . expectLayouts ( 1 ) ; mActivityRule . runOnUiThread ( new Runnable ( ) { @ Override public void run ( ) { adapter . notifyDataSetChanged ( ) ; } } ) ; layoutManager . waitForLayout ( 1 ) ; checkForMainThreadException ( ) ; assertThat ( cachedRecycleCount . get ( ) , is ( hasStableIds ? 0 : 1 ) ) ; }
If Adapter#setHasStableIds(boolean) is false, cached ViewHolders should be recycled in
 response to RecyclerView.Adapter#notifyDataSetChanged() and should report a position of
 RecyclerView#NO_POSITION inside of
 RecyclerView.Adapter#onViewRecycled(RecyclerView.ViewHolder).
If Adapter#setHasStableIds ( boolean ) is false cached ViewHolders should be recycled in response to RecyclerView . Adapter#notifyDataSetChanged () and should report a position of RecyclerView#NO_POSITION inside of RecyclerView . Adapter#onViewRecycled ( RecyclerView . ViewHolder ) .
// If the recycled holder is currently in the cache, then it's position in the // adapter should be RecyclerView.NO_POSITION. // Layout 2 items and sanity check that no items are in the recycler's cache. // Now only layout 1 item and assert that 1 item is cached. // Notify data set has changed then final assert. // If hasStableIds, then no cached views should be recycled, otherwise just 1 should have // been recycled.