2

I am trying to download blog features images form a WordPress blog page and then load them later.

Right now, every time the user scrolls down and up the image load again and again. Any idea...plzzz..I tried many solutions, for example, this one: Preload multiple images with Glide

But, it didn't work.

I am using implementation 'com.github.bumptech.glide:glide:3.7.0'

And this is my code:

RecyclerViewAdapter

public void getthumbnail(String imageurl, final ImageView imageView, final int position){

    ServiceWrapper serviceWrapper = new ServiceWrapper(null);
    Call<GetThumbnail> call = serviceWrapper.getThumbnailCall(imageurl);
    call.enqueue(new Callback<GetThumbnail>() {


        @Override
        public void onResponse(Call<GetThumbnail> call, Response<GetThumbnail> response) {
            if (response.body() != null && response.isSuccessful()) {
                try {

                    if (response.body().getMediaDetails()!=null){

                       // Log.e("recycler adapter", " image is here--  " + response.body().getMediaDetails().getSizes().getThumbnail().getSourceUrl());
                       // Log.e("Full IMG SIZE - ", " THIS IS FULL IMAGE URL--  " + response.body().getMediaDetails().getSizes().getFull().getSourceUrl());

                        imagepath.add(position, response.body().getMediaDetails().getSizes().getFull().getSourceUrl());


                        Glide.with(mContext)
                                .load(response.body().getMediaDetails().getSizes().getFull().getSourceUrl())
                                .diskCacheStrategy(DiskCacheStrategy.SOURCE)
                                .into(imageView);

                    }else {

                    }
                }catch (Exception e){
                   // Log.e("adapter", "fail not media tag "+ e.toString());
                }

            }
        }

        @Override
        public void onFailure(Call<GetThumbnail> call, Throwable t) {

          //  Log.e("adapter", " faile  image "+t.toString());
        }
    });

}

UPDATE I found this code and I put it on top of the other Glide code but it didn't work:

Glide.with(mContext)
                                .load(response.body().getMediaDetails().getSizes().getFull().getSourceUrl())
                                .downloadOnly(new SimpleTarget<File>() {
                                    @Override
                                    public void onResourceReady(File resource, GlideAnimation<? super File> glideAnimation) {

                                    }
                                });

UPDATE: Now I used this code and it doesnt download the images faster:

Glide.with(mContext)
                                .load(response.body().getMediaDetails().getSizes().getFull().getSourceUrl())
                                .downloadOnly(new SimpleTarget<File>() {
                                    @Override
                                    public void onResourceReady(File resource, GlideAnimation<? super File> glideAnimation) {

                                        Glide.with(mContext)
                                                .load(response.body().getMediaDetails().getSizes().getFull().getSourceUrl())
                                                .diskCacheStrategy(DiskCacheStrategy.SOURCE)
                                                .into(imageView);
                                    }
                                });

Here is the complete ViewAdapter.java

import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.support.v7.widget.CardView;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.Html;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.request.animation.GlideAnimation;
import com.bumptech.glide.request.target.SimpleTarget;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class RecyclerViewAdapter extends 
RecyclerView.Adapter<RecyclerView.ViewHolder> {

private ArrayList<Model> dataset;
private Context mContext;
private ArrayList<Model> list;
private RecyclerViewAdapter adapter;
private RecyclerView recyclerView;
private LinearLayoutManager mLayoutManager;
public static List<WPPost> mListPost;
ArrayList<String> imagepath = new ArrayList<>();
private String baseURL = "https://www.myfitbytes.com/";

public RecyclerViewAdapter(ArrayList<Model> mlist, Context context) {
    this.dataset = mlist;
    this.mContext = context;
}

public static class ImageTypeViewHolder extends RecyclerView.ViewHolder{


    TextView title, subtitle, date;
    ImageView imageView;
    CardView cardview;

    public ImageTypeViewHolder(View itemView) {
        super(itemView);

        this.title = (TextView)  itemView.findViewById(R.id.title);
        //this.subtitle = (TextView) itemView.findViewById(R.id.subtitle);
        this.date = (TextView) itemView.findViewById(R.id.date);
        this.imageView = (ImageView) itemView.findViewById(R.id.Icon);
        this.cardview = (CardView) itemView.findViewById(R.id.cardview);
    }
}

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View view = LayoutInflater.from( parent.getContext()).inflate(R.layout.postdetails, parent, false);





    return new ImageTypeViewHolder(view) ;
}

@Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
    final Model object = dataset.get(position);
   // Log.d("RecyclerViewAdapter", "IMAGE="+object.Image);
    imagepath.add(position, "");

    if (Build.VERSION.SDK_INT >= 24)
    {
        //( (ImageTypeViewHolder) holder).subtitle.setText(Html.fromHtml(object.subtitle , Html.FROM_HTML_MODE_LEGACY));
        ( (ImageTypeViewHolder) holder).title.setText( Html.fromHtml(object.title , Html.FROM_HTML_MODE_LEGACY) );
        ( (ImageTypeViewHolder) holder).date.setText( Html.fromHtml(object.date , Html.FROM_HTML_MODE_LEGACY) );
    }
    else
    {
        //( (ImageTypeViewHolder) holder).subtitle.setText(Html.fromHtml(object.subtitle ));
        ( (ImageTypeViewHolder) holder).title.setText( Html.fromHtml(object.title ));
        ( (ImageTypeViewHolder) holder).date.setText( Html.fromHtml(object.date ));
    }


    ( (ImageTypeViewHolder) holder).imageView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Intent intent = new Intent(mContext, WPPostDetails.class);
            intent.putExtra("itemPosition", position);
            mContext.startActivity(intent);
        }
    });



    try {
        getthumbnail(object.Image, ( (ImageTypeViewHolder) holder).imageView, position);



    }catch (Exception e){
     //   Log.e("adapter ","failed to get image "+e.toString() );
    }
    /// dataset.get(position)



}


public void getthumbnail(String imageurl, final ImageView imageView, final int position){

    ServiceWrapper serviceWrapper = new ServiceWrapper(null);
    Call<GetThumbnail> call = serviceWrapper.getThumbnailCall(imageurl);
    call.enqueue(new Callback<GetThumbnail>() {


        @Override
        public void onResponse(Call<GetThumbnail> call, final Response<GetThumbnail> response) {
            if (response.body() != null && response.isSuccessful()) {
                try {

                    if (response.body().getMediaDetails()!=null){

                       // Log.e("recycler adapter", " image is here--  " + response.body().getMediaDetails().getSizes().getThumbnail().getSourceUrl());
                       // Log.e("Full IMG SIZE - ", " THIS IS FULL IMAGE URL--  " + response.body().getMediaDetails().getSizes().getFull().getSourceUrl());

                        imagepath.add(position, response.body().getMediaDetails().getSizes().getFull().getSourceUrl());

                        Glide.with(mContext)
                                .load(response.body().getMediaDetails().getSizes().getFull().getSourceUrl())
                                .downloadOnly(new SimpleTarget<File>() {
                                    @Override
                                    public void onResourceReady(File resource, GlideAnimation<? super File> glideAnimation) {

                                        Glide.with(mContext)
                                                .load(response.body().getMediaDetails().getSizes().getFull().getSourceUrl())
                                                .diskCacheStrategy(DiskCacheStrategy.SOURCE)
                                                .into(imageView);
                                    }
                                });


                    }else {

                    }
                }catch (Exception e){
                   // Log.e("adapter", "fail not media tag "+ e.toString());
                }

            }
        }

        @Override
        public void onFailure(Call<GetThumbnail> call, Throwable t) {

          //  Log.e("adapter", " faile  image "+t.toString());
        }
    });

}




@Override
public int getItemCount() {

    return dataset.size() ;
   }
 }

UPDATE So, now that I tried to update glide 3 to 4.x I am getting the following in error in build.gradle.

All com.android.support libraries must use the exact same version specification (mixing versions can lead to runtime crashes). Found versions 27.1.1, 27.1.0, 26.1.0. Examples include com.android.support:support-compat:27.1.1 and com.android.support:animated-vector-drawable:27.1.0 less... (⌘F1) There are some combinations of libraries, or tools and libraries, that are incompatible, or can lead to bugs. One such incompatibility is compiling with a version of the Android support libraries that is not the latest version (or in particular, a version lower than your targetSdkVersion).

And the code is:

apply plugin: 'com.android.application'

