Kahibaro
Discord Login Register

16.2 Loading Images from the Internet

Why Loading Images from the Internet Matters

Modern Android apps very often display images that come from remote servers instead of from local resources. Social feeds, product catalogs, user avatars, or news thumbnails are all downloaded over the network. Loading these images correctly is critical for performance, memory usage, and user experience.

Although you could download an image manually with networking code and then display it, in real apps you almost never do this yourself. Instead, you rely on specialized image loading libraries that handle caching, efficient decoding, and smooth display.

This chapter focuses on how to load images from the internet into ImageView using popular libraries in Android, and what you should pay attention to when doing so in a real app.

Basic Idea: Image URLs and ImageView

In a typical scenario, you receive a URL as a String, for example from a REST API. Your goal is to display that image in an ImageView in your layout.

The simplest mental model is:

  1. You have an ImageView in your layout.
  2. You have a remote image URL, for example: https://example.com/images/avatar.png.
  3. You ask an image loading library to download and display it into your ImageView.

Instead of writing code to open HTTP connections, decode Bitmaps, and manage background threads, you call a concise API from a library that does everything under the hood.

Why You Should Use an Image Loading Library

Loading images from the internet involves several technical challenges. A good image loading library helps you with all of them.

First, it runs network operations off the main thread so your UI stays responsive. Second, it decodes images efficiently so your app does not run out of memory when showing many large images. Third, it caches images on disk and in memory, which reduces network usage and speeds up scrolling.

A library also handles image reuse in lists, such as with RecyclerView. When you scroll quickly, ImageView instances get recycled. A good library cancels outdated requests automatically so wrong images do not appear in the wrong list items.

Finally, libraries provide a simple, chainable API for features like placeholders, error images, transformations, and crossfade animations.

Common Image Loading Libraries on Android

Several libraries are popular in Android development. At the time of writing, the most commonly used modern libraries are Coil, Glide, and Picasso.

Coil is written in Kotlin and designed specifically for Kotlin and coroutines. Glide is powerful and widely used, with strong support for lists and GIFs. Picasso is older and simpler, with a very friendly API, though it is less commonly chosen for new projects than Coil or Glide.

All of these libraries integrate with ImageView and handle network operations and caching behind the scenes. You normally pick one library and use it consistently across your app.

Adding an Image Loading Library with Gradle

To use an image loading library, you first add a dependency in your app module build.gradle file. This is the Gradle configuration file for the app module, not the project level file.

For example, for Coil you add something similar to:

dependencies {
    implementation("io.coil-kt:coil:2.5.0")
}

For Glide, a typical dependency entry looks similar to:

dependencies {
    implementation("com.github.bumptech.glide:glide:4.16.0")
    kapt("com.github.bumptech.glide:compiler:4.16.0")
}

For Picasso, it might look like:

dependencies {
    implementation("com.squareup.picasso:picasso:2.8")
}

The exact versions will change over time, so you always check the official documentation or Maven Central for the latest version.

After you add the dependency, you click Sync in Android Studio so Gradle downloads the library. Once the sync completes, you can use the library classes in your Kotlin code through imports, just like any other dependency.

Basic Usage with Coil

Coil is a modern choice for Kotlin apps. Its basic usage with an ImageView is simple and expressive.

Assume you have an ImageView in your activity or fragment layout, with id imageView. The simplest way to load an image URL is:

val imageUrl = "https://example.com/image.jpg"
imageView.load(imageUrl)

Here load is an extension function provided by Coil on ImageView. Coil takes care of running the download on a background thread, decoding the image, caching it, and setting it on the ImageView.

Coil also supports placeholders and error images. A placeholder is shown while the image is still loading, and an error drawable is shown if the download fails. You typically add these using a builder style:

imageView.load(imageUrl) {
    placeholder(R.drawable.placeholder)
    error(R.drawable.error_image)
    crossfade(true)
}

You can also specify transformations such as circular images:

import coil.transform.CircleCropTransformation
imageView.load(imageUrl) {
    transformations(CircleCropTransformation())
}

All of this still uses a single call on the ImageView with a configuration block, so the code stays readable.

Basic Usage with Glide

Glide has a slightly different style and usually starts with Glide.with(context). This method prepares a request manager bound to the lifecycle of that context, for example an Activity or Fragment.

A basic Glide example looks like this:

val imageUrl = "https://example.com/image.jpg"
Glide.with(this)
    .load(imageUrl)
    .into(imageView)

Here this refers to the Activity context. If you are in a fragment, you can pass this as the fragment, or requireContext(). Glide chooses the right lifecycle behavior based on what you pass to with.

As with Coil, you can configure placeholders and error images:

Glide.with(this)
    .load(imageUrl)
    .placeholder(R.drawable.placeholder)
    .error(R.drawable.error_image)
    .into(imageView)

Glide supports many transformations and effects. For instance, a circular crop looks like:

import com.bumptech.glide.load.resource.bitmap.CircleCrop
Glide.with(this)
    .load(imageUrl)
    .transform(CircleCrop())
    .into(imageView)

Glide integrates very well with RecyclerView, and it is common to call Glide.with(itemView) inside a ViewHolder.

Basic Usage with Picasso

Picasso provides a concise, builder style API that is also easy to read. Its basic usage with an ImageView is:

val imageUrl = "https://example.com/image.jpg"
Picasso.get()
    .load(imageUrl)
    .into(imageView)

You can again configure placeholders and error images:

Picasso.get()
    .load(imageUrl)
    .placeholder(R.drawable.placeholder)
    .error(R.drawable.error_image)
    .into(imageView)

Picasso also supports resizing and transformations. For example:

Picasso.get()
    .load(imageUrl)
    .resize(200, 200)
    .centerCrop()
    .into(imageView)

