Picaso完美兼容OkHttp3.3,缓存优化两不误 - Tamic Developer"s Blog

为何在Fresco,Glide这么强大的背景下,我又想起了当初的Picasso,又为何写这篇文章?是因为最近项目采用了square公司的RxAndroid,Retrfit和OKhttp, 不得不联想到这个公司曾经还有款图片加载Picasso,所以采用了square公司的全家桶来进行项目开发,为了减少开发成本和也防止Apk增大,毕竟一个公司的框架之前兼容性不用担心,那么请让我们回顾一下Picass之路

首先先让我们看看主流图片加载库

  • Picasso,Square公司的开源项目 ,和Square的网络库一起能发挥最大作用。占用内存小,自身不带缓存,需依赖OKhttps实现缓存,不支持gif图片
  • Fresco,FB的明星项目,也是2015最火的项目之一,匿名共享缓存等机制保证低端机表现极佳,但是源代码基于C/C++,阅读困难度提升。效率高,sdk库占用包体积比较大
  • Glide,Google员工私人项目,但是Google很多项目在用,占用内存小,减低oom更靠谱,相对Picasso在Gif方面有优势,并自带缓存功能!

我做了一个实验对比 用一个普通listview加载50张图片,并快速滑动列表,下面分别是glide和picasso消耗内存图

  • 分析后得出 一个占用内存大 一个占用cpu资源大, 这种区别是由于picasso只缓存一张大图,每次加载根据imagview的大小裁剪,因此消耗的cpu资源高,glide是分别存储不同尺寸的小图,每次不用计算,因此消耗内存比较多,加载速度相对Picasso也快,但也很耗流量.
  • 为了避免OOM, 我毫不犹豫选择了消耗内存较小的picasso, Fresco不用说都是加载速度第一的框架,采用c库 ,我没做集成测试,具体消耗多少cpu资源我无法给出数据,据说业界第一,但是对apk大小要求的项目很可能不太合适,这里对Apk包体积要求不高的项目,Fresco是优先的首选。

喜欢glide的朋友可以看看这篇文章 :http://mrfu.me/2016/02/27/Glide_Getting_Started/