android {
compileSdkVersion 26
  defaultConfig {
    applicationId "com.myfitbytes"
    minSdkVersion 14
    targetSdkVersion 26
    versionCode 3
    versionName "3.0"
    testInstrumentationRunner 
 "android.support.test.runner.AndroidJUnitRunner"
   }
    buildTypes {
     release {
         minifyEnabled false
         proguardFiles getDefaultProguardFile('proguard-android.txt'), 
     'proguard-rules.pro'
    }
}

packagingOptions {
    exclude 'META-INF/DEPENDENCIES'
    exclude 'META-INF/LICENSE'
    exclude 'META-INF/LICENSE.txt'
    exclude 'META-INF/license.txt'
    exclude 'META-INF/NOTICE'
    exclude 'META-INF/NOTICE.txt'
    exclude 'META-INF/notice.txt'
    exclude 'META-INF/ASL2.0'
  }
 }

dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:26.1.0'

implementation 'com.android.support.constraint:constraint-layout:1.1.3'
implementation 'com.android.support:design:26.1.0'
implementation 'com.android.support:support-v4:26.1.0'

//library for wordpress rest api
implementation 'com.android.support:cardview-v7:26.1.0'
implementation 'com.android.support:recyclerview-v7:26.0.0-beta2'
implementation 'com.google.code.gson:gson:2.6.2'
implementation 'com.squareup.retrofit2:retrofit:2.3.0'
implementation 'com.squareup.retrofit2:converter-gson:2.0.2'
implementation 'com.squareup.okhttp:okhttp:2.4.0'
implementation 'com.squareup.okhttp3:okhttp:2.0.2'
implementation 'com.squareup.okhttp3:logging-interceptor:3.9.1'

  //implementation 'com.github.bumptech.glide:glide:3.7.0'
implementation 'com.github.bumptech.glide:glide:4.8.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.8.0'


implementation 'com.android.volley:volley:1.0.0'

implementation 'org.ocpsoft.prettytime:prettytime:4.0.1.Final'

//Firebase
implementation 'com.google.firebase:firebase-core:16.0.4'
implementation 'com.google.firebase:firebase-messaging:17.3.3'

implementation 'org.apache.httpcomponents:httpcore:4.4.1'
//implementation 'org.apache.httpcomponents:httpclient:4.5.6'
//implementation group: 'org.apache.httpcomponents' , name: 'httpclient- android' , version: '4.3.5.1'

implementation files('libs/google-http-client-1.24.1.jar')
implementation files('libs/httpclient-4.5.3.jar')

//bottom nav
implementation 'com.aurelhubert:ahbottomnavigation:2.1.0'

//picasso to download image from url faster
implementation 'com.squareup.picasso:picasso:2.71828'

testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'

 }

 apply plugin: 'com.google.gms.google-services'

And, this line is in red:

implementation 'com.android.support:appcompat-v7:26.1.0'
aminography
  • 21,986
  • 13
  • 70
  • 74
Maduro
  • 713
  • 5
  • 23
  • 44
  • Do you want the images be available outside the app? For example the user can see them in gallery or a file explorer. Another option is that the images are locally in your app and they would be cleared by clearing app data. Which one is your requirement? – aminography Nov 08 '18 at 08:33
  • Hi, the only thing that I need is to make this app faster. Right now when the user open the app the images take like 10-15 seconds to load, plus when the user start scrolling up and down images start changing from one position to another, for example, when the first image shows up and you scroll down and then up and go back to the first image becomes image #10 for like 5 seconds and then returns to it original place. However, I don't want the image to show on their gallery, they might feel I am watching them – Maduro Nov 08 '18 at 15:24
  • I think showing the images displaced at the wrong position comes from `RecyclerView`'s view pooling. How large is your images? Are they the main page images of https://www.myfitbytes.com ? – aminography Nov 09 '18 at 08:19
  • Image size is 300x200 – Maduro Nov 09 '18 at 11:18
  • However, they are at the wrong position for 15 seconds..then they take the right place..it just that its a slow – Maduro Nov 09 '18 at 11:20
  • Does `Model.Image` returns the correct image url? – aminography Nov 09 '18 at 13:10
  • Yes..everything works fine..it just that its slow ..when the user scrolls up and down..the image are loading slow.. – Maduro Nov 09 '18 at 13:22

3 Answers3

2

The loading delay problem and image item disposition comes from onBindViewHolder method where you call a webservice in it. Everytime a list item is appeared in scrolling, onBindViewHolder is called to initialize the view with correct values. So you should not call a webservice here at all (but in the code it is called everytime). As Glide creates a queue of requests, it guarantees that the image would be downloaded only one time because it caches them. Also it guarantees that the image would be loaded if the ImageView is in visible area of the RecyclerView in scrolling process.

