GapWorker.java revision a93a0c08b4543401d8e2df9bd4c88f26d3b72c11
107b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik/*
207b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik * Copyright (C) 2016 The Android Open Source Project
307b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik *
407b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik * Licensed under the Apache License, Version 2.0 (the "License");
507b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik * you may not use this file except in compliance with the License.
607b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik * You may obtain a copy of the License at
707b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik *
807b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik *      http://www.apache.org/licenses/LICENSE-2.0
907b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik *
1007b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik * Unless required by applicable law or agreed to in writing, software
1107b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik * distributed under the License is distributed on an "AS IS" BASIS,
1207b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1307b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik * See the License for the specific language governing permissions and
1407b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik * limitations under the License.
1507b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik */
1607b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craikpackage android.support.v7.widget;
1707b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik
1807b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craikimport android.support.v4.os.TraceCompat;
1907b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik
2007b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craikimport java.util.ArrayList;
2107b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craikimport java.util.Arrays;
2207b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craikimport java.util.concurrent.TimeUnit;
2307b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik
2407b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craikclass GapWorker implements Runnable {
2507b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik    static final ThreadLocal<GapWorker> sGapWorker = new ThreadLocal<>();
2607b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik
2707b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik    ArrayList<RecyclerView> mRecyclerViews = new ArrayList<>();
2807b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik    long mPostTimeNs;
2907b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik    long mFrameIntervalNs;
3007b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik
3107b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik    public void add(RecyclerView recyclerView) {
3207b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik        if (RecyclerView.DEBUG && mRecyclerViews.contains(recyclerView)) {
3307b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik            throw new IllegalStateException("RecyclerView already present in worker list!");
3407b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik        }
3507b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik        mRecyclerViews.add(recyclerView);
3607b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik    }
3707b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik
3807b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik    public void remove(RecyclerView recyclerView) {
3907b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik        boolean removeSuccess = mRecyclerViews.remove(recyclerView);
4007b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik        if (RecyclerView.DEBUG && !removeSuccess) {
4107b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik            throw new IllegalStateException("RecyclerView removal failed!");
4207b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik        }
4307b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik    }
4407b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik
4507b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik    /**
4607b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik     * Schedule a prefetch immediately after the current traversal.
4707b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik     */
4807b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik    void postFromTraversal(RecyclerView recyclerView, int prefetchDx, int prefetchDy) {
4907b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik        if (recyclerView.isAttachedToWindow()) {
5007b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik            if (RecyclerView.DEBUG && !mRecyclerViews.contains(recyclerView)) {
5107b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik                throw new IllegalStateException("attempting to post unregistered view!");
5207b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik            }
5307b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik            if (mPostTimeNs == 0) {
54a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik                mPostTimeNs = recyclerView.getNanoTime();
5507b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik                recyclerView.post(this);
5607b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik            }
5707b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik        }
5807b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik
5907b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik        recyclerView.mPrefetchDx = prefetchDx;
6007b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik        recyclerView.mPrefetchDy = prefetchDy;
61a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik    }
62a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik
63a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik    static boolean lastPrefetchIncludedPosition(RecyclerView recyclerView, int position) {
64a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik        if (recyclerView.mPrefetchArray != null) {
65a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik            for (int i = 0; i < recyclerView.mPrefetchArray.length; i++) {
66a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik                if (recyclerView.mPrefetchArray[i] == position) return true;
67a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik            }
68a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik        }
69a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik        return false;
70a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik    }
7107b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik
72a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik    /**
73a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik     * Called when prefetch indices are no longer valid for cache prioritization.
74a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik     */
75a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik    static void clearPrefetchPositions(RecyclerView recyclerView) {
76a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik        if (recyclerView.mPrefetchArray != null) {
77a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik            Arrays.fill(recyclerView.mPrefetchArray, -1);
78a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik        }
7907b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik    }
8007b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik
81a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik    static void layoutPrefetch(long deadlineNs, RecyclerView view) {
8207b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik        final int prefetchCount = view.mLayout.getItemPrefetchCount();
8307b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik        if (view.mAdapter == null
8407b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik                || view.mLayout == null
8507b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik                || !view.mLayout.isItemPrefetchEnabled()
8607b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik                || prefetchCount < 1
8707b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik                || view.hasPendingAdapterUpdates()) {
8807b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik            // abort - no work
8907b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik            return;
9007b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik        }
9107b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik
9207b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik        if (view.mPrefetchArray == null
9307b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik                || view.mPrefetchArray.length < prefetchCount) {
9407b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik            view.mPrefetchArray = new int[prefetchCount];
9507b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik        }
9607b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik        Arrays.fill(view.mPrefetchArray, -1);
9707b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik        int viewCount = view.mLayout.gatherPrefetchIndices(
9807b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik                view.mPrefetchDx, view.mPrefetchDy,
9907b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik                view.mState, view.mPrefetchArray);
100a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik        layoutPrefetchImpl(deadlineNs, view.mRecycler, view.mPrefetchArray, viewCount);
101a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik    }
102a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik
103a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik    static void layoutPrefetchImpl(long deadlineNs, RecyclerView.Recycler recycler,
104a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik            int[] prefetchArray, int viewCount) {
105a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik        if (viewCount == 0) return;
106a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik
107a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik        int childPosition = prefetchArray[viewCount - 1];
108a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik        if (childPosition < 0) {
109a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik            throw new IllegalArgumentException("Invalid prefetch position requested: "
110a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik                    + childPosition);
111a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik        }
112a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik        RecyclerView.ViewHolder holder = recycler.tryGetViewHolderForPositionByDeadline(
113a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik                childPosition, false, deadlineNs);
114a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik        if (viewCount > 1) {
115a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik            layoutPrefetchImpl(deadlineNs, recycler, prefetchArray, viewCount - 1);
116a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik        }
117a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik        if (holder != null) {
118a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik            if (holder.isBound()) {
119a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik                // Only give the view a chance to go into the cache if binding succeeded
120a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik                recycler.recycleViewHolderInternal(holder);
121a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik            } else {
122a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik                // Didn't bind, so we can't cache the view, but it will stay in the pool until next
123a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik                // prefetch/traversal. If a View fails to bind, it means we didn't have enough time
124a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik                // prior to the deadline (and won't for other instances of this type, during this
125a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik                // GapWorker pass).
126a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik                recycler.addViewHolderToRecycledViewPool(holder);
127a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik            }
128a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik        }
12907b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik    }
13007b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik
13107b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik    @Override
13207b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik    public void run() {
13307b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik        try {
13407b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik            TraceCompat.beginSection(RecyclerView.TRACE_PREFETCH_TAG);
13507b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik
13607b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik            if (mRecyclerViews.isEmpty()) {
13707b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik                // abort - no work to do
13807b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik                return;
13907b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik            }
14007b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik
14107b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik            // Query last vsync so we can predict next one. Note that drawing time not yet
14207b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik            // valid in animation/input callbacks, so query it here to be safe.
14307b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik            long lastFrameVsyncNanos = TimeUnit.MILLISECONDS.toNanos(
14407b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik                    mRecyclerViews.get(0).getDrawingTime());
14507b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik            if (lastFrameVsyncNanos == 0) {
14607b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik                // abort - couldn't get last vsync for estimating next
14707b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik                return;
14807b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik            }
14907b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik
150a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik            // TODO: consider rebasing deadline if frame was already dropped due to long UI work.
151a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik            // Next frame will still wait for VSYNC, so we can still use the gap if it exists.
15207b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik            long nextFrameNanos = lastFrameVsyncNanos + mFrameIntervalNs;
15307b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik
15407b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik            // NOTE: it's safe to iterate over mRecyclerViews since we know that (currently),
15507b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik            // no attach or detach will occur as a result of creating/binding view holders.
15607b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik            // If any prefetch work attached Views, we'd have to avoid fighting over the list.
15707b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik            final int count = mRecyclerViews.size();
15807b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik            for (int i = 0; i < count; i++) {
15907b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik                layoutPrefetch(nextFrameNanos, mRecyclerViews.get(i));
16007b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik            }
16107b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik
16207b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik            // TODO: consider rescheduling self, if there's more work to do
16307b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik        } finally {
16407b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik            mPostTimeNs = 0;
16507b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik            TraceCompat.endSection();
16607b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik        }
16707b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik    }
16807b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik}
169