实验测试并做了简单比较后,为何还要继续说Picasso,不是说他有多快多流畅,只是当你使用了square公司其他的开源项目,会发现他们都会依赖okhttp,okhttp的强大不言而喻,一个网络库可以无缝隙的对接Retrofit和Picasso.今天只介绍piacsso相关的,说说picasso(官方:https://github.com/square/picasso) 的一些常用技巧!



#使用方式:

配置gradle

dependencies {
c
compile 'com.squareup.picasso:picasso:2.5.2'
compile 'com.squareup.okhttp3:okhttp:3.3.1'
compile 'com.squareup.okhttp3:logging-interceptor:3.3.1'
}

据说目前的2.5.3已修复了2.52无法兼容okhttp3的问题,但我还是选择了2.52版本。

基本加载用法

Picasso.with(getApplication())
.load(url)
.into(imageView);

以上用法很简单,加载图片时提供url插入到imageview即可,picasso其他强大功还没有太多的理解的同学请Follow Me!

#裁剪图片

1
Picasso.with(getApplication()).resize(width, height);

这句方法会出现bug,误用!

请用Transformation来进行转义实现:

1

2

3

4

Picasso.with(getApplication())

.load(url)

.transform(new PaTransformation(width, height)).into(imageView);

Transformation可以拦截到picasoo返回的bitmap,拿着bitmap随心所欲!

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35
   public class TamicTransformation implements Transformation {

private int width;

private int height;

private String key;

public PaTransformation(int width, int height) {

this(width, height,  width + "*" + height);

}

public PaTransformation(int width, int height, String key) {

this.width = width;

this.height = height;

this.key = key;

}

@Override

public Bitmap transform(Bitmap source) {

略 拿着source进行裁剪缩放即可

if (result != source) {

source.recycle();

}

return result;

}

@Override

public String key() {

return key;

}

}

列如处理圆形头像

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

public class CircleTransformation implements Transformation {   

private static final int STROKE_WIDTH = 5;  

@Override   

public Bitmap transform(Bitmap source) {   

int size = Math.min(source.getWidth(), source.getHeight());   

int x = (source.getWidth() - size) / 2; 

int y = (source.getHeight() - size) / 2;  

Bitmap squaredBitmap = Bitmap.createBitmap(source, x, y, size, size);  

if (squaredBitmap != source) { 

source.recycle();   

} 

Bitmap bitmap = Bitmap.createBitmap(size, size,source.getConfig()); 

Canvas canvas = new Canvas(bitmap); 

Paint avatarPaint = new Paint();  

BitmapShader shader = new BitmapShader(squaredBitmap, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP);avatarPaint.setShader(shader);  

Paint outlinePaint = new Paint();

outlinePaint.setColor(Color.WHITE);

outlinePaint.setStyle(Paint.Style.STROKE);

outlinePaint.setStrokeWidth(STROKE_WIDTH);

outlinePaint.setAntiAlias(true);

float r = size / 2f;  

canvas.drawCircle(r, r, r, avatarPaint); 

canvas.drawCircle(r, r, r - STROKE_WIDTH / 2, outlinePaint);

squaredBitmap.recycle();

return bitmap;  

}

@Override  

public String key() { 

return "circle)";

}

}

接着设置渲染模式

1

2

Picasso.with(getApplication()) .fit().centerCrop()

清空缓存

新的版本2.52 已经无法直接拿到之前的cache,因此可以用Picasso.invalidate()的实现清楚缓存!

以前我们可以这样

1

2

Clear.clearCache(Picasso.with(context));

但现在 不行了

稍加封装成了这样子:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

void clearCache(Uri uri, File file, String path) {

if (!TextUtils.isEmpty(uri.toString())) {

mPicasso.invalidate(uri);

return;

}

if (!NullUtils.isNull(file)) {

mPicasso.invalidate(file);

return;

}

if (!TextUtils.isEmpty(path)) {

mPicasso.invalidate(path);

}

}

当然也可以这样!

1

2

Picasso.with(getContext()).load(Url).memoryPolicy(MemoryPolicy.NO_CACHE).into(image);

在加载图片时直接不让做缓存!

加入缓存

当然2.5.2没做对oKhttp3.3的兼容,因此我们加入自定义的cilent,对okhttp做下缓存定制,请照着下面姿势作

构建OkHttpClient

1

2

3

4

5

6

7
// creat the OkHttpClient.

OkHttpClient client =new OkHttpClient

.Builder()

.cache(new Cache("你的缓存路径", 1000*1024))

.addInterceptor(new CaheInterceptor(context, null))

.addNetworkInterceptor(new CaheInterceptor(context, null))

.build();

拦截器Interceptor

拦截器大家都不陌生,尤其是玩过okhttp和retofit的朋友,那肯定是拦截http的拦截请求和响应的.

public class CaheInterceptor implements Interceptor {

private Context context;
public CaheInterceptor(@NonNull Context context) {
    this.context = context;
}

@Override
public Response intercept(Chain chain) throws IOException {
    Request request = chain.request();
    if (NetworkUtil.isNetworkAvailable(context)) {
        Response response = chain.proceed(request);
        // read from cache for 60 s
        int maxAge = 300;
        String cacheControl = request.cacheControl().toString();
        Log.e("Tamic", maxAge+ "s load cahe:" + cacheControl);
        return response.newBuilder()
                .removeHeader("Pragma")
                .removeHeader("Cache-Control")
                .header("Cache-Control", "public, max-age=" + maxAge)
                .build();
    } else {
        Log.e("Tamic", " no network load cahe");
        request = request.newBuilder()
                .cacheControl(CacheControl.FORCE_CACHE)
                .build();
        Response response = chain.proceed(request);
        //set cahe times is 3 days
        int maxStale = 60 * 60 * 24 * 3;
        return response.newBuilder()
                .removeHeader("Pragma")
                .removeHeader("Cache-Control")
                .header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)
                .build();
    }
}
 }

添加到Picasso中

1

2

3

4

5
    // Generate the global default Picasso instance.

Picasso   mPicasso = getPicasso(context, null);

mPicasso.setLoggingEnabled(true);

}

自定义DownLoader

为了兼容okhttp3.31 实现下载器!