On the other hand, reaching the image url in your case needs two level of webservice api-s. So we should combine Glide procedure and retrieving the image url procedure to reach the best performance. Using Glide-OkHttp3-Integration library, I have developed this two level of asynchronous calls which makes the Glide aware of your data flow.

• Note that you should clean and rebuild your project to create GlideApp class in compile time.

ViewAdapter.java

// some code blocks

@Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
    final Model object = dataset.get(position);

    if (Build.VERSION.SDK_INT >= 24) {
        // ( (ImageTypeViewHolder) holder).subtitle.setText(Html.fromHtml(object.subtitle , Html.FROM_HTML_MODE_LEGACY));
        ((ImageTypeViewHolder) holder).title.setText(Html.fromHtml(object.title , Html.FROM_HTML_MODE_LEGACY));
        ((ImageTypeViewHolder) holder).date.setText(Html.fromHtml(object.date , Html.FROM_HTML_MODE_LEGACY));
    } else {
        // ((ImageTypeViewHolder) holder).subtitle.setText(Html.fromHtml(object.subtitle));
        ((ImageTypeViewHolder) holder).title.setText(Html.fromHtml(object.title));
        ((ImageTypeViewHolder) holder).date.setText(Html.fromHtml(object.date));
    }


    ((ImageTypeViewHolder) holder).imageView.setOnClickListener(new View.OnClickListener() {
       @Override
       public void onClick(View v) {
           Intent intent = new Intent(mContext, WPPostDetails.class);
           intent.putExtra("itemPosition", position);
           mContext.startActivity(intent);
       }
    });

    JsonApiGlideUrl url = new JsonApiGlideUrl(object.Image);
    GlideApp.with(imageView.getContext())
         .load(url)
         .into(imageView);
}

// some code blocks

OkHttpAppGlideModule.java

import android.content.Context;
import android.support.annotation.NonNull;

import com.bumptech.glide.Glide;
import com.bumptech.glide.Registry;
import com.bumptech.glide.annotation.GlideModule;
import com.bumptech.glide.module.AppGlideModule;

import java.io.InputStream;

/**
 * Registers OkHttp related classes via Glide's annotation processor.
 *
 * <p>For Applications that depend on this library and include an
 * {@link AppGlideModule} and Glide's annotation processor, this class
 * will be automatically included.
 */
@GlideModule
public final class OkHttpAppGlideModule extends AppGlideModule {

    @Override
    public void registerComponents(@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) {
        registry.replace(JsonApiGlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory());
    }

    @Override
    public boolean isManifestParsingEnabled() {
        return false;
    }

}

JsonApiGlideUrl.java

import com.bumptech.glide.load.model.GlideUrl;
import com.bumptech.glide.load.model.Headers;

import java.net.URL;

public class JsonApiGlideUrl extends GlideUrl {

    public JsonApiGlideUrl(URL url) {
        super(url);
    }

    public JsonApiGlideUrl(String url) {
        super(url);
    }

    public JsonApiGlideUrl(URL url, Headers headers) {
        super(url, headers);
    }

    public JsonApiGlideUrl(String url, Headers headers) {
        super(url, headers);
    }

}

OkHttpUrlLoader.java

import android.support.annotation.NonNull;

import com.bumptech.glide.load.Options;
import com.bumptech.glide.load.model.ModelLoader;
import com.bumptech.glide.load.model.ModelLoaderFactory;
import com.bumptech.glide.load.model.MultiModelLoaderFactory;

import java.io.InputStream;

import okhttp3.Call;
import okhttp3.OkHttpClient;

/**
 * A simple model loader for fetching media over http/https using OkHttp.
 */
public class OkHttpUrlLoader implements ModelLoader<JsonApiGlideUrl, InputStream> {

    private final Call.Factory client;

    // Public API.
    @SuppressWarnings("WeakerAccess")
    public OkHttpUrlLoader(@NonNull Call.Factory client) {
        this.client = client;
    }

    @Override
    public boolean handles(@NonNull JsonApiGlideUrl url) {
        return true;
    }

    @Override
    public LoadData<InputStream> buildLoadData(@NonNull JsonApiGlideUrl model, int width, int height, @NonNull Options options) {
        return new LoadData<>(model, new OkHttpStreamFetcher(client, model));
    }

