Проблема задержки загрузки и расположение элемента изображения происходят из метода onBindViewHolder
, в котором вы вызываете веб-сервис. Каждый раз, когда элемент прокрутки появляется при прокрутке, вызывается onBindViewHolder
для инициализации представления с правильными значениями. Так что вам вообще не следует вызывать веб-сервис (но в коде он вызывается каждый раз). Поскольку Glide
создает очередь запросов, это гарантирует, что изображение будет загружено только один раз, потому что оно их кэширует. Также это гарантирует, что изображение будет загружено, если ImageView
находится в видимой области RecyclerView
в процессе прокрутки.
С другой стороны, для достижения URL изображения в вашем случае необходимы два уровня API веб-сервиса. Таким образом, мы должны объединить процедуру Glide
и извлекать процедуру URL изображения, чтобы достичь наилучшей производительности. Используя библиотеку Glide-OkHttp3-Integration , я разработал эти два уровня асинхронных вызовов, которые позволяют Glide
знать о вашем потоке данных.
• Обратите внимание, что вы должны очистить и перестроить свой проект , чтобы создать GlideApp
класс во время компиляции.
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;
}
}
.
Тестовый код и визуальный результат:
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](https://media.giphy.com/media/4QF3BJpqi5pQnvQcsM/giphy.gif)