Although Picasso is not as feature rich as Glide or Coil, it remains a straightforward way to add remote image loading to a simple project.

Placeholders, Error States, and Loading Feedback

Users should never stare at a blank space and wonder whether something is broken. Placeholders and error drawables help provide feedback about what is happening.

A placeholder is a temporary image displayed while the real image is being downloaded. This could be a grey box, a generic avatar silhouette, or a branded loading graphic. Once the real image is ready, it replaces the placeholder.

An error drawable is displayed if the request fails, for example due to a network error or a bad URL. It often uses an icon that indicates something went wrong or a neutral fallback image.

Most libraries also support simple animations such as a crossfade between the placeholder and the finished image. Even a subtle visual transition can make the loading experience feel smoother.

When using lists, placeholders are especially important. Without them, recycled views may briefly show the old image from another list item while the new image is loading.

Caching and Performance Considerations

Image loading libraries rely heavily on caching to make scrolling and repeated image display fast. There are typically two types of cache.

A memory cache keeps recently used images in memory as Bitmap objects. If the same URL is requested again while the image is still in the memory cache, the library can display it immediately with no disk or network access.

A disk cache stores images in the device file system, usually in your app cache directory. If a user closes and reopens the app or scrolls away and back, images can be read from disk instead of downloaded again. This improves performance and saves data.

Caching behavior is different from local drawable resources. Drawable resources are always available locally in your APK, while remote images must be fetched at least once. With caching, most subsequent displays will use local caches. Libraries also respect HTTP cache headers from servers when possible.

Image loading libraries usually choose cache sizes and policies automatically. As a beginner, you typically do not override them. In more advanced scenarios, you can adjust caching strategies or clear caches explicitly when needed.

Using Image Loading in RecyclerView

Loading images from the internet becomes most noticeable in scrolling lists such as RecyclerView, where performance and correctness are critical.

When you bind a list item in a RecyclerView.Adapter, you usually have a bind function or override onBindViewHolder. In this method, you receive an item that contains a URL and an ImageView in that item layout.

Using Coil in a view holder can look like:

fun bind(item: User) {
    itemView.userNameTextView.text = item.name
    itemView.avatarImageView.load(item.avatarUrl) {
        placeholder(R.drawable.avatar_placeholder)
        error(R.drawable.avatar_error)
        crossfade(true)
    }
}

With Glide, you normally write:

fun bind(item: User) {
    itemView.userNameTextView.text = item.name
    Glide.with(itemView)
        .load(item.avatarUrl)
        .placeholder(R.drawable.avatar_placeholder)
        .error(R.drawable.avatar_error)
        .into(itemView.avatarImageView)
}

Libraries automatically cancel and restart requests if the same ImageView is reused for a new list item when you scroll. This avoids showing the wrong image in recycled rows. Using a library correctly in this way is much easier than managing AsyncTask or threads manually in each view holder.

Handling Common Issues with Remote Images

When loading images from the internet, you are relying on the network and external servers, which are both unpredictable. There are several common issues you need to consider.

First, the user might be offline or on a slow connection. In that case, image requests can time out or fail. Your placeholder, error images, and text labels should still provide a usable interface. You might also consider retry strategies or an explicit "retry" button in the UI, which you can implement at the app level.

Second, some URLs might be invalid or expire. If you receive incorrect URLs from your API, image loading libraries typically treat this as an error and show the error drawable. Logging such problems can help you detect backend issues.

Third, very large images can use a lot of memory. Libraries usually downsample images to fit the ImageView size automatically, but it is helpful to design server images with appropriate sizes and compression. Avoid using original camera photos at full resolution for simple thumbnails.

Finally, you should be aware of privacy and security. Loading images from unknown or untrusted servers can expose user data such as IP addresses. In most typical apps, images come from your own backend or trusted third parties, but in more advanced cases, you might need to validate URLs or use a proxy server.

Using HTTPS and App Permissions

Most modern image hosting uses HTTPS URLs. This is strongly recommended, because it protects users against eavesdropping and tampering on the network.

When you use a standard image loading library, you do not usually write networking code yourself. The library uses the Android networking stack under the hood. Typically, you do not have to request the INTERNET permission in your manifest for libraries, because they rely on the app manifest that already declares android.permission.INTERNET if needed. If your app does not declare it, network requests will simply fail.

If you plan to load images from a development server, such as http://10.0.2.2 for an emulator, you might have to configure cleartext traffic in your manifest. Production apps should always use HTTPS for security.

Integrating Image Loading into Your App Structure

Once you add an image loading library to your project, you generally use it in activities, fragments, and adapters wherever you need remote images.

For single ImageView usage in an activity, the call is short and similar to the basic examples. In MVVM or other architectures, your view models can expose just the image URL, and the view layer decides how to load and display it with the chosen library.

You should not mix multiple image loading libraries in the same project. Pick one library and apply it consistently. This helps with maintenance and avoids conflicts or duplicate caching.

If you later refactor your app and change libraries, you only need to adjust the places where you load images. Because all libraries follow a similar pattern load URL into ImageView, the conceptual change is small, even though the exact syntax will differ.

Summary of Practical Steps

To load images from the internet in an Android app with minimal effort, you follow a short sequence. First, choose an image loading library appropriate for your project, such as Coil, Glide, or Picasso. Second, add the corresponding dependency to your app module build.gradle and sync the project.

Third, use the library API in your activities, fragments, or adapters to load URLs into ImageView. Include placeholders and error drawables to provide visual feedback. Fourth, rely on the library caching to make scrolling smooth and reduce repeated downloads.

By doing this, you get efficient network usage, better performance, and a much better user experience without dealing with manual HTTP requests or complex bitmap handling.

Views: 1

Comments

Please login to add a comment.

Don't have an account? Register now!