    /**
     * The default factory for {@link OkHttpUrlLoader}s.
     */
    // Public API.
    @SuppressWarnings("WeakerAccess")
    public static class Factory implements ModelLoaderFactory<JsonApiGlideUrl, InputStream> {

        private static volatile Call.Factory internalClient;
        private final Call.Factory client;

        private static Call.Factory getInternalClient() {
            if (internalClient == null) {
                synchronized (Factory.class) {
                    if (internalClient == null) {
                        internalClient = new OkHttpClient();
                    }
                }
            }
            return internalClient;
        }

        /**
         * Constructor for a new Factory that runs requests using a static singleton client.
         */
        public Factory() {
            this(getInternalClient());
        }

        /**
         * Constructor for a new Factory that runs requests using given client.
         *
         * @param client this is typically an instance of {@code OkHttpClient}.
         */
        public Factory(@NonNull Call.Factory client) {
            this.client = client;
        }

        @NonNull
        @Override
        public ModelLoader<JsonApiGlideUrl, InputStream> build(@NonNull MultiModelLoaderFactory multiFactory) {
            return new OkHttpUrlLoader(client);
        }

        @Override
        public void teardown() {
            // Do nothing, this instance doesn't own the client.
        }

    }

}

OkHttpStreamFetcher.java

import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;

import com.bumptech.glide.Priority;
import com.bumptech.glide.load.DataSource;
import com.bumptech.glide.load.HttpException;
import com.bumptech.glide.load.data.DataFetcher;
import com.bumptech.glide.load.model.GlideUrl;
import com.bumptech.glide.util.ContentLengthInputStream;
import com.bumptech.glide.util.Preconditions;

import java.io.IOException;
import java.io.InputStream;
import java.util.Map;

import okhttp3.Call;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;

/**
 * Fetches an {@link InputStream} using the okhttp library.
 */
public class OkHttpStreamFetcher implements DataFetcher<InputStream>, okhttp3.Callback {

    private static final String TAG = "OkHttpFetcher";
    private final Call.Factory client;
    private final JsonApiGlideUrl url;
    private OkHttpJsonApiFetcher okHttpJsonApiFetcher;
    private InputStream stream;
    private ResponseBody responseBody;
    private DataCallback<? super InputStream> callback;
    // call may be accessed on the main thread while the object is in use on other threads. All other
    // accesses to variables may occur on different threads, but only one at a time.
    private volatile Call call;

    // Public API.
    @SuppressWarnings("WeakerAccess")
    public OkHttpStreamFetcher(Call.Factory client, JsonApiGlideUrl url) {
        this.client = client;
        this.url = url;
    }

    @Override
    public void loadData(@NonNull Priority priority, @NonNull final DataCallback<? super InputStream> callback) {

        okHttpJsonApiFetcher = new OkHttpJsonApiFetcher(client, url);
        okHttpJsonApiFetcher.loadData(new DataCallback<GlideUrl>() {

            @Override
            public void onDataReady(@Nullable GlideUrl data) {
                Request.Builder requestBuilder = new Request.Builder().url(data.toStringUrl());
                for (Map.Entry<String, String> headerEntry : data.getHeaders().entrySet()) {
                    String key = headerEntry.getKey();
                    requestBuilder.addHeader(key, headerEntry.getValue());
                }
                Request request = requestBuilder.build();
                OkHttpStreamFetcher.this.callback = callback;

                call = client.newCall(request);
                call.enqueue(OkHttpStreamFetcher.this);
            }

            @Override
            public void onLoadFailed(@NonNull Exception e) {
                callback.onLoadFailed(e);
            }
        });
    }

    @Override
    public void onFailure(@NonNull Call call, @NonNull IOException e) {
        if (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.d(TAG, "OkHttp failed to obtain result", e);
        }
        callback.onLoadFailed(e);
    }

    @Override
    public void onResponse(@NonNull Call call, @NonNull Response response) {
        responseBody = response.body();
        if (response.isSuccessful()) {
            long contentLength = Preconditions.checkNotNull(responseBody).contentLength();
            stream = ContentLengthInputStream.obtain(responseBody.byteStream(), contentLength);
            callback.onDataReady(stream);
        } else {
            callback.onLoadFailed(new HttpException(response.message(), response.code()));
        }
    }

