Search code examples
androidasynchronousandroid-glideokhttpleanback

okhttp3 throws NetworkOnMainThreadException when loading images asynchronously with glide


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?


Solution

  • 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());
                    }
                };
            }
        });
    
    
    }