Android开发之图片加载缓存框架Picasso的领教


Picasso是Square公司开源的一个Android图形缓存库,官方网址:http://square.github.io/picasso/
Picasso实现了图片的异步加载,并解决了Android中加载图片时常见的一些问题,它有以下特点:
  • 在Adapter中取消了不在视图范围内的ImageView的资源加载,因为可能会产生图片错位;
  • 使用复杂的图片转换技术降低内存的使用。
  • 自带内存和硬盘的二级缓存机制。
  • 内部也是集成LRU(近期最少使用)算法。
Picasso优先判断是否安装有OkHttpClient网络引擎,没有的话则采用常规的HttpUrlConnection进行网络请求,
static Downloader createDefaultDownloader(Context context) {
    try {
      Class.forName("com.squareup.okhttp.OkHttpClient");
      return OkHttpLoaderCreator.create(context);
    } catch (ClassNotFoundException ignored) {
    }
    return new UrlConnectionDownloader(context);
  }
Picasso默认将图片存到/data/data/<application package>/cache/picasso-cache内部存储空间里,开辟的空间大小最小是5兆最大是50兆,同时采用Lru算法来进行移除不常用的图片减少内存空间占有提高利用率。加载过程中默认开启三个线程进行本地与网络之间的访问。加载图片的顺序是 内存->本地->网络。

将Picasso添加到项目中:
目前Picasso的最新版本是2.5.2,你可以下载对应的Jar包,将Jar包添加到你的项目中,或者在build.gradle配置文件中加入
compile 'com.squareup.picasso:picasso:2.5.2'
注意如果你开启了混淆,你需要将以下代码添加到混淆规则文件中:
-dontwarn com.squareup.okhttp.**

基本使用方法
Picasso.with(context).load("http://i.imgur.com/DvpvklR.png").into(imageView);
Picasso.with(context).load(R.drawable.landing_screen).into(imageView1);
Picasso.with(context).load("file:///android_asset/DvpvklR.png").into(imageView2);
Picasso.with(context).load(new File(...)).into(imageView3);

高级使用方法
1.添加加载中占位图和加载失败占位图
Picasso .with(context)
               .load(imageUrl)
               .placeholder(R.mipmap.ic_launcher)              //添加占位图片
               .error(R.mipmap.ic_launcher)                          //加载失败显示的图片
               .into(imageView);
也可判断加载结果是否成功:
Picasso.with(this).load(userPhotoUrl).into(user_photo, new Callback() {
@Override
public void onSuccess() {
        //加载成功
    }

@Override
public void onError() {
        //加载失败
    }
});


2.调用.noFade()Picasso的默认图片加载方式有一个淡入的效果,如果调用了noFade(),加载的图片将直接显示在ImageView上

Picasso .with(context)
               .load(imageUrl)
               .placeholder(R.mipmap.ic_launcher)              //添加占位图片
               .error(R.mipmap.ic_launcher)                          //加载失败显示的图片
               .noFade()                                                          // 去除淡入效果
               .into(imageView);


3.调用.noPlaceholder()。有一个场景,当你从网上加载了一张图片到Imageview上,过了一段时间,想在同一个ImageView上展示另一张图片,这个时候你就会去调用Picasso,进行二次请求,这时Picasso就会把之前的图片进行清除,可能展示的是.placeholder()的图片,如果你没有设置占位图,则请求的过程中将会是一片空白,给用户并不是很好的体验,如果调用了noPlaceholder(),就不会出现这种情况.

Picasso .with(context)
               .load(imageUrl)
               .noPlaceholder(R.mipmap.ic_launcher)              //去除占位图
               .into(imageView);

4.调用resize(x, y)。自定义图片的加载大小
Picasso .with(context)
               .load(imageUrl)
               .resize(600,200)             //自定义图片的加载大小
               .into(imageView);