    @Override
    public void cleanup() {
        okHttpJsonApiFetcher.cleanup();

        try {
            if (stream != null) {
                stream.close();
            }
        } catch (IOException e) {
            // Ignored
        }
        if (responseBody != null) {
            responseBody.close();
        }
        callback = null;
    }

    @Override
    public void cancel() {
        okHttpJsonApiFetcher.cancel();

        Call local = call;
        if (local != null) {
            local.cancel();
        }
    }

    @NonNull
    @Override
    public Class<InputStream> getDataClass() {
        return InputStream.class;
    }

    @NonNull
    @Override
    public DataSource getDataSource() {
        return DataSource.REMOTE;
    }

}

OkHttpJsonApiFetcher.java

import android.support.annotation.NonNull;
import android.util.Log;

import com.bumptech.glide.load.HttpException;
import com.bumptech.glide.load.data.DataFetcher;
import com.bumptech.glide.load.model.GlideUrl;

import java.io.IOException;
import java.io.InputStream;
import java.util.Map;

import okhttp3.Call;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;

/**
 * Fetches an {@link InputStream} using the okhttp library.
 */
public class OkHttpJsonApiFetcher implements okhttp3.Callback {

    private static final String TAG = "OkHttpJsonApiFetcher";
    private final Call.Factory client;
    private final JsonApiGlideUrl url;
    private ResponseBody responseBody;
    private DataFetcher.DataCallback<? super GlideUrl> callback;
    // call may be accessed on the main thread while the object is in use on other threads. All other
    // accesses to variables may occur on different threads, but only one at a time.
    private volatile Call call;

    // Public API.
    @SuppressWarnings("WeakerAccess")
    public OkHttpJsonApiFetcher(Call.Factory client, JsonApiGlideUrl url) {
        this.client = client;
        this.url = url;
    }

    public void loadData(@NonNull final DataFetcher.DataCallback<? super GlideUrl> callback) {
        Request.Builder requestBuilder = new Request.Builder().get().url(url.toStringUrl());
        for (Map.Entry<String, String> headerEntry : url.getHeaders().entrySet()) {
            String key = headerEntry.getKey();
            requestBuilder.addHeader(key, headerEntry.getValue());
        }
        Request request = requestBuilder.build();
        this.callback = callback;

        call = client.newCall(request);
        call.enqueue(this);
    }

    @Override
    public void onFailure(@NonNull Call call, @NonNull IOException e) {
        if (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.d(TAG, "OkHttp failed to obtain result", e);
        }
        callback.onLoadFailed(e);
    }

    @Override
    public void onResponse(@NonNull Call call, @NonNull Response response) {
        responseBody = response.body();
        if (response.isSuccessful() && responseBody != null) {
            try {
                String json = responseBody.string();
                String url = JsonApiDataModel.getSourceUrl(json);
                callback.onDataReady(new GlideUrl(url));
            } catch (IOException e) {
                callback.onLoadFailed(new HttpException(response.message(), response.code()));
                e.printStackTrace();
            }
        } else {
            callback.onLoadFailed(new HttpException(response.message(), response.code()));
        }
    }

    public void cleanup() {
        if (responseBody != null) {
            responseBody.close();
        }
        callback = null;
    }

    public void cancel() {
        Call local = call;
        if (local != null) {
            local.cancel();
        }
    }

}

JsonApiDataModel.java

import com.google.gson.Gson;
import com.google.gson.annotations.SerializedName;

public class JsonApiDataModel {

    @SerializedName("media_details")
    MediaDetails mediaDetails;

    public static String getSourceUrl(String json) {
        return new Gson().fromJson(json, JsonApiDataModel.class).mediaDetails.sizes.full.sourceUrl;
    }

    public class MediaDetails {
        @SerializedName("sizes")
        Sizes sizes;
    }

    public class Sizes {
        // you can use full, medium or thumbnail here!
        @SerializedName("full")
        Full full;
    }

    public class Full {
        @SerializedName("source_url")
        String sourceUrl;
    }

}

.

Test Code & Visual Result:

import com.aminography.glideapplication.glide.okhttp3.GlideApp;
import com.aminography.glideapplication.glide.okhttp3.JsonApiGlideUrl;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        final ImageView imageView = findViewById(R.id.imageView);
        TextView textView = findViewById(R.id.textView);

        String sourceUrl = "https://www.myfitbytes.com/wp-json/wp/v2/media/2811";

        textView.setText("JsonApiGlideUrl:\n\n" + sourceUrl);

        final JsonApiGlideUrl url = new JsonApiGlideUrl(sourceUrl);
        GlideApp.with(MainActivity.this).load(url).into(imageView);
    }

}

