— android — 2 min read
You have probably been in a situation where you wanted to load some images into an ImageView but the API call to get the image returns a JSON object that contains a field specifying the actual image URL. This usually happens when you are using a third-party service and can’t modify the behavior of the API.
This was a challenge I faced when I decided to use the Last.FM API for FX Music Player. One way to solve this is to create a loader or AsyncTask that makes the API call then parses the JSON response and return the image URL. But each time you want to load an image you will have to manage multiple requests yourself and this gets difficult when you have a List or RecyclerView where items get reused.
I will show you a neat trick that allows you to load images from a JSON response using the Glide library. Each time you want to load an image, it will just take a single line with no additional stress.
First, create a request class. This will be a regular java class with any fields/variables you want to add to it. Example:
1public class JsonRequest {2 3 // Add any field you can use to identify the request or that will hold values you will4 // pass to the api endpoint. For example this can be an id, name, size or any other thing5}
Next, create a class that models the API JSON response. This is also just a regular java class and you can add any field/variable to it. You can use the JSONSchema2Pojo tool to do the API response modeling. Example:
1public class JsonResponse {2 3 // This class should contain fields that model the response from the api call you are making4 // You can use this site to create a proper model based on the response: http://www.jsonschema2pojo.org/5}
Next, we will have to create a custom Glide ModelLoader. This will be the class doing most of the work here like making the network calls and parsing the JSON response.
In order to keep this post HTTP library agnostic, I didn’t include any HTTP calls in the example code below. Just follow the TODO comments in the code below and use whatever HTTP library you are already using in your project to make the HTTP requests.
1import android.support.annotation.NonNull;2import android.support.annotation.Nullable;3 4import com.bumptech.glide.Priority;5import com.bumptech.glide.load.data.DataFetcher;6import com.bumptech.glide.load.model.stream.StreamModelLoader;7 8import java.io.IOException;9import java.io.InputStream;10 11public class JsonModelLoader implements StreamModelLoader<JsonRequest> {12 13 public JsonModelLoader() {}14 15 @Override16 public DataFetcher<InputStream> getResourceFetcher(final JsonRequest model, int width, int height) {17 // Return a custom ImageLoader that will handle a JSON response18 return new JsonImageLoader(model);19 }20 21 private static class JsonImageLoader implements DataFetcher<InputStream> {22 23 private final JsonRequest model;24 25 JsonImageLoader(@NonNull JsonRequest model) {26 this.model = model;27 }28 29 @Override30 public InputStream loadData(Priority priority) throws IOException {31 JsonResponse jsonResponse = makeApiCallToGetData(model);32 33 if (jsonResponse != null) {34 return getInputStreamFromApiResponse(jsonResponse);35 }36 37 return null;38 }39 40 private @Nullable JsonResponse makeApiCallToGetData(@NonNull JsonRequest model) {41 // TODO: Make the call to the API to fetch data for the given model.42 // You can use any field you added to the model class here like ids, name, and more43 // Feel free to use any networking library here like OkHttp, Ion or any other.44 return null;45 }46 47 private @Nullable InputStream getInputStreamFromApiResponse(@NonNull JsonResponse response) {48 // TODO: Make the actual call to fetch the image data from the url specified in the Json Response49 // Use your favorite Http client (OkHTTP, Ion and more) and just return the input stream50 return null;51 }52 53 @Override54 public String getId() {55 // TODO: Do not return null, rather return a value that uniquely identifies the request else caching won't work56 // This can be any unique field in the model class57 return null;58 }59 60 @Override61 public void cancel() {62 // You can ignore for now63 }64 65 @Override66 public void cleanup() {67 // Do any necessary cleanup68 }69 70 }71 72}
You will have to create a custom ModelLoaderFactory that returns your newly created ModelLoader. Its pretty straightforward, this class just returns a new instance of the JsonModelLoader. Use the code below:
1import android.content.Context;2 3import com.bumptech.glide.load.model.GenericLoaderFactory;4import com.bumptech.glide.load.model.ModelLoader;5import com.bumptech.glide.load.model.ModelLoaderFactory;6 7import java.io.InputStream;8 9class JsonModelLoaderFactory implements ModelLoaderFactory<JsonRequest, InputStream> {10 11 @Override12 public ModelLoader<JsonRequest, InputStream> build(Context context, GenericLoaderFactory factories) {13 return new JsonModelLoader();14 }15 16 @Override17 public void teardown() {} 18}
Then create a GlideModule that will allow us to use the JsonRequest class we created above to make requests with Glide. All we will be doing in this class is just to register our custom component (You can have several components, all you have to do is register them here so they will work). Use the code below to register the JsonModelLoader:
1import android.content.Context;2 3import com.bumptech.glide.Glide;4import com.bumptech.glide.GlideBuilder;5import com.bumptech.glide.load.DecodeFormat;6import com.bumptech.glide.module.GlideModule;7 8import java.io.InputStream;9 10public class MyGlideModule implements GlideModule {11 12 @Override13 public void applyOptions(Context context, GlideBuilder builder) {}14 15 @Override16 public void registerComponents(Context context, Glide glide) {17 // register ModelLoaders here.18 glide.register(JsonRequest.class, InputStream.class, new JsonModelLoaderFactory());19 }20}
Finally, add the GlideModule in your AndroidManifest.xml file. This is pretty easy, just use the code below to do that (don’t forget to change your.company.package.name to your actual package name).
1<manifest xmlns:android="http://schemas.android.com/apk/res/android">23 <application>45 <!-- Register Glide Module -->6 <meta-data7 android:name="**your.company.package.name.MyGlideModule**"8 android:value="GlideModule" />910 </application>1112</manifest
Everything is all set now. If you want to load images, it’s really really easy. Just use do something like this:
1JsonRequest jsonRequest = new JsonRequest();2// Set any of the request object fields you defined3Glide.with(this).load(jsonRequest).into(imageView);
Once you understand the concepts explained here, you will be able to create more custom loaders to handle different types of requests/responses.