5.调用onlyScaleDown()。一般.resize(x,y)会将加载的图片的重新计算然后按照设定展示出来。如果我们加载的图片的尺寸比.resize(x,y)的尺寸还大,此时调用.onlyScaleDown()将会直接将图片按照.resize(x,y)的尺寸展示出来,效率提高了图片的加载时间。

Picasso .with(context)
               .load(imageUrl)
               .resize(600,200)             //自定义图片的加载大小
               .onlyScaleDown()         //效率提高图片的加载时间配合resize(x,y)使用。           
               .into(imageView);


6.针对拉伸图片的处理。Picasso给我们提供了两种进行图片拉伸的展示方式,视情况选择,避免图片拉伸变形难看。

centerCrop() - 图片会根据最短边进行多余剪切,但是图片质量看着没有什么区别。

Inside()- 图片会被完整的展示,图片过小使图片不会填充满ImageView`,图片过大则会硬填充到view中,图片此时出现变形。
Picasso .with(context)
               .load(imageUrl)
               .resize(600,200)             //自定义图片的加载大小
               .centerCrop()               //填充居中裁剪显示
               .into(imageView);


7.调用.fit()。Picasso会对图片的大小及ImageView进行测量,计算出最佳的大小及最佳的图片质量来进行图片展示,减少内存,并对视图没有影响;

Picasso .with(context)
               .load(imageUrl)
               .fit()
               .into(imageView);


8.调用.priority()。设定图片加载的优先级。通常情况我们进行网络加载图片时一般是哪个图片小就先把那个图片先加载出来。有些时候我们想让图片按照某个顺序或者等级加载图片,Picasso为我们提供了三种图片加载的优先级:HIGH, MEDIUM,  LOW。所有的加载默认优先级为MEDIUM。

Picasso .with(context)
               .load(imageUrl)
               .fit()
               .Priority(Picasso.Priority.HIGH)    //设定图片加载的优先级
               .into(imageView);


9.调用.tag()。控制图片的加载暂停或取消。

Picasso提供了三种设置Tag的方式
暂停标记 pauseTag()
可见标记 resumeTag()
取消标记 cancleTag()
Picasso .with(context)
               .load(imageUrl)
               .tag("PICASSO_TAG")
               .into(imageView);
针对listView(),有些时候我们想在滑动的时候不加载图片,滑动结束的时候在进行加载我们可以重写下边的这个方法。
Picasso .with(context)
               .load(imageUrl)
               .tag("PICASSO_TAG")
               .into(imageView);
有些时候我们针对当前的请求需要中断:
Picasso .with(context)
              .cancelTag("PICASSO_TAG");

10.指定Target。我们之前调用.into()方法,只是将获取到的资源加载到ImageView中,但我们还可以将资源作为回调放到Target中。但是这里的target不能作为匿名内部类直接引用,即不能在.into()里直接new出来,因为垃圾回收机制在你获取不到Bitmap的时候会把对象回收。
Picasso下载图片
private void download() {
        //获得图片的地址
        String url = mList.get(mPosition);
        //Target
        Target target = new Target(){
            @Override
            public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
                String imageName = System.currentTimeMillis() + ".png";
                File dcimFile = FileUtil
                        .getDCIMFile(FileUtil.PATH_PHOTOGRAPH,imageName);        
                FileOutputStream ostream = null;
                try {
                    ostream = new FileOutputStream(dcimFile);
                    bitmap.compress(Bitmap.CompressFormat.PNG, 100, ostream);
                    ostream.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                Toast.makeText(PicActivity.this,"图片下载至:"+dcimFile,Toast.LENGTH_SHORT).show();
            }
            @Override
            public void onBitmapFailed(Drawable errorDrawable) {
            }
            @Override
            public void onPrepareLoad(Drawable placeHolderDrawable) {
            }
        };
        //Picasso下载
        Picasso.with(this).load(url).into(target);
    }

11.自定义通知栏采用Picasso加载图片。
private void testRemoteView() {
    RemoteViews remoteViews = new RemoteViews(getPackageName(), R.layout.item_picasso);
    remoteViews.setImageViewResource(R.id.iv_remoteview, R.mipmap.abc);

    remoteViews.setTextViewText(R.id.tv_title, "This Title");
    remoteViews.setTextViewText(R.id.tv_desc, "This desc");

    remoteViews.setTextColor(R.id.tv_title, getResources().getColor(android.R.color.black));
    remoteViews.setTextColor(R.id.tv_desc, getResources().getColor(android.R.color.holo_blue_bright));

    NotificationCompat.Builder builder = new NotificationCompat.Builder(MainActivity.this)
            .setSmallIcon(R.mipmap.notifation)
            .setContentTitle("Context Title")
            .setContentText("Content Text")
            .setContent(remoteViews)
            .setPriority(NotificationCompat.PRIORITY_MIN);
    Notification notification = builder.build();
if (Build.VERSION.SDK_INT > 16) {
        notification.bigContentView = remoteViews;
    }
    NotificationManager mNotificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
    mNotificationManager.notify(NOTIFICATION_ID, notification);

    Picasso.with(MainActivity.this)
            .load("http://www.jycoder.com/json/Image/3.jpg")
            .into(remoteViews, R.id.iv_remoteview, NOTIFICATION_ID, notification);
}


12.图片旋转

a.根据自身中心旋转:
Picasso .with(context)
        .load(imageUrl)
        .rotate(90f) //旋转90度
.into(imageView);
b.根据某个点旋转:
Picasso .with(context)
        .load(imageUrl)
        .rotate(45f, 200f, 100f)    //以中心点(200,100)旋转45度。
.into(imageView);


13.Picasso对图片的转化(Transformation扩展)。

先了解一下Transformation这个类,这是一个接口,里面有两个方法,其中transForm这个方法最重要,负责对Bitmap的转换操作。
publicinterface Transformation {
    //对Bitmap进行转换操作
  Bitmap transform(Bitmap source);
    //一个key  主要是缓存的key的生成和它有关
  String key();
}

a.将加载完成的图片做成圆形:

public class CircleTransform implements Transformation {
@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;

//创建一个正方形区域的Btimap
Bitmap squaredBitmap=Bitmap.createBitmap(source,x,y,size,size);

if(squaredBitmap!=source){
            source.recycle();
        }

//创建一张可以操作的正方形图片的位图
Bitmap bitmap=Bitmap.createBitmap(size,size,source.getConfig());

//创建一个画布Canvas
Canvas canvas=new Canvas(bitmap);
//创建画笔
Paint paint=new Paint();

        BitmapShader shader=new BitmapShader(squaredBitmap,BitmapShader.TileMode.CLAMP,BitmapShader.TileMode.CLAMP);
        paint.setShader(shader);
        paint.setAntiAlias(true);

//圆形半径
float r=size/2f;
        canvas.drawCircle(r,r,r,paint);
        squaredBitmap.recycle();
return bitmap;
    }

@Override
public String key() {
return "circle";
    }
}
调用:
Picasso.with(context)
        .load(UsageExampleListViewAdapter.eatFoodyImages[0])
        .transform(new CircleTransform())
        .into(imageViewTransformationBlur);
b.模糊一张图片
public class BlurTransformation implements Transformation {

    RenderScript rs;

public BlurTransformation(Context context) {
super();
rs = RenderScript.create(context);
    }

@Override
public Bitmap transform(Bitmap bitmap) {
// 创建一个Bitmap作为最后处理的效果Bitmap
Bitmap blurredBitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true);

// 分配内存
Allocation input = Allocation.createFromBitmap(rs, blurredBitmap, Allocation.MipmapControl.MIPMAP_FULL, Allocation.USAGE_SHARED);
        Allocation output = Allocation.createTyped(rs, input.getType());

// 根据我们想使用的配置加载一个实例
ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
        script.setInput(input);

// 设置模糊半径
script.setRadius(10);

//开始操作
script.forEach(output);

// 将结果copy到blurredBitmap中
output.copyTo(blurredBitmap);

//释放资源
bitmap.recycle();

return blurredBitmap;
    }

@Override
public String key() {
return "blur";
    }
}
 
  
调用:
Picasso.with(context)
        .load(UsageExampleListViewAdapter.eatFoodyImages[0])
        .transform(new BlurTransformation(Context))
        .into(imageViewTransformationBlur);
c.实现圆形模糊图片:
Picasso.with(context)
        .load(UsageExampleListViewAdapter.eatFoodyImages[0])
        .transform(new BlurTransformation(Context))
        .into(imageViewTransformationBlur);

14.调用.memoryPolicy()。

memoryPolicy主要用于Picasso对内存的管理。有两个枚举类:
NO_CACHE - 让Picasso跳过从内存中读取图片这一操作
NO_STORE - 如果你的图片只加载一次就没用了,就调用该值,这样的话Picasso就不会在内存及本地进行缓存了。
Picasso  
    .with(context)
    .load(url)
    .memoryPolicy(MemoryPolicy.NO_CACHE)
    .into(imageViewFromDisk);
Picasso  
    .with(context)
    .load(url)
    .memoryPolicy(MemoryPolicy.NO_CACHE,MemoryPolicy.NO_STORE)
    .into(imageViewFromDisk);


15.调用.networkPolicy()。

networkPolicy和memoryPolicy类似,前者是对内存管理后者是对存储空间管理。有三个枚举类:
NO_CACHE - 让Picasso跳过从本地读取资源这一过程
NO_STORE - 让Picasso不进行本地图片缓存
OFFLINE - 让Picasso加载图片的时候只从本地读取除非联网正常并且本地找不到资源的情况下进行网络请求。
Picasso  
    .with(context)
    .load(UsageExampleListViewAdapter.eatFoodyImages[2])
    .networkPolicy(NetworkPolicy.NO_CACHE)
    .into(imageViewFromNetwork);
或配合memoryPolicy一起使用:
Picasso  
    .with(context)
    .load(UsageExampleListViewAdapter.eatFoodyImages[2])
    .memoryPolicy(MemoryPolicy.NO_CACHE, MemoryPolicy.NO_STORE)
    .networkPolicy(NetworkPolicy.NO_CACHE)
    .into(imageViewFromNetwork); 


16.调用.setIndicatorsEnabled(true),查看图片来源。

兴许加载图片的时候我们不知道加载的图片是从内存加载出来还是从本地加载出来的还是直接从网络上加载出来的,这时候我们调用.setIndicatorsEnabled(true)会在图片的左上角出现一个有颜色的小三角帮助我们区分,总共有三种颜色:
蓝色 - 从内存中获取,是最佳性能展示
绿色 - 从本地获取,性能一般
红色 - 从网络加载,性能最差
Picasso  
    .with(context)
    .setIndicatorsEnabled(true);
然后在进行加载图片就可以看到每张图片的来源了
Picasso  
    .with(context)
    .load(url)
    .into(imageViewFromDisk);

17.调用.setLoggingEnabled(true)。用来查看每张图片加载出来的用时多少,日志在控制台里打出来。

Picasso  
    .with(context)
    .setLoggingEngabled(true);
然后再进行加载图片就可以看到每张图片的日志了
Picasso  
    .with(context)
    .load(url)
    .into(imageViewFromDisk);


18.整体加载分析StatsSnapshot。有些时候我们想看我们加载了这么多张图片后图片内存占用多少,可以采用StatsSnapshot.

StatsSnapshot picassoStats = Picasso.with(context).getSnapshot();

然后打印日志:
Log.d("Picasso Stats", picassoStats.toString());




猜你喜欢

转载自blog.csdn.net/li0978/article/details/52240574