1 /* 2 * Copyright 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package androidx.paging 18 19 import androidx.annotation.IntRange 20 import androidx.paging.PagingConfig.Companion.MAX_SIZE_UNBOUNDED 21 import androidx.paging.PagingSource.LoadResult.Page.Companion.COUNT_UNDEFINED 22 import kotlin.jvm.JvmField 23 import kotlin.jvm.JvmOverloads 24 25 /** 26 * An object used to configure loading behavior within a [Pager], as it loads content from a 27 * [PagingSource]. 28 */ 29 public class PagingConfig 30 @JvmOverloads 31 public constructor( 32 /** 33 * Defines the number of items loaded at once from the [PagingSource]. 34 * 35 * Should be several times the number of visible items onscreen. 36 * 37 * Configuring your page size depends on how your data is being loaded and used. Smaller page 38 * sizes improve memory usage, latency, and avoid GC churn. Larger pages generally improve 39 * loading throughput, to a point (avoid loading more than 2MB from SQLite at once, since it 40 * incurs extra cost). 41 * 42 * If you're loading data for very large, social-media style cards that take up most of a 43 * screen, and your database isn't a bottleneck, 10-20 may make sense. If you're displaying 44 * dozens of items in a tiled grid, which can present items during a scroll much more quickly, 45 * consider closer to 100. 46 * 47 * Note: [pageSize] is used to inform [PagingSource.LoadParams.loadSize], but is not enforced. A 48 * [PagingSource] may completely ignore this value and still return a valid 49 * [Page][PagingSource.LoadResult.Page]. 50 */ 51 @JvmField public val pageSize: Int, 52 53 /** 54 * Prefetch distance which defines how far from the edge of loaded content an access must be to 55 * trigger further loading. Typically should be set several times the number of visible items 56 * onscreen. 57 * 58 * E.g., If this value is set to 50, a [PagingData] will attempt to load 50 items in advance of 59 * data that's already been accessed. 60 * 61 * A value of 0 indicates that no list items will be loaded until they are specifically 62 * requested. This is generally not recommended, so that users don't observe a placeholder item 63 * (with placeholders) or end of list (without) while scrolling. 64 */ 65 @JvmField @IntRange(from = 0) public val prefetchDistance: Int = pageSize, 66 67 /** 68 * Defines whether [PagingData] may display `null` placeholders, if the [PagingSource] provides 69 * them. 70 * 71 * [PagingData] will present `null` placeholders for not-yet-loaded content if two conditions 72 * are met: 73 * 1) Its [PagingSource] can count all unloaded items (so that the number of nulls to present is 74 * known). 75 * 2) [enablePlaceholders] is set to `true` 76 */ 77 @JvmField public val enablePlaceholders: Boolean = true, 78 79 /** 80 * Defines requested load size for initial load from [PagingSource], typically larger than 81 * [pageSize], so on first load data there's a large enough range of content loaded to cover 82 * small scrolls. 83 * 84 * Note: [initialLoadSize] is used to inform [PagingSource.LoadParams.loadSize], but is not 85 * enforced. A [PagingSource] may completely ignore this value and still return a valid initial 86 * [Page][PagingSource.LoadResult.Page]. 87 */ 88 @JvmField 89 @IntRange(from = 1) 90 public val initialLoadSize: Int = pageSize * DEFAULT_INITIAL_PAGE_MULTIPLIER, 91 /** 92 * Defines the maximum number of items that may be loaded into [PagingData] before pages should 93 * be dropped. 94 * 95 * If set to [MAX_SIZE_UNBOUNDED], pages will never be dropped. 96 * 97 * This can be used to cap the number of items kept in memory by dropping pages. This value is 98 * typically many pages so old pages are cached in case the user scrolls back. 99 * 100 * This value must be at least two times the [prefetchDistance] plus the [pageSize]). This 101 * constraint prevent loads from being continuously fetched and discarded due to prefetching. 102 * 103 * [maxSize] is best effort, not a guarantee. In practice, if [maxSize] is many times 104 * [pageSize], the number of items held by [PagingData] will not grow above this number. 105 * Exceptions are made as necessary to guarantee: 106 * * Pages are never dropped until there are more than two pages loaded. Note that a 107 * [PagingSource] may not be held strictly to [requested pageSize][PagingConfig.pageSize], so 108 * two pages may be larger than expected. 109 * * Pages are never dropped if they are within a prefetch window (defined to be `pageSize + 110 * (2 * prefetchDistance)`) of the most recent load. 111 * 112 * @see PagingConfig.MAX_SIZE_UNBOUNDED 113 */ 114 @JvmField @IntRange(from = 2) public val maxSize: Int = MAX_SIZE_UNBOUNDED, 115 116 /** 117 * Defines a threshold for the number of items scrolled outside the bounds of loaded items 118 * before Paging should give up on loading pages incrementally, and instead jump to the user's 119 * position by triggering REFRESH via invalidate. 120 * 121 * Defaults to [COUNT_UNDEFINED], which disables invalidation due to scrolling large distances. 122 * 123 * Note: In order to allow [PagingSource] to resume from the user's current scroll position 124 * after invalidation, [PagingSource.getRefreshKey] must be implemented. 125 * 126 * @see PagingSource.getRefreshKey 127 * @see PagingSource.jumpingSupported 128 */ 129 @JvmField public val jumpThreshold: Int = COUNT_UNDEFINED 130 ) { 131 init { 132 if (!enablePlaceholders && prefetchDistance == 0) { 133 throw IllegalArgumentException( 134 "Placeholders and prefetch are the only ways" + 135 " to trigger loading of more data in PagingData, so either placeholders" + 136 " must be enabled, or prefetch distance must be > 0." 137 ) 138 } 139 if (maxSize != MAX_SIZE_UNBOUNDED && maxSize < pageSize + prefetchDistance * 2) { 140 throw IllegalArgumentException( 141 "Maximum size must be at least pageSize + 2*prefetchDist" + 142 ", pageSize=$pageSize, prefetchDist=$prefetchDistance" + 143 ", maxSize=$maxSize" 144 ) 145 } 146 <lambda>null147 require(jumpThreshold == COUNT_UNDEFINED || jumpThreshold > 0) { 148 "jumpThreshold must be positive to enable jumps or COUNT_UNDEFINED to disable jumping." 149 } 150 } 151 152 public companion object { 153 /** 154 * When [maxSize] is set to [MAX_SIZE_UNBOUNDED], the maximum number of items loaded is 155 * unbounded, and pages will never be dropped. 156 */ 157 @Suppress("MinMaxConstant") public const val MAX_SIZE_UNBOUNDED: Int = Int.MAX_VALUE 158 internal const val DEFAULT_INITIAL_PAGE_MULTIPLIER = 3 159 } 160 } 161