如何在 Glide 监听器中获取 http responseCode?
How to get http responseCode in Glide listener?
我正在使用 Glide 来显示位于远程服务器上的图像的预览。需要以不同的方式处理不同的响应代码。例如,当服务器响应代码 202
时,我必须等待特定的套接字事件。这是我的代码:
fun ImageView.loadRounded(glideUrl: GlideUrl, radius: Int = 20) {
GlideApp.with(this.context)
.load(glideUrl)
.apply(RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.ALL))
.apply(RequestOptions.bitmapTransform(RoundedCorners(radius)))
.listener(object : RequestListener<Drawable> {
override fun onLoadFailed(e: GlideException?, model: Any?, target: Target<Drawable>?, isFirstResource: Boolean): Boolean {
e?.rootCauses?.forEach {
logd("${it.message}")
}
return false
}
override fun onResourceReady(resource: Drawable?, model: Any?, target: Target<Drawable>?, dataSource: DataSource?, isFirstResource: Boolean): Boolean {
return false
}
})
.into(this)
}
在服务器发送响应代码 202
的情况下,在 onLoadFailed
中我得到以下输出:
java.io.IOException(java.lang.RuntimeException: setDataSource failed: status = 0x80000000)
发生这种情况是因为 202
是成功状态代码之一。 Glide 尝试下载图片,但是没有。
方法e?.logRootCauses()
也不给出响应代码。
有没有办法在 Glide 侦听器中获取 http 响应代码?
找到了一种截取状态码并用另一个替换它的方法。为此,我用修改后的版本替换了 Glide
中使用的 DataFetcher
实现。
以下是步骤:
如果您还没有 @GlideModule
,请创建它:
@GlideModule
public class MyGlideModule extends AppGlideModule {
@Override
public void registerComponents(@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) {
registry.append(GlideUrl.class, InputStream.class,
new MyOkHttpUrlLoader.Factory(new OkHttpClient()));
}
}
其中标准 OkHttpUrlLoader
也被替换为修改版本。不幸的是,您不能只扩展标准实现,因为您必须与它的私有成员进行交互。所以它应该是原始 OkHttpUrlLoader
的完整副本,但是 return 在 buildLoadData
方法中自定义 DataFetcher
。另外 build
方法应该 return 加载器修改版本的实例:
import androidx.annotation.NonNull;
import com.bumptech.glide.load.Options;
import com.bumptech.glide.load.model.GlideUrl;
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 MyOkHttpUrlLoader implements ModelLoader<GlideUrl, InputStream> {
private final Call.Factory client;
// Public API.
@SuppressWarnings("WeakerAccess")
public MyOkHttpUrlLoader(@NonNull Call.Factory client) {
this.client = client;
}
@Override
public boolean handles(@NonNull GlideUrl url) {
return true;
}
@Override
public LoadData<InputStream> buildLoadData(@NonNull GlideUrl model, int width, int height,
@NonNull Options options) {
return new LoadData<>(model, new MyOkHttpStreamFetcher(client, model));
}
/**
* The default factory for {@link com.bumptech.glide.integration.okhttp3.OkHttpUrlLoader}s.
*/
// Public API.
@SuppressWarnings("WeakerAccess")
public static class Factory implements ModelLoaderFactory<GlideUrl, InputStream> {
private static volatile Call.Factory internalClient;
private final Call.Factory client;
/**
* 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;
}
private static Call.Factory getInternalClient() {
if (internalClient == null) {
synchronized (Factory.class) {
if (internalClient == null) {
internalClient = new OkHttpClient();
}
}
}
return internalClient;
}
@NonNull
@Override
public ModelLoader<GlideUrl, InputStream> build(MultiModelLoaderFactory multiFactory) {
return new MyOkHttpUrlLoader(client);
}
@Override
public void teardown() {
// Do nothing, this instance doesn't own the client.
}
}
}
这是我修改后的 DataFetcher 版本,它几乎是标准实现 (OkHttpStreamFetcher) 的副本。不同之处在于,在 onResponse
方法中,我检查代码,如果是 202
,我将其替换为错误代码 418:
import android.util.Log;
import androidx.annotation.NonNull;
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 com.zultys.mobiledroid.utils.logger.Logger;
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 MyOkHttpStreamFetcher implements DataFetcher<InputStream>, okhttp3.Callback {
private static final String TAG = "OkHttpFetcher";
private final Call.Factory client;
private final GlideUrl url;
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 ZultysOkHttpStreamFetcher(Call.Factory client, GlideUrl url) {
this.client = client;
this.url = url;
}
@Override
public void loadData(@NonNull Priority priority,
@NonNull final DataCallback<? super InputStream> callback) {
Request.Builder requestBuilder = new Request.Builder().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();
// Code 202 means that file was sent from another MX in MX net,
// and you have to wait until it gets copied to your MX. After that you will receive
// csta packet with messageStatus IMS_FILE_DOWNLOAD, and can repeat same request.
if (response.code() == 202) {
callback.onLoadFailed(new HttpException("I'm a teapot", 418));
} else 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() {
try {
if (stream != null) {
stream.close();
}
} catch (IOException e) {
// Ignored
}
if (responseBody != null) {
responseBody.close();
}
callback = null;
}
@Override
public void 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;
}
}
我正在使用 Glide 来显示位于远程服务器上的图像的预览。需要以不同的方式处理不同的响应代码。例如,当服务器响应代码 202
时,我必须等待特定的套接字事件。这是我的代码:
fun ImageView.loadRounded(glideUrl: GlideUrl, radius: Int = 20) {
GlideApp.with(this.context)
.load(glideUrl)
.apply(RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.ALL))
.apply(RequestOptions.bitmapTransform(RoundedCorners(radius)))
.listener(object : RequestListener<Drawable> {
override fun onLoadFailed(e: GlideException?, model: Any?, target: Target<Drawable>?, isFirstResource: Boolean): Boolean {
e?.rootCauses?.forEach {
logd("${it.message}")
}
return false
}
override fun onResourceReady(resource: Drawable?, model: Any?, target: Target<Drawable>?, dataSource: DataSource?, isFirstResource: Boolean): Boolean {
return false
}
})
.into(this)
}
在服务器发送响应代码 202
的情况下,在 onLoadFailed
中我得到以下输出:
java.io.IOException(java.lang.RuntimeException: setDataSource failed: status = 0x80000000)
发生这种情况是因为 202
是成功状态代码之一。 Glide 尝试下载图片,但是没有。
方法e?.logRootCauses()
也不给出响应代码。
有没有办法在 Glide 侦听器中获取 http 响应代码?
找到了一种截取状态码并用另一个替换它的方法。为此,我用修改后的版本替换了 Glide
中使用的 DataFetcher
实现。
以下是步骤:
如果您还没有 @GlideModule
,请创建它:
@GlideModule
public class MyGlideModule extends AppGlideModule {
@Override
public void registerComponents(@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) {
registry.append(GlideUrl.class, InputStream.class,
new MyOkHttpUrlLoader.Factory(new OkHttpClient()));
}
}
其中标准 OkHttpUrlLoader
也被替换为修改版本。不幸的是,您不能只扩展标准实现,因为您必须与它的私有成员进行交互。所以它应该是原始 OkHttpUrlLoader
的完整副本,但是 return 在 buildLoadData
方法中自定义 DataFetcher
。另外 build
方法应该 return 加载器修改版本的实例:
import androidx.annotation.NonNull;
import com.bumptech.glide.load.Options;
import com.bumptech.glide.load.model.GlideUrl;
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 MyOkHttpUrlLoader implements ModelLoader<GlideUrl, InputStream> {
private final Call.Factory client;
// Public API.
@SuppressWarnings("WeakerAccess")
public MyOkHttpUrlLoader(@NonNull Call.Factory client) {
this.client = client;
}
@Override
public boolean handles(@NonNull GlideUrl url) {
return true;
}
@Override
public LoadData<InputStream> buildLoadData(@NonNull GlideUrl model, int width, int height,
@NonNull Options options) {
return new LoadData<>(model, new MyOkHttpStreamFetcher(client, model));
}
/**
* The default factory for {@link com.bumptech.glide.integration.okhttp3.OkHttpUrlLoader}s.
*/
// Public API.
@SuppressWarnings("WeakerAccess")
public static class Factory implements ModelLoaderFactory<GlideUrl, InputStream> {
private static volatile Call.Factory internalClient;
private final Call.Factory client;
/**
* 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;
}
private static Call.Factory getInternalClient() {
if (internalClient == null) {
synchronized (Factory.class) {
if (internalClient == null) {
internalClient = new OkHttpClient();
}
}
}
return internalClient;
}
@NonNull
@Override
public ModelLoader<GlideUrl, InputStream> build(MultiModelLoaderFactory multiFactory) {
return new MyOkHttpUrlLoader(client);
}
@Override
public void teardown() {
// Do nothing, this instance doesn't own the client.
}
}
}
这是我修改后的 DataFetcher 版本,它几乎是标准实现 (OkHttpStreamFetcher) 的副本。不同之处在于,在 onResponse
方法中,我检查代码,如果是 202
,我将其替换为错误代码 418:
import android.util.Log;
import androidx.annotation.NonNull;
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 com.zultys.mobiledroid.utils.logger.Logger;
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 MyOkHttpStreamFetcher implements DataFetcher<InputStream>, okhttp3.Callback {
private static final String TAG = "OkHttpFetcher";
private final Call.Factory client;
private final GlideUrl url;
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 ZultysOkHttpStreamFetcher(Call.Factory client, GlideUrl url) {
this.client = client;
this.url = url;
}
@Override
public void loadData(@NonNull Priority priority,
@NonNull final DataCallback<? super InputStream> callback) {
Request.Builder requestBuilder = new Request.Builder().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();
// Code 202 means that file was sent from another MX in MX net,
// and you have to wait until it gets copied to your MX. After that you will receive
// csta packet with messageStatus IMS_FILE_DOWNLOAD, and can repeat same request.
if (response.code() == 202) {
callback.onLoadFailed(new HttpException("I'm a teapot", 418));
} else 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() {
try {
if (stream != null) {
stream.close();
}
} catch (IOException e) {
// Ignored
}
if (responseBody != null) {
responseBody.close();
}
callback = null;
}
@Override
public void 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;
}
}