public class ImageDownLoader implements Downloader {
 OkHttpClient client = null;

public ImageDownLoader(OkHttpClient client) {
    this.client = client;
}

@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  大专栏  Picaso完美兼容OkHttp3.3,缓存优化两不误 - Tamic Developer"s Blognew ResponseException(responseCode + " " + response.message(), networkPolicy,
                responseCode);
    }

    boolean fromCache = response.cacheResponse() != null;

    ResponseBody responseBody = response.body();
    return new Response(responseBody.byteStream(), fromCache, responseBody.contentLength());

}

@Override
public void shutdown() {

    Cache cache = client.cache();
    if (cache != null) {
        try {
            cache.close();
        } catch (IOException ignored) {
        }
    }
 }
}

接着将ImageDownLoader 加入到Picasso

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19
    /**

* Download Big Image only, Not singleton but shared cache

*/

public Picasso getPicasso(Context context) {

OkHttpClient client = getProgressBarClient();

return new Picasso.Builder(context)

.downloader(new ImageDownLoader(client))

.build();

}

/**

* Not singleton

*/

private  OkHttpClient getProgressBarClient() {

return client.newBuilder()

.addInterceptor(new CaheInterceptor(context))

.addNetworkInterceptor(new CaheInterceptor(contextr))

.build();

}

这样我们在做图片加载时 就可以:

1

2

(context) .load(Url).into(image)

因此用了Picasso我们可以直接将缓存策略用到retrofit上去,其实一箭双雕,大大简化了开发成本!

#如何支持Https

姿势很简单 利用上面构建好的downloader, 设置OkHttpprotocols即可,并构建ssl。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

OkHttpClient client = new OkHttpClient();

client.setHostnameVerifier(new HostnameVerifier() {

public boolean verify(String s, SSLSession sslSession) {

return true;

}

});

TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {

public void checkClientTrusted(

java.security.cert.X509Certificate[] x509Certificates,

String s) throws java.security.cert.CertificateException {

}

public void checkServerTrusted(

java.security.cert.X509Certificate[] x509Certificates,

String s) throws java.security.cert.CertificateException {

}

public java.security.cert.X509Certificate[] getAcceptedIssuers() {

return new java.security.cert.X509Certificate[] {};

}

} };

try {

SSLContext sc = SSLContext.getInstance("TLS");

sc.init(null, trustAllCerts, new java.security.SecureRandom());

client.setSslSocketFactory(sc.getSocketFactory());

} catch (Exception e) {

e.printStackTrace();

}

clent.protocols(Collections.singletonList(Protocol.HTTP_1_1))

.build();

final Picasso picasso = new Picasso.Builder(this)

.downloader(new ImageDownloader(client))

.build();

Picasso.setSingletonInstance(picasso);

优化相关

优化不缓存策略

1

2

3

4
   public RequestCreator skipMemoryCache(RequestCreator requestCreator) {

return requestCreator.memoryPolicy(MemoryPolicy.NO_STORE, MemoryPolicy.NO_CACHE)

.networkPolicy(NetworkPolicy.NO_STORE, NetworkPolicy.NO_CACHE);

}

降低内存消耗
设置RGB_565编码格式,降低内存消耗

1

2

3

4

public RequestCreator cutDownMemory(RequestCreator requestCreator) {

return requestCreator.config(Bitmap.Config.RGB_565);

}

取消加载

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27
public class TamicImageView extends ImageView  {

public TamicImageView(Context context) {

this(context, null, 0);

}

public TamicImageView(Context context, AttributeSet attrs) {

this(context, attrs, 0);

}

public TamicImageView(Context context, AttributeSet attrs, int defStyleAttr) {

super(context, attrs, defStyleAttr);

}

protected void onDetachedFromWindow() {

super.onDetachedFromWindow();

// 不可见时释放Bitmap

setImageDrawable(null);

// 暂停加载

mPicasso.pauseTag(this);

}

}

还有很多api,比如:

  • requestCreator.tag(tag);设置key
  • requestCreator.error(); 设置加载失败图片
  • mPicasso.pauseTag(); 暂停加载
  • mPicasso.resumeTag();恢复加载
  • mPicasso.cancelRequest();取消加载
  • requestCreator.priority()优先级
  • requestCreator..rotate() 旋转之类

下面说几个常用的api

#扩展加载

