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