Skip to content

Image Preloading

Eli Hart edited this page Jun 18, 2019 · 3 revisions

Epoxy provides a generic preloading system that allows you to kick off background fetches for View content before a model is actually bound. This is useful for preloading images, video, or any other content you might need to show.

Right now an implementation for Glide preloading is provided, but you can easily create preloaders for other libraries.

Glide Preloading

If you load images using Glide then you can leverage Epoxy's built in support for easily preloading Glide images.

The Dependency

First, add a dependency on Epoxy's Glide preloading module

implementation 'com.airbnb.android:epoxy-glide-preloading:3.x.y' // Use latest version

Define ImageViews to Preload

To specify which ImageViews will have a preload request executed for them, add the Preloadable interface to the Model you want to preload.

  • If your EpoxyModel is from a View annotated with @ModelView then have that view implement Preloadable
  • If your Model uses a ViewHolder then have that EpoxyHolder subclass implement Preloadable

In both cases the viewsToPreload property should provide a list containing all ImageViews that will be preloaded (in most cases this will just be a single ImageView, but we support multiple if needed).

Enabling Preloading on the RecyclerView

Finally, register a Glide preloader on your RecyclerView using the addGlidePreloader extension function.

epoxyRecyclerView.addGlidePreloader(
    Glide.with(this), // Pass a Glide RequestManager
    preloader = glidePreloader { requestManager, epoxyModel: YourModelType_, viewData ->
         // Use the requestManager to build your image request and return it here.
         // It is important that the request returned here matches EXACTLY to the request
         // that will be executed when the model is bound, otherwise you won't get a cache hit!

         // The EpoxyModel that is being preloaded is provided, so you can use its
         // data to know which url to load.
         // The viewData can also be used to get optional metadata about the view.
    }
)

If you don't use EpoxyRecyclerView then there is a RecyclerView#addGlidePreloader extension as well - the only difference is you need to manually provide your EpoxyController.

Customization

And that's it! Your image request will be executed before the model is bound. This is the simplest way to set up preloading, but there is room for customization and flexibility if needed. See the code documentation on addGlidePreloader for details. You can also look at the epoxy-preloadersample for sample code.

Default Sizing and Scaling

For the best preloading performance your View's image request will have to exactly match the preloaded request so that Glide is able to do a memory cache hit. For a memory cache hit to happen, both requests need to have the exact same image dimensions, tranformations, scaling, and other options set.(http://bumptech.github.io/glide/doc/caching.html)

To help with that, Epoxy's implementation automatically detects the size of the ImageView and its ScaleType. It then sets up the Glide preload target with a matching size and applies a scale transformation to match the ScaleType. This should help most general cases to get an accurate preload, but if needed you can modify the Glide preloaders however you like.

Debugging

To check that preloading is working as expected you can look at Glide's logs. (http://bumptech.github.io/glide/doc/debugging.html)

First, enable verbose logging with adb shell setprop log.tag.Engine VERBOSE Then in logcat look for Glide Engine logs as you scroll through your preloading list. The first time an image is loaded you will see a log message starting with Engine: Started new load

If an image request is made and there is a cache hit you will instead see a message like Engine: Loaded resource from cache or Engine: Loaded resource from active resources.

Check that as you scroll the urls being loaded are for items further down the list, off screen. This means the preloading is being activated.

Check that you see cache hits in the logs as you scroll a new image into view. If this doesn't seem to be the case, look at the cache key of each load. This starts with key: EngineKey. Look for differences in cache key between the prefetched request and the actual request - this will give clues for what you need to change to make the prefetch match the actual request.

Tips for Preloading

Most preloading implementations will simply warm a cache, so when the model is eventually bound it will make a request for its content and get a cache hit. For this to work it is important that the request executed in the preloader exactly matches the request in the View. It is recommended that you prefetch request and actual request share the same code path.