enter image description here

aminography
  • 21,986
  • 13
  • 70
  • 74
  • =( stills getting errors `11-10 10:39:58.340 10322-10322/com.myfitbytes E/AndroidRuntime: FATAL EXCEPTION: main Process: com.myfitbytes, PID: 10322 java.lang.RuntimeException: Unable to start activity ComponentInfo{com.myfitbytes/com.myfitbytes.MainActivity}: java.lang.NullPointerException: Argument must not be null at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2955)` – Maduro Nov 10 '18 at 15:41
  • Also, remember that object.Image has many links..In this example, MainActivity (or in my case Blog.java Fragment) is passing only one link...But I there are many – Maduro Nov 10 '18 at 15:42
  • Are you running above test code? Or your own code with RecyclerView? Your mentioned error means that you are passing null context to start activity. – aminography Nov 10 '18 at 15:52
  • 1
    Man..i need to buy you a coffe after this jjsajjajajaja...no joke! – Maduro Nov 10 '18 at 15:54
  • You should pass a link just like something which is used in example. It returns a json containing all details of a post. – aminography Nov 10 '18 at 15:54
  • yes, I did pass just one link but the code crash...I clean and rebuild the code..but still...I see that your example works...but not with my Adapter =( – Maduro Nov 10 '18 at 15:56
  • I think if possible, share your full source code in a private gitlab repository so that I can run and debug it. – aminography Nov 10 '18 at 15:58
  • My user id in gitlab for inviting me is aminography, or use my email amin.maxim@gmail.com – aminography Nov 10 '18 at 16:02
  • Sent..so it is almost working..now the new problem is that its not showing 2 of the image blog post...In my app, when you open the app an activity start with logo and a progress bar (3 seconds) then it goes to Blog.java and then start fetching and loading posts. So, you think we can make the Adapter or Blog.java run while the Welcome activity start running? I think, in this way it can be faster – Maduro Nov 10 '18 at 16:39
  • Do you add me to the repository? – aminography Nov 10 '18 at 17:22
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/183424/discussion-between-aminography-and-jmaster-b). – aminography Nov 10 '18 at 17:22
  • Just want to say thank you for your help!!!! It really helped me a lot! – Maduro Nov 16 '18 at 20:56
  • Hiii...I have a question..I am trying to do the same thing in iOS...it is that possible? can I get wordpress blog post in iOS? please let me know. Any link or comment will be appreciate =) – Maduro Dec 04 '18 at 14:30
  • Hi. Unfortunately I have not developed any iOS program and not informed about this issue in iOS :-( – aminography Dec 04 '18 at 16:58
0

• First of all, I strongly recommend you to use Glide v4.

The loading delay problem and image item disposition comes from onBindViewHolder method where you call a webservice in it. Everytime a list item is appeared in scrolling, onBindViewHolder is called to initialize the view with correct values. So you should not call a webservice here at all (but in the code it is called everytime). As Glide creates a queue of requests, it guarantees that the image would be downloaded only one time because it caches them. Also it guarantees that the image would be loaded if the ImageView is in visible area of the RecyclerView in scrolling process. In addition, by setting diskCacheStrategy to DiskCacheStrategy.ALL, Glide shows an image which is resized according to the ImageView size.

@Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
    final Model object = dataset.get(position);

    if (Build.VERSION.SDK_INT >= 24) {
        // ( (ImageTypeViewHolder) holder).subtitle.setText(Html.fromHtml(object.subtitle , Html.FROM_HTML_MODE_LEGACY));
        ((ImageTypeViewHolder) holder).title.setText(Html.fromHtml(object.title , Html.FROM_HTML_MODE_LEGACY));
        ((ImageTypeViewHolder) holder).date.setText(Html.fromHtml(object.date , Html.FROM_HTML_MODE_LEGACY));
    } else {
        // ((ImageTypeViewHolder) holder).subtitle.setText(Html.fromHtml(object.subtitle));
        ((ImageTypeViewHolder) holder).title.setText( Html.fromHtml(object.title));
        ((ImageTypeViewHolder) holder).date.setText( Html.fromHtml(object.date));
    }


    ((ImageTypeViewHolder) holder).imageView.setOnClickListener(new View.OnClickListener() {
       @Override
       public void onClick(View v) {
           Intent intent = new Intent(mContext, WPPostDetails.class);
           intent.putExtra("itemPosition", position);
           mContext.startActivity(intent);
       }
    });

    Glide.with(imageView.getContext())
         .load(object.Image)
         .apply(RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.ALL))
         .into(imageView);
}