当然还有一个对通知栏加载的api

通知栏支持

1

2

into(RemoteViews remoteViews, int viewId, int notificationId,Notification notification)

widget支持

1
into(RemoteViews remoteViews, int viewId, int[] appWidgetIds)

第一个是远程视图,第二个view Id 第三个是widget的id数组

预加载

有返回值

1

2

Picasso.with(context).load(url).get()

此api可以预先加载图片到disk和内存中,并有返回值Bitmap,此api必须同步调用,不能用UI主线程去调用,通常我们可以用在viewpager中预加载后面index的图片,或者提前拿到目标bitmap来进行业务操作,或者一些效果处理。

无返回值

1

2

Picasso.with(context).load(url).fetch()

也有预加载图片功能,此api可以在主线程调用,主要有callback实现,提供失败和成功函数供上层调用。但无法获取的加载好的图片资源。

1

2

3

4

5

6

7

8

9

10

public static class EmptyCallback implements Callback {

@Override public void onSuccess() {

}

@Override public void onError() {

}

}

}

#取消加载

  • cancelRequest(ImageView imageView)
  • cancelTag(Object obj)
  • cancelRequest(Target)

主要以后上面的三种方式,第一个不明思议,就是取消某个view的加载请求,通常我们在activity死亡时候调用,第三个方法方法是我们取消某个指定的加载action, 譬如一次加载中设置了picasso的 Picasso.with(context).tag()时,就可以用cancelTag(”tag”)取消指定的请求,那么最后一个又是什么,他需要我们加入Tag的包装类 Target来进行回调请求处理。方便开发者上层对取消流程的控制。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16
 mPicasso.cancelRequest(new Target() {

@Override

public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom afrom) {

}

@Override

public void onBitmapFailed(Drawable errorDrawable) {

}

@Override

public void onPrepareLoad(Drawable placeHolderDrawable) {

}

});

另外还有一个对通知栏图片的取消的接口

1

2

3

cancelRequest(RemoteViews remoteViews, int viewId)

通知栏的VIEW大家都非常熟悉,都是用RemoteViews 来进行转换展现的,那么在通知被cancel时我们就可以直接调用这个取消的方法

后记

总之虽然picasso 并不是最快的图片加载框架,但是他在基本的加载本地和网络图片基础上,还能很好的提供了让我们自我扩展能力,其扩展性和适应性更强,相信你结合了ohttp+ rxJava + Picasso 后你会发现他确实适合你!
如果你爱好glide请看这篇完美的文章:glide系列教程)



原文地址:https://www.cnblogs.com/lijianming180/p/12262458.html

时间: 2024-10-27 10:32:04

Picaso完美兼容OkHttp3.3,缓存优化两不误 - Tamic Developer"s Blog的相关文章

PHP服务缓存优化之ZendOpcache、xcache、eAccelerator

PHP服务缓存优化原理 Nginx 根据扩展名或者过滤规则将PHP程序请求传递给解析PHP的FCGI,也就是php-fpm进程 缓存操作码(opcode) Opcode,PHP编译后的中间文件,缓存给用户访问 当客户端请求一个PHP程序时,服务器的PHP引擎会解析该PHP程序,并将其编译为特定的操作码文件,该文件是执行PHP代码后的一种二进制文件表现形式.默认情况下,这个编译好的操作码文件由PHP引擎执行后丢弃:而操作码缓存的原理就是将编译后的操作码保存下来,并放入到共享内存里,以便再下一次调用

hdu - 4920 - Matrix multiplication(缓存优化+开挂)

题意:求两个n x n的矩阵相乘后模3的结果,n <= 800. 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4920 -->>呀呀.. 1.3层计算的for进行缓存优化,根据CPU的L1级缓存的实现原理,减少缓存的变更.如果每次都计算完一个单元格的结果再计算下一个单元格的结果,那么被乘矩阵的访问就会频繁地更新缓存,使效率很低.. 2.输入开挂,G++提效500ms+.. 3.对乘法进行剪枝.. 没有第1个操作,后果是严重的.. n^3的

LDAP索引及缓存优化

