我分为两部分基本上把最近Android网络请求最火的开源框架说完了。本篇就来讲一讲Android的图片的处理,在Android App上加载图片是很常见的,同时也是很容易造成OOM。
如果你对加载大图、图片缓存、OOM异常等问题不太了解,请先看看郭大神这篇文章,分析的很详细;当然,本篇主要是让你学会使用Picasso和Fresco这两个图片处理库,这两个库的使用方法在网上都很多,而且都非常简单,但是今天我会说点不一样的!
Picasso2 && OkHttp3
我在 第六篇 网络请求篇(上) 的时候为了说OkHttp3,就示例使用OkHttp3加载图片,其实那不是很好的加载图片的方法,Square有一款专门的图片加载工具Picasso:官网地址
Picasso的常规使用
在官网上可以看到,Picasso的使用方法很简单:
Picasso.with(context).load("path").into(imageView);
通过官网上面的解析,我们还可以了解到Picasso的其它的一些用法,比如:placeholder()、resize()、centerCrop()、error()等等;都可以很简单的实现。
同时,Picasso的内部会默认帮你实现了内存缓存,其大小为内存1/7左右,所以不做详述。因为今天我们要说的是本地缓存,如何用OkHttp3配合Picasso实现本地缓存呢?
Picasso的本地缓存
由于Picasso和OkHttp同属Square公司,所以,他们互相支持调用;
我在写这篇博客之前在google了很久,stackOverflow上面的用OkHttp写缓存都比较简单,也没有比较权威的说法。于是在github上面找到了Jake Wharton大神写的关于Picasso使用OkHttp3的配置,瞬间觉得这就是权威!看源码:
这里不把全部代码贴出来,部分解析:大家需要明确一点Downloader已经实现了DiskLruCache,我们现在需要的是配合OkHttp的网络请求来重写缓存。
public final class OkHttp3Downloader implements Downloader {
private static final String PICASSO_CACHE = "picasso-cache";
private static final int MIN_DISK_CACHE_SIZE = 5 * 1024 * 1024; // 5MB
private static final int MAX_DISK_CACHE_SIZE = 50 * 1024 * 1024; // 50MB
//新建一个默认的缓存文件夹
//可以在你手机中/android/data/yourapp/picasso-cache文件夹中可以找到
private static File createDefaultCacheDir(Context context) {
File cache = new
File(context.getApplicationContext().getCacheDir(), PICASSO_CACHE);
if (!cache.exists()) {
//noinspection ResultOfMethodCallIgnored
cache.mkdirs();
}
return cache;
}
//计算缓存文件的大小,
private static long calculateDiskCacheSize(File dir) {
long size = MIN_DISK_CACHE_SIZE;
try {
StatFs statFs = new StatFs(dir.getAbsolutePath());
long available = ((long) statFs.getBlockCount()) * statFs.getBlockSize();
// Target 2% of the total space.
size = available / 50;
} catch (IllegalArgumentException ignored) {}
// Bound inside min/max size for disk cache.
return Math.max(Math.min(size, MAX_DISK_CACHE_SIZE), MIN_DISK_CACHE_SIZE);
}
......
//网络请求时的本地缓存处理,如何没有网络且有缓存,则从缓存中读取;
@Override public Response load(Uri uri, int networkPolicy) throws IOException {
CacheControl cacheControl = null;
if (networkPolicy != 0) {
if (NetworkPolicy.isOfflineOnly(networkPolicy)) {
cacheControl = CacheControl.FORCE_CACHE;
} else {
CacheControl.Builder builder = new CacheControl.Builder();
if (!NetworkPolicy.shouldReadFromDiskCache(networkPolicy)) {
builder.noCache();
}
if (!NetworkPolicy.shouldWriteToDiskCache(networkPolicy)) {
builder.noStore();
}
cacheControl = builder.build();
}
}
Request.Builder builder = new Request.Builder().url(uri.toString());
if (cacheControl != null) {
builder.cacheControl(cacheControl);
}
okhttp3.Response response = client.newCall(builder.build()).execute();
int responseCode = response.code();
if (responseCode >= 300) {
response.body().close();
throw new ResponseException(responseCode + " " + response.message(), networkPolicy,responseCode);
}
boolean fromCache = response.cacheResponse() != null;
ResponseBody responseBody = response.body();
return new Response(responseBody.byteStream(), fromCache, responseBody.contentLength());
}
//关闭App时,如果有缓存,关闭DiskLruCache;
@Override public void shutdown() {
if (cache != null) {
try {
cache.close();
} catch (IOException ignored) {}
}
}
}
通过上面的代码已经构建出来一个OkHttp3downloader了,现在只需要将它加载到Picasso中去执行就好了;(注意:构建的picasso实例为一个单例,所以需要在Application中去创建这个实例,然后在调用的地方获取picasso单例即可);我是这样写的:
Picasso picasso = new Picasso.Builder(this)
.downloader(new OkHttp3Downloader(new OkHttpClient()))
.build();
Picasso.setSingletonInstance(picasso);
调用的时候这样写:
picasso.with(context).load("Path").into(imageView);
好了,Picasso的本地缓存说完了,接下来说一说Picasso的其它的使用方法:
说之前推荐一篇文章:使用Picasso加载带饼状进度条的图片。
Picasso的使用技巧
添加listener:当你的使用picasso加载图片失败的时候,可以通过listener把失败原因打印
Picasso picasso = new Picasso.Builder(this)
.listener(new Picasso.Listener() {
@Override
public void onImageLoadFailed(Picasso picasso, Uri uri, Exception exception) {
//图片加载失败时的操作
}
})
.loggingEnabled(true)// 打log
.indicatorsEnabled(true)// 分辨不同的路径的价值的图片;
.build();
回调:当图片加载成功和失败时的回调;
picasso.with(this)
.load("path")
.into(imageView, new Callback() {
@Override
public void onSuccess() { }
@Override
public void onError() { }
});
Picasso的相关内容就说完了,如果大家有问题请在评论中指出;
Fresco
Fresco是Facebook开发的一款强大的开源图片处理库,好处不多说,看官方文档,Facebook还是很贴心的,中文版也很清晰;
使用方法(官网代码):
//使用Facebook自定义的ImageView
//图片加载
Uri uri = Uri.parse("https://raw.githubusercontent.com/facebook/fresco/gh-pages/static/fresco-logo.png");
SimpleDraweeView draweeView = (SimpleDraweeView)findViewById(R.id.my_image_view);
draweeView.setImageURI(uri);
当然我们最关心还是缓存技术,在Fresco中实现了三层缓存技术来最大限度地节省空间和CPU时间:两级内存(内存缓存和本地缓存)和一级文件(Bitmap缓存),这里不需要像上面Picasso一样来自己使用OkHttp来做本地缓存了。
关于网络请求方面,Fresco也是支持自定义网络请求的,可以使用OkHttp,官网文档有介绍,不过我认为这里的OkHttp已经不是我们熟悉的Square的OkHttp了,Facebook应该为适配Fresco而做了一些改进。
使用Fresco在提升性能的同时牺牲了不少空间的,可以看看这篇文章:Fresco调研与性能测试。里面结合实际例子展现出来:Fresco占用的Native Heap很大,但是占用Java Heap很小:意思就是说Fresco更多的是使用了OS的资源,而不是使用我们程序所在进程中的资源;
Fresco要说的不多,但是它的使用方法非常多,我们以后想要将这个库应用到实际开发中,还是需要多上手实践一下;官网是最好的教程。
最后看一篇文章,带你看看Fresco有哪些不足之处:Fresco之强大之余的痛楚
Picasso? Fresco?
看了上面的介绍,你是否有疑惑?两个框架同样优秀,到底该选择哪一个使用呢?两者的区别可以在stackoverflow上面找;
下图是我分别使用Picasso和Fresco的例子,如果有兴趣的可以在Github上面把代码Clone下来看看效果:
Picasso&&Fresco.png
(我为什么使用Picasso?因为对Square的信仰!!!)
总结一下:当你的App有大量的图片出现、图片的分辨率非常大的时候,请使用Fresco,因为使用其它框架很容易造成OOM;为了性能而牺牲一些空间资源是值得的;而当App中图片较少、图片的质量不是很高的时候可以使用Picasso:Fresco资源占用太多就不值得了,这时Picasso就会有很大的优势;
当然还有其他的图片加载库,如Glide,使用方法和Picasso几乎一样,有兴趣可以了解一下。
以上就是本篇的全部内容,如果有问题,请在评论中指出!