When aspect ratio is off (cache key bit 63 clear, i.e. Long key ≥ 0 in the signed interpretation used in the fast check) and CalcType is one of:
PERCENT(ordinal 7)SCALED(ordinal 11)DENSITY(ordinal 14)DIAGONAL(ordinal 1)INTERPOLATED(ordinal 5)PERIMETER(ordinal 8)
DimenCache.getOrPut returns compute() directly and does not store the result in the shard table. These paths reduce to baseValue * preComputedFactor — a single multiplication using a value computed once per configuration change.
All other strategy ordinals (AUTO, FLUID, POWER, LOGARITHMIC, FIT, FILL, …) go through the normal cache path when the cache is enabled.
For the six bypassed types, measured cost of a single multiply is lower than a full cache-slot lookup; memoization is still provided by Compose remember (and by call-site batching where used). When aspect ratio is on, the computation is heavier and the cache path is used.
ScreenFactors.updateFactors() runs only on configuration changes and pre-computes:
| Field | Formula |
|---|---|
scale |
sw / 300 |
arMultiplier |
1 + (sw - 300) * (ADJUSTMENT_SCALE + SENSITIVITY_DEFAULT * logNormalizedAr) |
diagonalScale |
sqrt(sm² + lg²) / BASE_DIAGONAL_DP |
powerScale |
(sw / BASE_WIDTH_DP) ^ 0.75 |
logScale |
1 ± 0.4 * ln(sw * INV_BASE_RATIO) |
interpolatedScale |
1 + (sw * INV_BASE_RATIO - 1) * 0.5 |
perimeterScale |
(sm + lg) / BASE_PERIMETER_DP |
aspectRatioMul |
1 + SENSITIVITY_DEFAULT * logNormalizedAr |
Each calculate*Dp function reads the pre-computed factor from ScreenFactors for the default path (qualifier = SMALL_WIDTH, inverter = DEFAULT, customSensitivityK = null). Non-default paths still compute inline but avoid Double conversions where possible.
UiModeType.fromConfiguration(context, null) — which accesses SensorManager, hinge sensor lookup, and WindowMetricsCalculator — is now cached per configuration hash in DimenCache.getCachedUiModeType(context). The cache is invalidated automatically when the configuration hash changes. All *Mode / *Screen facilitators across 48 extension files read from this cache.
- Diagonal:
sqrt((sm² + lg²).toDouble()).toFloat()eliminated — uses pre-computeddiagonalScale. - Power:
ratio.toDouble().pow(0.75).toFloat()eliminated on default path — uses pre-computedpowerScale. Non-default paths useMath.pow. - Logarithmic: raw
kotlin.math.ln()eliminated on default path — uses pre-computedlogScale.
ResizeMath.buildResizeStepsPx writes directly to a pre-allocated FloatArray, avoiding ArrayList<Float> boxing/unboxing overhead.
toDynamicScaledPx, toDynamicScaledDp, sdp, hdp, wdp (and their a/i/ia variants) have specialized Int and Float receiver overloads that avoid Number.toFloat() boxing.
consumer-rules.pro keeps the public API surface (code, compose, common, and the listed core types). The rest of core is not blanket-kept, so R8 may still shrink, obfuscate, and inline internal helpers where no other rule forbids it. (ProGuard’s -allowoptimization is not valid for R8 and was removed.)
DimenCache writes to a Preferences DataStore with namespace com.appdimens.dynamic.cache. The write flow uses sample(500) (not debounce) so that a first-startup burst of cache misses flushes within 500 ms of the first miss, instead of waiting until the burst quiets. For testing, call DimenCache.shutdown() to cancel the internal CoroutineScope and avoid leaked writes during teardown.
Do not use SCALED / PERCENT / DENSITY / DIAGONAL / INTERPOLATED / PERIMETER without AR to measure cache throughput — those calls intentionally bypass shard storage.