一.设置索引 索引将查找信息和 Directory Server 条目关联起来. Directory Server支持以下几种索引: 1出现索引 (pres) - 列出了具有特定属性的条目,与属性的值无关. 2等式索引 (eq) - 使您能够高效地搜索包含特定属性值的条目. 3近似索引 (approx) - 通过使用 ~= 过滤运算符提供了高效的"音似"搜索功能. 例如,近似索引对于搜索部分名称或拼错的名称很有用.Directory Server 使用 变音位语音算法的一个变体来执行近

华硕200系主板完美兼容M.2安装Win7系统

虽然Windows 10系统的装机率正不断攀升,但经典的Windows 7依然有着大量的用户群体.特别是在我们中国, Windows 7依然是许许多多电脑用户的装机首选系统. 经久不衰的Windows 7 可是,自从Skylake架构处理器时代开始,用户发现在新电脑安装Windows 7的过程中会遇到很多问题.其实,这种原因主要是原生的Windows 7缺少某些必要的驱动程序所导致的: 上一代Skylake平台要想安装Windows 7,需要在Windows 7镜像系统中嵌入USB驱动!最新一代

OutputCache缓存优化asp.net代码 提高网页性能

对于asp.net编写的网页来说,使用缓存是一种非常重要也是很常用的优化技术,它可以大大减轻服务器的负载压力,优化这些网页的性能,在网与使用 .NET Framework 的任何其他功能相比,适当地使用缓存可以更好地提高站点的性能.同时,在网页加速显示上也起了很大的作用. OutputCache以声明的方式控制 ASP.NET 页或页中包含的用户控件的输出缓存策略. 语法: <%@ OutputCache Duration="#ofseconds"   Location=&quo

Web Uploader - 功能齐全,完美兼容 IE 的上传组件

文件上传是网站和 Web 应用程序的常用功能,一直没有一款完美的文件上传组件,因此让很多开发人员碰到头疼的浏览器兼容问题. WebUploader 是由 Baidu FEX 团队开发的一款以 HTML5 为主,FLASH 为辅的现代文件上传组件.在现代的浏览器里面能充分发挥 HTML5 的优势,同时又不摒弃主流IE浏览器,沿用原来的 FLASH 运行时,兼容 IE6+,iOS 6+, android 4+.采用大文件分片并发上传,极大的提高了文件上传效率. 源码下载      在线演示 特性 分

[转]CSS完美兼容IE6/IE7/IE8/IE9/IE10的通用方法

CSS完美兼容IE6/IE7/IE8/IE9/IE10的通用方法 2013-12-12  By 奥夫 关于CSS对各个浏览器兼容已经是老生常谈的问题了, 网络上的教程遍地都是.以下内容没有太多新颖, 纯属个人总结, 希望能对初学者有一定的帮助. 一.CSS HACK 以下两种方法几乎能解决现今所有HACK. 1, !important 随着IE7对!important的支持, !important 方法现在只针对… 一.CSS HACK 以下两种方法几乎能解决现今所有HACK. 1, !impo

即构科技在线抓娃娃H5方案正式上线,400ms超低延迟,完美兼容微信

近几个月,在线抓娃娃热席卷全国,除了原生app抓娃娃方案以外,H5在线抓娃娃方案也得到了不少创业者的青睐. H5在线抓娃娃方案独特的优势 1.开发成本低和开发周期短.H5页面的功能大都比较轻量.简易,开发起来非常便捷. 2.兼容性强,在微信和所有浏览器上都能用,开发一次所有的浏览器上都可以跑. 3.传播途径广.用户使用成本低. H5可以通过各种社交产品实现病毒式扩散,用户只需简单的打开链接即可达到宣传推广目的,特别是在微信公众号和朋友权重进行传播. 4.迭代速度快.相比原生APP, 迭代升级快,

input输入框的border-radius属性在IE8下的完美兼容

在工作中我们发现搜索框大部分都是有圆角的,为此作为经验不足的前端人员很容易就想到,给input标签添加border-radius属性不就解决了嘛.不错方法确实是这样,但是不要忘了border-radius是css3属性,IE8不兼容!!!那么问题来了,怎样在IE8下完美兼容呢? 网上有很多关于css3属性在IE8下兼容的方法,需要引入PIE.htc这个文件,相信很容易搜到并下载该文件,但是用上position:relative;behavoior:url(PIE.htc);后发现圆角是实现了,但