A fellow developer shared a Weibo post (https://weibo.com/1808884742/IApbpEVQr) where blogger @王波粒 noticed a peculiar phenomenon on the Mate 30 Pro. I highly recommend watching the video first.
The question concerns a difference observed in 2020: some Huawei samples displayed feed images during scrolling, while some other samples waited until the list stopped. This is not a claim about every current device; configuration, caches, and system behavior can differ even with the same client version.
The same version of Weibo behaving differently on different phones was something I wanted to understand as a system performance engineer. Had Weibo made a specific adaptation? What had changed? Let’s look at the code and see what we can learn.
Historical version note (reviewed 2026-10-14): This is a 2020 case, labelled in the original as Weibo 10.8.1. The old images and impressions remain, but the original APK hash, complete Huawei firmware/PerfSDK versions, runtime trace, and controlled measurements are unavailable. Image work, vendor scrolling physics, and subjective feel require separate verification; this is not a ranking of current devices.

What makes this so special?
- Deferring some image work during fast scrolling is a trade-off, not a universal default. Downloading, decoding, transforming, caching, and displaying are separate stages; pausing one need not stop all of them.
- Images appearing mid-scroll need not be downloading at that moment; they may come from cache or earlier requests. Displaying images mid-scroll can reduce placeholder time while adding CPU, GPU, memory, or network work. The benefit depends on visible content and frame timing.
- I felt that the Huawei sample decelerated more gently at the time. Without matched initial velocity, content, refresh mode, and firmware, this is an impression rather than proof of universally longer or more iOS-like motion.
This was my view of the collaboration at the time, not a new benchmark result:
As an Android developer, I really admired the attention Huawei paid to this small part of the experience. I thought its work with Weibo was a useful example for cooperation with other major apps.
Contextual Notes
- Around June 2020, I saw some non-Huawei samples wait until scrolling stopped before showing images. When I looked again in August, images appeared during scrolling too. This is historical observation, not today’s server configuration.
- The code includes feed_scroll_loadimage_enable and gray-release configuration access. Without server responses, it cannot establish rollout dates, account/device coverage, or immediate propagation.
- Identical APKs can take different paths due to configuration, cache, OS version, and hot patches. PatchProxy checks appear in the images; an APK version alone does not identify executed code.
- The sample label is Weibo 10.8.1. Obfuscated method numbers vary across screenshots and are not stable APIs or proof of one exact build.
The Verdict
The images supply two clues: Weibo registers threshold callbacks controlling ImageLoader, and vendor Framework snippets calculate scrolling distance and velocity differently. The callback actions are visible; automatically enabling a new curve whenever the SDK is present, or guaranteeing smoother rendering, requires more evidence.
- HwPerfVelocityCallback reports the relationship to a threshold; the shown callbacks do not deliver numeric velocity every frame.
- The downward callback calls resume, and the upward callback calls pause. Which jobs pause, and whether in-flight work continues, depends on this ImageLoader implementation, which is not shown.
- The Framework fragment uses a SmartSlideOverScroller branch. Its complete activation conditions and connection to this SDK require the matching firmware or trace.
I tried this in AOSP myself and got a demo running. If you’re interested in the details, feel free to contact me. That demo is not attached to this repository and was not reproduced in this review.
How the Optimization Works
Phenomenon Analysis
For a typical AbsListView gesture:
- SCROLL_STATE_TOUCH_SCROLL = 1: the finger drags the list. Merely pressing down need not enter this state before the touch threshold is crossed.
- SCROLL_STATE_FLING = 2: an eligible release starts inertial motion. Minimum fling velocity, boundaries, and nested-scroll consumption also matter.
- SCROLL_STATE_IDLE = 0: no scrolling.
Dragging also has velocity and can be fast. Neither “velocity exists only during fling” nor “a finger on the screen guarantees slow scrolling” is correct.
Phase 1 Optimization
With the pictured optimization flag false, non-IDLE states call pause. With it true, non-FLING states call resume. That allows loading during TOUCH_SCROLL; it does not guarantee jank-free frames or immediate network requests.
Phase 2 Optimization (Fling)
The callbacks aim to pause during fast fling and resume as it slows. Request reduction and frame improvements depend on caching, cancellation, and the actual branch; two function calls cannot quantify them.
Curve tuning is separate. AOSP already uses a nonlinear spline, not constant-speed linear motion. A longer or gentler fling does not by itself make rendering faster.
Technical Deep Dive (Reverse Engineering)
These are partial decompiler and Framework views, without the complete SDK conditions, ImageLoader implementation, or firmware version.
Phase 1 State Callback
| Optimization flag | State | Direct action shown |
|---|---|---|
| true | IDLE / TOUCH_SCROLL | resume |
| true | FLING | No direct pause/resume in this branch |
| false | TOUCH_SCROLL / FLING | pause |
| false | IDLE | resume |

Thus state 2 does not itself pause loading in the enabled path; earlier state remains relevant until other code, such as a threshold callback, acts. The image also handles async_card and video preloading, so its whole performance effect cannot be assigned to images. A PatchProxy override can bypass the visible ordinary path.
Monitoring Velocity
The screenshot passes HwPerfSpeedLoader.HwPerfVelocityCallback to the list helper:
HwPerfonVelocityDownToThreshold()calls ImageLoader.getInstance().resume().HwPerfonVelocityUpToThreshold()calls ImageLoader.getInstance().pause().

The threshold value, units, initial notification behavior, and hysteresis are not shown. Do not invent one universal value for all densities and refresh rates.
The Fling Curve (Framework Layer)
The supplied Huawei update fragment invokes getUpdateDistance/getUpdateVelocity on SmartSlideOverScroller and applies a distance cutoff:

Distance calculation:

Velocity calculation:

The velocity expression uses an exponential and normalized time, but the images omit full parameter sources, entry conditions, and the firmware tag. They show a different implementation, not proof of a longer total duration, universal activation, or equivalence to iPhone behavior. Distance correction and cutoff logic also matter.
The original discussion of iOS transitions remains background reading, not a replacement for measurements on these devices.
Handling Other Manufacturers
The boolean a is the helper’s registration result. False can reflect lack of support, initialization failure, or other conditions; it does not uniquely identify a non-Huawei phone. The ordinary fallback requires Build.VERSION.SDK_INT >= 28 (Android 9) AND the FeedAbManager feature flag:

FeedAbManager exposes several feature readers:

The corresponding method reads feed_scroll_loadimage_enable:

The configuration fragment distinguishes SYNC_WITH_SERVER, REMAIN_UNCHANGE_INAPPLIFECYCLE, and NOTCACHED policies. Names alone do not establish a fresh server request on every callback.

This fallback can permit mid-scroll loading, but the images do not show that it gains the same velocity callbacks or vendor curve. Measure the paths separately; an image appearing is not proof that PerfSDK is active.
The “Scroll-to-Click” Challenge
In Android 10 AbsListView, ACTION_DOWN during fling can be intercepted and treated as stopping or taking over scrolling rather than an ordinary item click. This explains some stop-then-tap interactions, not a rule that every Android list always requires two taps.
A longer fling can extend the interval in which it can be interrupted. Click behavior also depends on event dispatch, target stability, touch slop, and the widget. The comparisons below describe how it felt to me at the time, not measurements that apply to every device.
A short, abrupt fling feels like sliding a stone across rough concrete: it barely travels before stopping. Whether you’re the one throwing it or I’m standing nearby watching, neither of us is likely to admire how gracefully it moves. That was how the stock Android curve felt to me.
A longer, gentler fling is more like sending a block of ice across a smooth frozen surface. It keeps going. iOS took that too far for my taste: it felt painfully slow to settle, and I often had to tap once to stop the list before tapping the item I actually wanted. I preferred MIUI 12’s adjustment at the time. Pairing that kind of tuning with app cooperation, as Huawei and Weibo had done, seemed worth exploring.
I proposed allowing a click after 95% or 98% of the distance, borrowing the idea from Launcher; this was a design suggestion, not a verified MIUI 12/Huawei feature. Distance progress is not remaining time or instantaneous speed. Check target stability, finger movement, and accidental activations, preserve click/accessibility semantics, and do not simply pass touches through to moving items.
Extended Reading
Performance Considerations
Image work spans scheduling, network transfer, decoding, transforms, caches, texture upload, and View updates. Prioritize visible content, bound prefetch, and cancel or deprioritize obsolete off-screen requests when appropriate. Prefetching is legitimate; loading unseen content is not inherently a mistake.
Queues are not necessarily FIFO. The fiftieth image need not wait for all forty-nine predecessors: concurrency, priorities, cancellation, and cache hits matter. List reuse is normal; wrong images often involve stale request-to-item bindings. Resuming paused work may create a burst and needs measurement too.
Modern apps can start with public scroll callbacks and image-library request/lifecycle management instead of a vendor-private SDK. Define units, absolute-speed handling, initial state, hysteresis, and off-screen cleanup. Avoid reflecting into ListView’s private Scroller. If the app owns an OverScroller it can use its public API; VelocityTracker can estimate gesture velocity.
For validation, combine Macrobenchmark frame metrics and Perfetto with fixed content, cache state, network, account/rollout, refresh mode, and input. Compare frame overruns, visible-image timing, requests/bytes, and memory rather than video impressions or mean fps alone. No new benchmark gains are claimed here.
Scroll State Cheat Sheet
For AbsListView.OnScrollListener, the following is an interface summary with explanatory comments:
1 | public interface OnScrollListener { |
onScrollStateChanged reports state changes, not per-frame velocity. onScroll can run repeatedly during scrolling or layout updates; the API phrase “after the scroll has completed” must not be read as “only at final idle.” AOSP’s invokeOnItemScrollListener call sites confirm this.
AndroidX comparison: RecyclerView.OnScrollListener (since 1.0.0) uses IDLE, DRAGGING, and SETTLING. SETTLING also covers programmatic smooth scrolling. onScrolled dx/dy are displacement, not velocity; layout changes can trigger a zero/zero callback. Estimating speed requires valid time intervals and samples, not just translating AbsListView state names.
Scroll State Transitions
Typical gestures include:
- Release after a slow drag: IDLE → TOUCH_SCROLL → IDLE.
- Release satisfying fling conditions: IDLE → TOUCH_SCROLL → FLING → IDLE.
These are not exhaustive. New touches, boundaries, nested scrolling, or programmatic motion can change the path. VelocityTracker also estimates speed before the finger leaves the screen.
Computing the Fling Curve
The following restores the zero-time/end checks from AOSP android-10.0.0_r1 OverScroller.SplineOverScroller.update(). It is a Framework-internal method requiring surrounding fields, not a standalone app class or the Huawei-modified implementation above:
1 | boolean update() { |
SPLINE interpolates sampled curve values. In that branch, multiplying distance/duration by 1000 converts pixels per millisecond to pixels per second. BALLISTIC models deceleration beyond an edge, while CUBIC returns to the valid range; neither should be equated directly with newer EdgeEffect stretch rendering.
The scrolling call chain drives updates; FlingRunnable schedules further work, but that does not guarantee one execution per display refresh. Elapsed-time position calculations can advance after scheduling delays. Internal axis mCurrVelocity values are not a uniform physical-speed contract across every branch; public getCurrVelocity returns the X/Y vector magnitude without direction.
Conclusion
I’d like to see more cooperation like this between manufacturers and apps such as WeChat, Kuaishou, and Douyin. When the two sides spend their time working around each other, users pay for it. Working together on these details looks like a better direction to me.
I included the Pinduoduo job description below because it specifically mentioned working with manufacturers on optimization. It was a 2020 example of that direction, not a current vacancy listing.

About Me && Blog
Below is my personal intro and related links. I look forward to exchanging ideas with fellow professionals. “When three walk together, one can always be my teacher!”
- Blogger Intro
- Blog Content Navigation: A guide for my blog content.
- Curated Excellent Blog Articles - Android Performance Optimization Must-Knows
- Android Performance Optimization Knowledge Planet: The paid community stopped operating in 2026 and no longer accepts new members; this link preserves its history.
One walks faster alone, but a group walks further together.