By the above changes, your RecyclerView should work smoothly as well as fast. If you want to make Glide more integrated with RecyclerView, you can follow this. But I think this is not necessary and the previous level is enough.

aminography
  • 21,986
  • 13
  • 70
  • 74
  • Thanks for your help. Do you still want me to use Glide inside `getthumbnai`?l – Maduro Nov 09 '18 at 14:12
  • You're welcome dude. No, do not use `getThumbnail()` method. Use `Glide` directly in `onBindViewHolder()` as I did. Please do this and tell me about the result. I used `Glide` in a `RecyclerView` to show hundreds images which each of them had MB size. So using this structure does not lead to performance and speed leakage. – aminography Nov 09 '18 at 14:56
  • The problem now is that I THINK...(i don't know but I think) that glide 4 is giving me an error with the library. I will update the question so you can see my `app` – Maduro Nov 09 '18 at 15:00
  • The cause of showing incorrect images according to positions is that you was calling a webservice api asynchronously for each item. When its result is received (for example after 15 seconds) and tried to show in ImageView, according to RecyclerView's view pooling mechanism, it tries to show another image in the target ImageView because items are scrolled. So a race condition is created and it makes the problem. – aminography Nov 09 '18 at 15:07
  • Please check my update...thanks. This happens after updated the glide library to 4.8 – Maduro Nov 09 '18 at 15:36
  • Try to update support libraries to 28.0.0. You should keep all dependencies to last versions. – aminography Nov 09 '18 at 16:12
  • The problem was Picasso, I was using Picasso before and so I had the library included. But now the only problem that I have is that `into(imageView);` is not working. `imageView` is in red. – Maduro Nov 09 '18 at 16:28
  • I had to access imageView using `( (ImageTypeViewHolder) holder).imageView` but still it doesn't show any image =/ now.. – Maduro Nov 09 '18 at 16:41
  • Use Glide everywhere instead of Picasso. What is exactly content of `object.Image`? Could you debug it? – aminography Nov 09 '18 at 16:45
  • `object.Image` is an object that contains blog post details for example, it returns this json `https://www.myfitbytes.com/wp-json/wp/v2/media/2811` – Maduro Nov 09 '18 at 17:01
  • So, every time I scroll up and down that object is been called ...=/ – Maduro Nov 09 '18 at 17:04
  • The problem is that the url in `object.Image` is not the image url, so you should call this api before creation of RecyclerView and maintain image urls. I mean you should pass a ready data to the RecyclerView adapter, not calling apis in showing time. – aminography Nov 09 '18 at 19:00
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/183387/discussion-between-jmaster-b-and-aminography). – Maduro Nov 09 '18 at 19:03
-1

The current version for Glide is '4.8.0'. Any reason you don't want to work with that version? https://github.com/bumptech/glide

https://github.com/bumptech/glide/blob/master/samples/flickr/src/main/java/com/bumptech/glide/samples/flickr/FlickrPhotoGrid.java

You need to use a RecyclerView. Here is an example:

 grid.addItemDecoration(new RecyclerView.ItemDecoration() {
      @Override
      public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
          RecyclerView.State state) {
        outRect.set(gridMargin, gridMargin, gridMargin, gridMargin);
      }
    });
    grid.setRecyclerListener(new RecyclerView.RecyclerListener() {
      @Override
      public void onViewRecycled(RecyclerView.ViewHolder holder) {
        PhotoViewHolder photoViewHolder = (PhotoViewHolder) holder;
        GlideApp.with(FlickrPhotoGrid.this).clear(photoViewHolder.imageView);
      }
    });
Waxhaw
  • 599
  • 5
  • 9
  • I pulled a snippet form the link I show you above that is on their GitHub account. The RecyclerView will take care of caching the thumbnails for you as you scroll the window. Look at the classes and example where FlickrPhotoGrid.java is found. I would suggest you leverage that example. RecyclerView does a ton of caching based on what is viewable and is highly optimized for the problem you are trying to solve. – Waxhaw Nov 06 '18 at 16:33