Hot questions for Using Glide in okhttp

Question:

Am using glide v4 and okhttp3 integration with glide. I want to change it's timeout time. how to do that? by extending AppGlideModule or is there any other way? I have searched for proper documentation but not seen anywhere.


Answer:

You can change the timeout by creating a class, which extends AppGlideModule and has annotation @GlideModule. Then you override the method registerComponents and inside it, you can create a new OkHttpClient, which you can use to control the Glide request timeout.

First, you should add Glide and OkHttp3 Glide Integration library gradle dependencies in the build.gradle file:

compile 'com.github.bumptech.glide:glide:4.2.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.2.0'
compile("com.github.bumptech.glide:okhttp3-integration:4.2.0") {
    exclude group: 'glide-parent'
}

Second, the sample code for the custom GlideAppModule class:

@GlideModule
public class MyGlideAppModule extends AppGlideModule {
    @Override
    public void registerComponents(Context context, Glide glide, Registry registry) {
        OkHttpClient client = new OkHttpClient.Builder()
                .connectTimeout(20, TimeUnit.SECONDS)
                .readTimeout(20, TimeUnit.SECONDS)
                .build();

        OkHttpUrlLoader.Factory factory = new OkHttpUrlLoader.Factory(client);
        glide.getRegistry().replace(GlideUrl.class, InputStream.class, factory);
    }
}

Credits to Amir Ziarati for providing the solution!

Also, refer to this article about Glide customization with modules for more information about the differences between Glide 3.x and Glide 4.x.

Question:

How to check if the URL is an image URL that must be either PNG, GIF, JPG formats

I see that it can be done with this code:

URLConnection connection = new URL("http://foo.bar/w23afv").openConnection();
String contentType = connection.getHeaderField("Content-Type");
boolean image = contentType.startsWith("image/");

But, I need to check using either Glide or OKHttpClient.

How to achieve this using two techniques mentioned above?


Answer:

If all you want to do is check the Content-Type of a URL, without actually downloading the content, an HTTP HEAD request would be appropriate.

The HEAD method is identical to GET except that the server MUST NOT return a message-body in the response. The metainformation contained in the HTTP headers in response to a HEAD request SHOULD be identical to the information sent in response to a GET request. This method can be used for obtaining metainformation about the entity implied by the request without transferring the entity-body itself. This method is often used for testing hypertext links for validity, accessibility, and recent modification.

You can do this with OkHttp as follows:

OkHttpClient client = new OkHttpClient();

Request request = new Request.Builder()
        .url("http://foo.bar/w23afv")
        .head()
        .build();

try {
    Response response = client.newCall(request).execute();
    String contentType = response.header("Content-Type");
    boolean image = false;
    if (contentType != null) {
        image = contentType.startsWith("image/");
    }

} catch (IOException e) {
    // handle error
}

Question:

I need to get the http response headers from image. How to create a interceptor for each request from Glide? Is it necessary create a Glide Module?


Answer:

Frist, you have to integrate Glide and OkHttp using custom GlideModule instead of the default one.

Refer this example on how to do that.

Then you have to write an OkHttp interceptor to intercept the response information and get the response headers.

Refer this OkHttp Logging Interceptor code and you will get an idea on how to intercept the response and get the response headers.

You have to set that interceptor to your OkHttpClient (which mostly would be a singleton instance):

OkHttpClient okHttpClient = new OkHttpClient.Builder().addInterceptor(yourInterceptor).build();

That OkHttpClient instance has to be passed to your custom GlideModule implementation's registerComponents() callback:

glide.register(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory( okHttpClient ));

Question:

I wonder if it's possible to make Glide use the same OkHttpClient which I register and create in Application class?

Since we have a fairly complex OkHttpClient which is consumed by our service layer, we would like to use this instead of having Glide use it's own.

Is it possible to registering this as custom module instead?

OkHttp version: 2.5.0
Glide version: 3.6.1

Best regards, Henric


Answer:

To achieve setting the proper OkHttpClient I chose to use Picasso (2.5.2) instead. Similar libraries, so for our usage we chose Picasso instead.

Simply because we use an older version of OkHttp, and not able to update to OkHttp 3+ just yet.

In CustomApplication class called in onCreate()

private void setupPicasso()
{
    final Picasso picasso = new Picasso.Builder(getApplicationContext())
            .downloader(new OkHttpDownloader(getPrimaryHttpClient()))
            .build();
    Picasso.setSingletonInstance(picasso);
}

Gradle import:

compile 'com.squareup.picasso:picasso:2.5.2'

So now the proper client is being used, and images is working as expected.

I hope this helps anyone at least.

Question:

I'm trying to load images asynchronously into a CardPresenter in Leanback like this.

public interface CustomImageModel {  
    String requestCustomUrl(int width, int height);
}

public static class CustomImageModelGrabber implements CustomImageModel {

    public CustomImageModelGrabber() {

    }

    @Override
    public String requestCustomUrl(int width, int height) {
        OkHttpClient client = new OkHttpClient;
        Request request = new Request.Builder().url(image_url).build();
        return client.newCall(request).execute().body().string();
    }
}

public static class CustomImageUrlLoader extends BaseGlideUrlLoader<CustomImageModel> {  
    public CustomImageUrlLoader(Context context) {
        super( context );
    }

    @Override
    protected String getUrl(CustomImageModel model, int width, int height) {
        return model.requestCustomUrl();
    }
}

In CardPresenter.java

@Override
public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) {

    CustomImageModel customImageRequest = new CustomImageModelGrabber();

    Glide  
            .with( context )
            .using( new CustomImageUrlLoader( context ) )
            .load( customImageRequest )
            .into( imageView1 );
}

Unfortunately this doesn't work as expected. Only a few images are loaded correctly into the card presenter, but most of them are not, and the following error is thrown:

android.os.NetworkOnMainThreadException
android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1303)

It's totally random which work and which don't.

I also tried setting the strict mode in MainActivity.java.

StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);

While this solution loads all images correctly and doesn't throw any NetworkOnMainThreadException errors, it comes with huge performance issues. The scrolling becomes slow and laggy, showing me the following message:

I/Choreographer: Skipped 182 frames!  The application may be doing too much work on its main thread.

Is there any solution to make the images load asynchronously while still maintaining a smooth and good performance?


Answer:

My approach was totally wrong. I thought that BaseGlideUrlLoader runs on a background-thread, but it doesn't.

So the code to go is the following:

@Override
public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) {

    final ImageCardView cardView = (ImageCardView) viewHolder.view;

    OkHttpClient client = new OkHttpClient;
    Request request = new Request.Builder().url(image_url).build();


    client.newCall(request).enqueue(new Callback() {
      @Override public void onFailure(Call call, IOException e) {
        e.printStackTrace();
      }

      @Override public void onResponse(Call call, Response response) throws IOException {
        try (ResponseBody responseBody = response.body()) {
            if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);


            String imageFromResponse = responseBody.string();
            // do whatever is needed to get the image (i.e JSON-handling)

            Handler mainHandler = new Handler(Looper.getMainLooper());
            Runnable myRunnable = new Runnable() {
                @Override
                public void run() {
                    Glide.with(cardView.getContext())
                            .load(imagefromResponse)
                            .error(mDefaultCardImage)
                            .into(cardView.getMainImageView());
                }
            };
        }
    });


}