在android中,目前主流的加载图片基本都是用Glide框架。这个框架会自动的为我们处理网络加载与缓存图片的各种功能,而我们只需要给图片的地址与显示位置即可。但在加载图片的过程中我们无法知道当前加载的进度,所以我们需要对Glide进行扩展。
同理,我们先来看看效果:

现在我们正式开始吧
添加依赖
implementation 'com.github.bumptech.glide:glide:3.7.0'
implementation 'com.squareup.okhttp3:okhttp:3.9.0'
添加权限
<uses-permission android:name="android.permission.INTERNET" />
替换Glide里面网络通讯组件HTTP为OkHttp。
新建一个OkHttpFetcher类,并且实现DataFetcher接口,代码如下所示:
package cn.xie.fingerprinttest.http;
import com.bumptech.glide.Priority;
import com.bumptech.glide.load.data.DataFetcher;
import com.bumptech.glide.load.model.GlideUrl;
import com.bumptech.glide.util.ContentLengthInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
/**
* 将Glide中的HTTP通讯组件替换成OkHttp。
* @author xiejinbo
* @date 2019/11/12 0012 11:43
*/
public class OkHttpFetcher implements DataFetcher<InputStream> {
private final OkHttpClient client;
private final GlideUrl url;
private InputStream stream;
private ResponseBody responseBody;
private volatile boolean isCancelled;
public OkHttpFetcher(OkHttpClient client, GlideUrl url) {
this.client = client;
this.url = url;
}
@Override
public InputStream loadData(Priority priority) throws Exception {
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();
if (isCancelled) {
return null;
}
Response response = client.newCall(request).execute();
responseBody = response.body();
if (!response.isSuccessful() || responseBody == null) {
throw new IOException("Request failed with code: " + response.code());
}
stream = ContentLengthInputStream.obtain(responseBody.byteStream(),
responseBody.contentLength());
return stream;
}
@Override
public void cleanup() {
try {
if (stream != null) {
stream.close();
}
if (responseBody != null) {
responseBody.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public String getId() {
return url.getCacheKey();
}
@Override
public void cancel() {
isCancelled = true;
}
}
然后新建一个OkHttpGlideUrlLoader类,并且实现ModelLoader:
package cn.xie.fingerprinttest.http;
import android.content.Context;
import com.bumptech.glide.load.data.DataFetcher;
import com.bumptech.glide.load.model.GenericLoaderFactory;
import com.bumptech.glide.load.model.GlideUrl;
import com.bumptech.glide.load.model.ModelLoader;
import com.bumptech.glide.load.model.ModelLoaderFactory;
import java.io.InputStream;
import okhttp3.OkHttpClient;
/**
* 将Glide中的HTTP通讯组件替换成OkHttp。
* @author xiejinbo
* @date 2019/11/12 0012 11:45
*/
public class OkHttpGlideUrlLoader implements ModelLoader<GlideUrl, InputStream> {
private OkHttpClient okHttpClient;
public static class Factory implements ModelLoaderFactory<GlideUrl, InputStream> {
private OkHttpClient client;
public Factory() {
}
public Factory(OkHttpClient client) {
this.client = client;
}
private synchronized OkHttpClient getOkHttpClient() {
if (client == null) {
client = new OkHttpClient();
}
return client;
}
@Override
public ModelLoader<GlideUrl, InputStream> build(Context context, GenericLoaderFactory factories) {
return new OkHttpGlideUrlLoader(getOkHttpClient());
}
@Override
public void teardown() {
}
}
public OkHttpGlideUrlLoader(OkHttpClient client) {
this.okHttpClient = client;
}
@Override
public DataFetcher<InputStream> getResourceFetcher(GlideUrl model, int width, int height) {
return new OkHttpFetcher(okHttpClient, model);
}
}
接下来,新建一个MyGlideModule类并实现GlideModule接口,然后在registerComponents()方法中将我们刚刚创建的OkHttpGlideUrlLoader和OkHttpFetcher注册到Glide当中,将原来的HTTP通讯组件给替换掉,如下所示:
package cn.xie.fingerprinttest.http;
import android.content.Context;
import com.bumptech.glide.Glide;
import com.bumptech.glide.GlideBuilder;
import com.bumptech.glide.load.model.GlideUrl;
import com.bumptech.glide.module.GlideModule;
import java.io.InputStream;
import okhttp3.OkHttpClient;
/**
* 将Glide中的HTTP通讯组件替换成OkHttp。
* @author xiejinbo
* @date 2019/11/12 0012 11:47
*/
public class MyGlideModule implements GlideModule {
@Override
public void applyOptions(Context context, GlideBuilder builder) {
}
@Override
public void registerComponents(Context context, Glide glide) {
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.addInterceptor(new ProgressInterceptor());
OkHttpClient okHttpClient = builder.build();
glide.register(GlideUrl.class, InputStream.class, new OkHttpGlideUrlLoader.Factory(okHttpClient));
}
}
最后,为了让Glide能够识别我们自定义的MyGlideModule,还得在AndroidManifest.xml文件当中的application里面加入如下配置才行:
<meta-data
android:name="cn.xie.fingerprinttest.http.MyGlideModule"
android:value="GlideModule" />
现在Http组件以及被替换为了okhttp了,下面就是实现下载的监听了。
新建ProgressInterceptor类并实现Interceptor接口,代码如下所示:
package cn.xie.fingerprinttest.http;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
/**
* OkHttp中添加一个自定义的拦截器
* @author xiejinbo
* @date 2019/11/12 0012 11:49
*/
public class ProgressInterceptor implements Interceptor {
static final Map<String, ProgressListener> LISTENER_MAP = new HashMap<>();
public static void addListener(String url, ProgressListener listener) {
LISTENER_MAP.put(url, listener);
}
public static void removeListener(String url) {
LISTENER_MAP.remove(url);
}
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Response response = chain.proceed(request);
String url = request.url().toString();
ResponseBody body = response.body();
Response newResponse = response.newBuilder().body(new ProgressResponseBody(url, body)).build();
return newResponse;
}
}
现在自定义的拦截器已经启用了,接下来就可以开始去实现下载进度监听的具体逻辑了。首先新建一个ProgressListener接口,用于作为进度监听回调的工具,如下所示:
package cn.xie.fingerprinttest.http;
/**
* @author xiejinbo
* @date 2019/11/12 0012 13:06
*/
public interface ProgressListener {
void onProgress(int progress);
}
现在实现下载进度的具体计算。我们需要新建一个ProgressResponseBody类,并让它继承自OkHttp的ResponseBody,然后在这个类当中去编写具体的监听下载进度的逻辑,代码如下所示:
package cn.xie.fingerprinttest.http;
import android.util.Log;
import java.io.IOException;
import okhttp3.MediaType;
import okhttp3.ResponseBody;
import okio.Buffer;
import okio.BufferedSource;
import okio.ForwardingSource;
import okio.Okio;
import okio.Source;
/**
* 监听下载进度的逻辑
* @author xiejinbo
* @date 2019/11/12 0012 13:08
*/
public class ProgressResponseBody extends ResponseBody {
private static final String TAG = "ProgressResponseBody";
private BufferedSource bufferedSource;
private ResponseBody responseBody;
private ProgressListener listener;
public ProgressResponseBody(String url, ResponseBody responseBody) {
this.responseBody = responseBody;
listener = ProgressInterceptor.LISTENER_MAP.get(url);
}
@Override
public MediaType contentType() {
return responseBody.contentType();
}
@Override
public long contentLength() {
return responseBody.contentLength();
}
@Override
public BufferedSource source() {
if (bufferedSource == null) {
bufferedSource = Okio.buffer(new ProgressSource(responseBody.source()));
}
return bufferedSource;
}
private class ProgressSource extends ForwardingSource {
long totalBytesRead = 0;
int currentProgress;
ProgressSource(Source source) {
super(source);
}
@Override
public long read(Buffer sink, long byteCount) throws IOException {
long bytesRead = super.read(sink, byteCount);
long fullLength = responseBody.contentLength();
if (bytesRead == -1) {
totalBytesRead = fullLength;
} else {
totalBytesRead += bytesRead;
}
int progress = (int) (100f * totalBytesRead / fullLength);
Log.d(TAG, "download progress is " + progress);
if (listener != null && progress != currentProgress) {
listener.onProgress(progress);
}
if (listener != null && totalBytesRead == fullLength) {
listener = null;
}
currentProgress = progress;
return bytesRead;
}
}
}
截止目前为止,所有的轮子以及建造完毕,接下来就是在需要的地方之间调用了。
我们在MainActivity中添加了一个Button,在点击该Btn的时候开始加载图片。然后用一个ImageView来显示加载后的图片。
xml布局:
<Button
android:id="@+id/button3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onGlideLoadImage"
android:text="点击加载图片" />
<ImageView
android:id="@+id/show_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
在MainActivity中初始化加载进度条弹出框,此处的ProgressDialog是为了简便显示加载选用的,在最新的版本中已经弃用,大家可以选用目前的ProgressBar来展示加载进度,若是想要更多炫酷的加载效果,可以参考这里,现在回到正题。
private ProgressDialog progressDialog;
private ImageView loadImage;
loadImage = findViewById(R.id.show_image);
progressDialog = new ProgressDialog(this);
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressDialog.setMessage("加载中");
最后就是点击加载按钮开始加载了。
/**
* 点击图片加载按钮开始加载图片
* @param view
*/
public void onGlideLoadImage(View view) {
final String url = "http://image14.m1905.cn/uploadfile/2018/0907/thumb_1_1380_460_20180907013518839623.jpg";
ProgressInterceptor.addListener(url, new ProgressListener() {
@Override
public void onProgress(int progress) {
try {
//此处添加延迟是为了更好的展示加载的效果
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.e(TAG, "progress: " + progress);
progressDialog.setProgress(progress);
/*if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
progressBar.setProgress(progress, true);
} else {
progressBar.setProgress(progress);
}*/
}
});
Glide.with(this)
.load(url)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.override(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL)
.into(new GlideDrawableImageViewTarget(loadImage) {
@Override
public void onLoadStarted(Drawable placeholder) {
super.onLoadStarted(placeholder);
progressDialog.show();
//progressDialog.dismiss();
}
@Override
public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> animation) {
super.onResourceReady(resource, animation);
progressDialog.dismiss();
ProgressInterceptor.removeListener(url);
}
});
}
这是一个很好的轮子,前面的进度监控已经封装好了,我们直接调用即可。最后感谢郭霖大神,我完全是看郭霖大神的《第一行代码》进入到android世界的。
参考郭霖大神的:Android图片加载框架最全解析(七),实现带进度的Glide图片加载功能
本文介绍了如何在Android中通过扩展Glide框架,实现图片加载的进度条功能。通过替换Glide的HTTP组件为OkHttp,创建自定义的OkHttpFetcher和OkHttpGlideUrlLoader,以及实现ProgressInterceptor和ProgressResponseBody,最终达到监听并显示图片加载进度的效果。在MainActivity中展示了如何调用这些功能。

2078

被折叠的 条评论
为什么被折叠?



