介绍
picasso是Square公司开源的一个Android图形缓存库,地址:http://square.github.io/picasso/ 它不仅满足加载图片的基本需求,而且扩展性也很强
版本和大小
目前最新版本为:2.5.2,对应jar包名称:picasso-2.5.2.jar
jar包大小124K
Picasso功能
1.加载速度快
2.资源消耗低
3.保证加载图片时不错位
4.加载图片类型丰富,支持网络图片以及各种本地图片如Asserts目录下,内容提供者提供的图片资源路径
Picasso策略
1.加载速度快
标准的二级缓存(内存缓存+磁盘缓存)+Net
a.标配策略,MemoryCache+DiskCache+Net。提高加载速度,同时保证流量。
b.Net部分,兼顾单请求加载速度与多请求并发能力,从而提高整体加载速度。
c.MemoryCache部分,通过Lru策略提高缓存效率。
2.资源消耗低
a.渲染适当尺寸图片来减少内存。
b.通过线程池来限制并发的图片加载线程,降低资源消耗。
c.请求相同图片的线程要合并,减少线程数。
3.加载图片类型丰富
Picasso内置针对不同的图片资源类型的各种handler 例如:网络下载图片:NetworkRequestHandler,Asserts目录下的图片资源:AssetRequestHandler
4.保证加载图片时不错位
Picasso维护了Map,Key为ImageView,Value为Action,每个ImageView均只对应一个Action。若获取的图片Action与ImageView不符合,则丢弃,等待正确的Action执行完。
Picasso所需要的权限
如下:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
picasso嵌入工程方式
本地依赖
1.将picasso-2.5.2.jar拷贝到工程libs目录中
2.改写工程build.gradle如下
apply plugin: 'com.android.application'
android {
compileSdkVersion 23
buildToolsVersion "23.0.1"
defaultConfig {
applicationId "com.meizu.analysepicasso"
minSdkVersion 15
targetSdkVersion 23
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
repositories {
flatDir {
dirs 'src/main/libs'
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.1.1'
compile files('src/main/libs/picasso-2.5.2.jar')
}
远程依赖
1.直接依赖远程仓库的picasso-2.5.2.jar,下载好的文件缓存在~/.gradle/caches目录中
2.改写工程build.gradle如下
apply plugin: 'com.android.application'
android {
compileSdkVersion 23
buildToolsVersion "23.0.1"
defaultConfig {
applicationId "com.meizu.analysepicasso"
minSdkVersion 15
targetSdkVersion 23
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.1.1'
compile 'com.squareup.picasso:picasso:2.5.2'
compile 'com.squareup.okhttp:okhttp:2.+'
compile 'com.squareup.okhttp:okhttp-urlconnection:2.+'
}
picasso使用
链式编程
Picasso.with(MainActivity.this).load("https://www.baidu.com/img/bd_logo1.png").into(item.img_iv);
上述代码是不是看起来很直观,Picasso使用起来就是这么简单
列表项中使用
如何在具有缓存view功能,类似ListView,GridView中使用。
实例代码如下
首先是界面布局 activity_main_picasso.xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="${relativePackage}.${activityClass}" >
<ListView
android:id="@+id/listview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:dividerHeight="1dip"
/>
</RelativeLayout>
界面中子元素的布局 main_listview_item:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_gravity="center_vertical"
android:padding="5dip" >
<ImageView
android:id="@+id/imageView1"
android:layout_width="60dip"
android:layout_height="60dip"
android:scaleType="centerInside"
android:src="@drawable/ic_launcher" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_marginLeft="15dip"
>
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TextView"
android:textSize="20sp"
android:layout_marginTop="5dip"/>
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TextView"
android:textSize="14sp"
android:layout_marginTop="2dip"/>
</LinearLayout>
</LinearLayout>
处理逻辑代码:
public class MainActivity extends Activity {
private static final String BASE_URL = "http://img1.3lian.com/img2011/w1/106/85/";
private DrawLineTransformation drawLineTransformation;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_picasso);
ArrayList<Dish> dishList = new ArrayList<Dish>();
dishList.add(new Dish(BASE_URL + "42.jpg", "水煮鱼片", "38.00"));
dishList.add(new Dish(BASE_URL + "34.jpg", "小炒肉", "18.00"));
dishList.add(new Dish(BASE_URL + "37.jpg", "清炒时蔬", "15.00"));
dishList.add(new Dish(BASE_URL + "12.jpg", "金牌烤鸭", "36.00"));
dishList.add(new Dish(BASE_URL + "13.jpg", "小鸡蹲蘑菇", "40.00"));
dishList.add(new Dish(BASE_URL + "14.jpg", "麻婆豆腐", "25.00"));
dishList.add(new Dish(BASE_URL + "15.jpg", "北京烧鸭", "30.00"));
dishList.add(new Dish(BASE_URL + "16.jpg", "麻辣风爪", "25.00"));
dishList.add(new Dish(BASE_URL + "17.jpg", "宫爆鸡丁", "36.00"));
dishList.add(new Dish(BASE_URL + "19.jpg", "椒盐虾", "45.00"));
dishList.add(new Dish(BASE_URL + "20.jpg", "清蒸鲈鱼", "20.00"));
MainListViewAdapter adapter = new MainListViewAdapter(dishList);
mListView.setAdapter(adapter);
}
private class MainListViewAdapter extends BaseAdapter {
private ArrayList<Dish> dishList;
public MainListViewAdapter(ArrayList<Dish> list) {
this.dishList = list;
}
@Override
public int getCount() {
return dishList.size();
}
@Override
public Object getItem(int position) {
return dishList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ListViewItemHolder item = null;
if (convertView == null) {
convertView = LayoutInflater.from(MainActivity.this).inflate(R.layout.main_listview_item, null);
item = new ListViewItemHolder();
item.img_iv = (ImageView) convertView.findViewById(R.id.imageView1);
item.name_textview = (TextView) convertView.findViewById(R.id.textView1);
item.price_textview = (TextView) convertView.findViewById(R.id.textView2);
convertView.setTag(item);
} else {
item = (ListViewItemHolder) convertView.getTag();
}
Dish dish = dishList.get(position);
Picasso.with(MainActivity.this).load(dish.getImgUrl()).into(item.img_iv);
item.name_textview.setText(dish.getName());
item.price_textview.setText(dish.getPrice() + "元");
return convertView;
}
}
private class ListViewItemHolder{
ImageView img_iv;
TextView name_textview;
TextView price_textview;
}
}
Tips:这里我们注意的是图片错位问题,像原来我们一般要对这种情况做特殊处理,一般使用的是利用url做为View的tag标签,
当要显示数据时,我们会对当前url和tag标签进行判断,当一样时才会显示.但是Piscasso已经帮我们进行了处理。
3.效果图如下:
占位符图片的使用
picasso支持请求图片之前显示一个图片和下载失败后显示错误图片
实例
Picasso.with(MainActivity.this).load(dish.getImgUrl()).placeholder(R.drawable.test).error(R.drawable.test).into(item.img_iv);
picasso调试
Picasso调试非常简单,只需要使用如下代码将其开关打开。
Picasso.with(MainActivity.this).setIndicatorsEnabled(true);
Tips: 打开调试开关后,会在每个图片针对不同的加载显示不同颜色的三角形
现象解释:
红色:代表从网络下载的图片
蓝色:代表从磁盘缓存加载的图片
绿色:代表从内存中加载的图片
picasso释放内存
在OnDestroy方法中调用shutdown(),如下代码
Picasso.with(MainActivity.this).shutdown();
或者在适当时机通过反射释放内存,如下代码
protected void clearPicassoCache() {
Picasso picasso = Picasso.with(context.getApplicationContext());
try {
Object object = ReflectHelper.getField(picasso, "cache");
ReflectHelper.invoke(object, "clear", null);
} catch (Exception e) {
e.printStackTrace();
}
}
picasso降低内存使用
Picasso默认使用的色彩模式时ARGB_8888,这就意味着一个像素会使用4个字节的空间大小,且不管imageview尺寸大小如何Picasso只缓存一个全尺寸的图片,这就会导致picasso使用内存相对较高,当我们对图片质量不是特别严谨时,或者对速度稍微可以容忍时,我们可以使用如下两种方式降低Picasso内存使用
第一种
Picasso.with(this)
.load("https://www.baidu.com/img/bd_logo1.png");
.resize(768, 432)
.into(item.img_iv);
第二种
Picasso.with(this)
.load("https://www.baidu.com/img/bd_logo1.png")
.fit()
.centerCrop()
.into(item.img_iv);
picasso性能
Picasso的线程池是优化过的,根据当前设备网络状况设置ThreadCount。
在网络良好的条件下,线程池持有较多线程,保证下载速度够快。在网络较差的条件下(2G网络等),线程池减少持有线程,保证带宽不会被多个连接阻塞。
Picasso将图片uri、resize、transform等参数糅合为key,将key封装到Action中进行请求。请求线程Hunter对相同key的Action进行合并,请求完成后,Action依次得到图片。
picasso架构图
picasso时序图
picasso扩展
后续图片处理
比如说我们现在有一个这样需求,针对新商品,我们想在新商品左上角了加一个新的标签,当然这里也是有两种思路的:第一种:直接提供已经做好标签的图片,第二种直接对照片进行
处理,然后自己绘制上去,这里我们主要看下第二种,看如何扩展Picasso实现这个功能
Picasso提供接口类Transformation,其代码如下:
public interface Transformation {
/**
* Transform the source bitmap into a new bitmap. If you create a new bitmap instance, you must
* call {@link android.graphics.Bitmap#recycle()} on {@code source}. You may return the original
* if no transformation is required.
*/
Bitmap transform(Bitmap source);
/**
* Returns a unique key for the transformation, used for caching purposes. If the transformation
* has parameters (e.g. size, scale factor, etc) then these should be part of the key.
*/
String key();
}
实例
1.自定义的对图片处理类”’
class DrawLineTransformation implements Transformation {
@Override
public String key() {
// TODO Auto-generated method stub
return "drawline";
}
@Override
public Bitmap transform(Bitmap bitmap) {
// TODO Auto-generated method stub
synchronized (DrawLineTransformation.class) {
if(bitmap == null) {
return null;
}
Bitmap resultBitmap = bitmap.copy(bitmap.getConfig(), true);
Canvas canvas = new Canvas(resultBitmap);
Paint paint = new Paint();
canvas.save();
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.FILL);
canvas.drawRect(0, 0, resultBitmap.getWidth() / 3, resultBitmap.getHeight() / 3, paint);
canvas.restore();
paint.setTextAlign(Paint.Align.CENTER);
String TEXT = "推荐";
canvas.save();
int canvasWidth = canvas.getWidth();
int canvasHeight = canvas.getHeight();
paint.setTextSize(30);
paint.setStrokeWidth(10);
float textHeight = paint.descent() - paint.ascent();
int baseX = canvasWidth / 6;
int baseY = canvasHeight / 6;
paint.setColor(getResources().getColor(android.R.color.white));
canvas.translate(0, (canvasHeight/6 - textHeight / 2));
canvas.drawText(TEXT, baseX , baseY, paint);
canvas.restore();
bitmap.recycle();
return resultBitmap;
}
}
2.如何使用自定义的对图片处理类,代码如下
Picasso.with(MainActivity.this).load(dish.getImgUrl()).transform(drawLineTransformation).into(item.img_iv);
显示图片控件的扩展
是不是Picasso就只能通过ImageView来显示图片呢?答案当然是no,下面就让我们看看如何使用其他控件来显示图片。
Picasso提供接口类Target,其代码如下:
public interface Target {
/**
* Callback when an image has been successfully loaded.
* <p>
* <strong>Note:</strong> You must not recycle the bitmap.
*/
void onBitmapLoaded(Bitmap bitmap, LoadedFrom from);
/**
* Callback indicating the image could not be successfully loaded.
* <p>
* <strong>Note:</strong> The passed {@link Drawable} may be {@code null} if none has been
* specified via {@link RequestCreator#error(android.graphics.drawable.Drawable)}
* or {@link RequestCreator#error(int)}.
*/
void onBitmapFailed(Drawable errorDrawable);
/**
* Callback invoked right before your request is submitted.
* <p>
* <strong>Note:</strong> The passed {@link Drawable} may be {@code null} if none has been
* specified via {@link RequestCreator#placeholder(android.graphics.drawable.Drawable)}
* or {@link RequestCreator#placeholder(int)}.
*/
void onPrepareLoad(Drawable placeHolderDrawable);
}
实例
1.自定义的对图片显示类
public class ProfileView extends FrameLayout implements Target {
public ProfileView(Context context) {
super(context);
}
public ProfileView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
setBackgroundDrawable(new BitmapDrawable(bitmap));
}
@Override
public void onBitmapFailed(Drawable errorDrawable) {
setBackgroundDrawable(errorDrawable);
}
@Override public void onPrepareLoad(Drawable placeHolderDrawable) {
setBackgroundDrawable(placeHolderDrawable);
}
}
2.如何使用自定义的对图片显示类,代码如下
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_gravity="center_vertical"
android:padding="5dip" >
<com.meizu.analysepicasso.ProfileView
android:id="@+id/imageView1"
android:layout_width="60dip"
android:layout_height="60dip"
android:scaleType="centerInside"
android:src="@drawable/ic_launcher" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_marginLeft="15dip"
>
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TextView"
android:textSize="20sp"
android:layout_marginTop="5dip"/>
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TextView"
android:textSize="14sp"
android:layout_marginTop="2dip"/>
</LinearLayout>
</LinearLayout>
Picasso如何集成其他优秀库
Picasso不能能使用自己一套的下载器,当然也能天衣无缝的集成优秀框架OKHttp
实例
1.工程依赖OKHttp库
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.1.1'
compile 'com.squareup.picasso:picasso:2.5.2'
compile 'com.squareup.okhttp:okhttp:2.+'
compile 'com.squareup.okhttp:okhttp-urlconnection:2.+'
}
2.Picasso中如何使用OKHttp库
public class PicassoToOkHttpActivity extends Activity {
private static final String BASE_URL = "http://img1.3lian.com/img2011/w1/106/85/";
private Picasso mPicasso;
private DrawLineTransformation drawLineTransformation
private OkHttpClient okHttpClient;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_picasso);
ArrayList<Dish> dishList = new ArrayList<Dish>();
drawLineTransformation = new DrawLineTransformation();
//picasso完美衔接OKHttpClient
okHttpClient= new OkHttpClient();
okHttpClient.networkInterceptors().add(new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Response originalResponse = chain.proceed(chain.request());
return originalResponse.newBuilder().header("Cache-Control", "max-age=" + (60 * 60 * 24 * 365)).build();
}
});
okHttpClient.setCache(new Cache(this.getCacheDir(), Integer.MAX_VALUE));
OkHttpDownloader okHttpDownloader = new OkHttpDownloader(okHttpClient);
mPicasso = new Picasso.Builder(this).downloader(okHttpDownloader).build();
dishList.add(new Dish(BASE_URL + "42.jpg", "水煮鱼片", "38.00"));
dishList.add(new Dish(BASE_URL + "34.jpg", "小炒肉", "18.00"));
dishList.add(new Dish(BASE_URL + "37.jpg", "清炒时蔬", "15.00"));
dishList.add(new Dish(BASE_URL + "12.jpg", "金牌烤鸭", "36.00"));
dishList.add(new Dish(BASE_URL + "13.jpg", "小鸡蹲蘑菇", "40.00"));
dishList.add(new Dish(BASE_URL + "14.jpg", "麻婆豆腐", "25.00"));
dishList.add(new Dish(BASE_URL + "15.jpg", "北京烧鸭", "30.00"));
dishList.add(new Dish(BASE_URL + "16.jpg", "麻辣风爪", "25.00"));
dishList.add(new Dish(BASE_URL + "17.jpg", "宫爆鸡丁", "36.00"));
dishList.add(new Dish(BASE_URL + "19.jpg", "椒盐虾", "45.00"));
dishList.add(new Dish(BASE_URL + "20.jpg", "清蒸鲈鱼", "20.00"));
ListView mListView = (ListView) this.findViewById(R.id.listview);
MainListViewAdapter adapter = new MainListViewAdapter(dishList);
mListView.setAdapter(adapter);
}
private class MainListViewAdapter extends BaseAdapter {
private ArrayList<Dish> dishList;
public MainListViewAdapter(ArrayList<Dish> list) {
this.dishList = list;
}
@Override
public int getCount() {
return dishList.size();
}
@Override
public Object getItem(int position) {
return dishList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ListViewItemHolder item = null;
if (convertView == null) {
convertView = LayoutInflater.from(PicassoToOkHttpActivity.this).
inflate(R.layout.main_listview_item, null);
item = new ListViewItemHolder();
item.img_iv = (ImageView) convertView.findViewById(R.id.imageView1);
item.name_textview = (TextView) convertView.findViewById(R.id.textView1);
item.price_textview = (TextView) convertView.findViewById(R.id.textView2);
convertView.setTag(item);
} else {
item = (ListViewItemHolder) convertView.getTag();
}
Dish dish = dishList.get(position);
mPicasso.load(dish.getImgUrl()).transform(drawLineTransformation).into(item.img_iv);
item.name_textview.setText(dish.getName());
item.price_textview.setText(dish.getPrice() + "元");
return convertView;
}
}
对每个请求Request进行预处理
比如,有两台图片服务器,a.meizu.me/b.meizu.me,本来所有的访问地址都是a.meizu.me的,但是要在切换到b.meizu.me,那么我们就可以直接通过该接口进行处理。当然Reqeust还包含了很多其他的信息,我们后面会慢慢看到。只要是Request的相关信息,我们都可以通过这个接口进行修改。
Picasso提供接口类RequestTransformer,其代码如下:
public interface RequestTransformer {
/**
* Transform a request before it is submitted to be processed.
*
* @return The original request or a new request to replace it. Must not be null.
*/
Request transformRequest(Request request);
/** A {@link RequestTransformer} which returns the original request. */
RequestTransformer IDENTITY = new RequestTransformer() {
@Override public Request transformRequest(Request request) {
return request;
}
};
}
}
实例
1.自定义RequestTransformer类,其代码如下:
class RequestTransformerDNS implements Picasso.RequestTransformer {
@Override
public Request transformRequest(com.squareup.picasso.Request request) {
//private static final String REPLACE_URL = "https://www.baidu.com/img/bd_logo1.png";
return new Request.Builder(Uri.parse(REPLACE_URL)).transform(drawLineTransformation).build();
}
2.如何使用自定义RequestTransformer,其代码如下:
public class MainActivity extends Activity {
private static final String BASE_URL = "http://img1.3lian.com/img2011/w1/106/85/";
private static final String REPLACE_URL = "https://www.baidu.com/img/bd_logo1.png";
private Picasso mPicasso;
private Picasso.Builder mBuilder;
private DrawLineTransformation drawLineTransformation;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_picasso);
ArrayList<Dish> dishList = new ArrayList<Dish>();
drawLineTransformation = new DrawLineTransformation();
//不使用Picasso默认参数 自定义Picasso的一些参数
mBuilder = new Picasso.Builder(MainActivity.this);
mBuilder.requestTransformer(new RequestTransformerDNS());
mPicasso = mBuilder.build();
//将自己创建的mPicasso设置为单例模式
Picasso.setSingletonInstance(mPicasso);
dishList.add(new Dish(BASE_URL + "42.jpg", "水煮鱼片", "38.00"));
dishList.add(new Dish(BASE_URL + "34.jpg", "小炒肉", "18.00"));
dishList.add(new Dish(BASE_URL + "37.jpg", "清炒时蔬", "15.00"));
dishList.add(new Dish(BASE_URL + "12.jpg", "金牌烤鸭", "36.00"));
dishList.add(new Dish(BASE_URL + "13.jpg", "小鸡蹲蘑菇", "40.00"));
dishList.add(new Dish(BASE_URL + "14.jpg", "麻婆豆腐", "25.00"));
dishList.add(new Dish(BASE_URL + "15.jpg", "北京烧鸭", "30.00"));
dishList.add(new Dish(BASE_URL + "16.jpg", "麻辣风爪", "25.00"));
dishList.add(new Dish(BASE_URL + "17.jpg", "宫爆鸡丁", "36.00"));
dishList.add(new Dish(BASE_URL + "19.jpg", "椒盐虾", "45.00"));
dishList.add(new Dish(BASE_URL + "20.jpg", "清蒸鲈鱼", "20.00"));
ListView mListView = (ListView) this.findViewById(R.id.listview);
MainListViewAdapter adapter = new MainListViewAdapter(dishList);
mListView.setAdapter(adapter);
}
private class MainListViewAdapter extends BaseAdapter {
private ArrayList<Dish> dishList;
public MainListViewAdapter(ArrayList<Dish> list) {
this.dishList = list;
}
@Override
public int getCount() {
return dishList.size();
}
@Override
public Object getItem(int position) {
return dishList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ListViewItemHolder item = null;
if (convertView == null) {
convertView = LayoutInflater.from(MainActivity.this).inflate(
R.layout.main_listview_item, null);
item = new ListViewItemHolder();
item.img_iv = (ProfileView) convertView.findViewById(R.id.imageView1);
item.name_textview = (TextView) convertView.findViewById(R.id.textView1);
item.price_textview = (TextView) convertView.findViewById(R.id.textView2);
convertView.setTag(item);
} else {
item = (ListViewItemHolder) convertView.getTag();
}
Dish dish = dishList.get(position);
mPicasso.load(dish.getImgUrl()).transform(drawLineTransformation).into(item.img_iv);
item.name_textview.setText(dish.getName());
item.price_textview.setText(dish.getPrice() + "元");
return convertView;
}
自定义Dowloader
假如我们不想使用picasso内置的下载器时,也不想集成okHttp时,这时候我们就可以自己定义一套自己的下载器了
Picasso提供接口类Downloader,我们只需要实现它,或者继承它的子类既可.
实例
public class PicassoBitmapDownloader extends UrlConnectionDownloader {
private static final int MIN_DISK_CACHE_SIZE = 5 * 1024 * 1024; // 5MB
private static final int MAX_DISK_CACHE_SIZE = 50 * 1024 * 1024; // 50MB
@NonNull
private Context context;
@Nullable
private DiskLruCache diskCache;
public class IfModifiedResponse extends Downloader.Response {
private final String ifModifiedSinceDate;
public IfModifiedResponse(InputStream stream, boolean loadedFromCache, long contentLength, String ifModifiedSinceDate) {
super(stream, loadedFromCache, contentLength);
this.ifModifiedSinceDate = ifModifiedSinceDate;
}
public String getIfModifiedSinceDate() {
return ifModifiedSinceDate;
}
}
public PicassoBitmapDownloader(@NonNull Context context) {
super(context);
this.context = context.getApplicationContext();
}
@Override
public Downloader.Response load(final Uri uri, int networkPolicy) throws IOException {
final String key = getKey(uri);
{
Response cachedResponse = getCachedBitmap(key);
if (cachedResponse != null) {
return cachedResponse;
}
}
IfModifiedResponse response = _load(uri);
byte [] data = toByteArray(response.getInputStream());
if (cacheBitmap(key, data, response.getIfModifiedSinceDate())) {
IfModifiedResponse cachedResponse = getCachedBitmap(key);
if (cachedResponse != null) {
return cachedResponse;
}
}
return response;
}
private static byte[] toByteArray(InputStream input) throws IOException {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024 * 4];
int n;
while (-1 != (n = input.read(buffer))) {
byteArrayOutputStream.write(buffer, 0, n);
}
return byteArrayOutputStream.toByteArray();
}
@NonNull
protected IfModifiedResponse _load(Uri uri) throws IOException {
HttpURLConnection connection = openConnection(uri);
int responseCode = connection.getResponseCode();
if (responseCode >= 300) {
connection.disconnect();
throw new ResponseException(responseCode + " " + connection.getResponseMessage(),
0, responseCode);
}
long contentLength = connection.getHeaderFieldInt("Content-Length", -1);
String lastModified = connection.getHeaderField("Last-Modified");
return new IfModifiedResponse(connection.getInputStream(), false, contentLength, lastModified);
}
@Override
protected HttpURLConnection openConnection(Uri path) throws IOException {
HttpURLConnection conn = super.openConnection(path);
DiskLruCache diskCache = getDiskCache();
DiskLruCache.Snapshot snapshot = diskCache == null ? null : diskCache.get(getKey(path));
if (snapshot != null) {
String ifModifiedSince = snapshot.getString(1);
if (!TextUtils.isEmpty(ifModifiedSince)) {
conn.addRequestProperty("If-Modified-Since", ifModifiedSince);
}
}
return conn;
}
@Override
public void shutdown() {
try {
if (diskCache != null) {
diskCache.flush();
diskCache.close();
}
} catch (IOException e) {
e.printStackTrace();
}
super.shutdown();
}
@Nullable
public IfModifiedResponse getCachedBitmap(String key) {
try {
DiskLruCache diskCache = getDiskCache();
DiskLruCache.Snapshot snapshot = diskCache == null ? null : diskCache.get(key);
if (null != snapshot) {
String content = snapshot.getString(1);
InputStream inputStream = snapshot == null ? null : snapshot.getInputStream(0);
long fileLength = snapshot.getLength(0);
if (inputStream == null) {
return null;
}
return new IfModifiedResponse(inputStream, true, fileLength, content);
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 获取DiskLruCache
* @return
*/
@Nullable
synchronized public DiskLruCache getDiskCache() {
if (diskCache == null) {
try {
File file = new File(context.getCacheDir() + "/images");
if (!file.exists()) {
file.mkdirs();
}
long maxSize = calculateDiskCacheSize(file);
diskCache = DiskLruCache.open(file, BuildConfig.VERSION_CODE, 2, maxSize);
} catch (Exception e) {
e.printStackTrace();
}
}
return diskCache;
}
/**
* 缓存bitmap到磁盘上
* @param key
* @param data
* @param ifModifiedSince
* @return
*/
public boolean cacheBitmap(@Nullable String key, @Nullable byte[] data, @Nullable String ifModifiedSince) {
OutputStream os = null;
DiskLruCache.Editor editor = null;
try {
DiskLruCache diskCache = getDiskCache();
if (diskCache == null) {
return false;
}
editor = diskCache.edit(key);
if (editor == null) {
return false;
}
os = editor.newOutputStream(0);
os.write(data);
editor.set(1, ifModifiedSince == null ? "" : ifModifiedSince);
editor.commit();
} catch (Throwable x) {
if (null != editor) {
try {
editor.abort();
}catch (Exception e){
e.printStackTrace();
}
}
Log.d("x in writeToDiskCache", x.getMessage());
} finally {
ChatUtils.close(os);
}
return true;
}
/**
* 通过uri获取对应的key
* @param uri
* @return
*/
public static String getKey(Uri uri) {
String cacheKey;
String uriKey = uri.toString();
try {
final MessageDigest mDigest = MessageDigest.getInstance("MD5");
mDigest.update(uriKey.getBytes());
cacheKey = bytesToHexString(mDigest.digest());
} catch (NoSuchAlgorithmException e) {
cacheKey = String.valueOf(uriKey.hashCode());
}
return cacheKey;
}
private static String bytesToHexString(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
String hex = Integer.toHexString(0xFF & bytes[i]);
if (hex.length() == 1) {
sb.append('0');
}
sb.append(hex);
}
return sb.toString();
}
/**
* 计算当前目录剩余空间
* @param dir
* @return
*/
static long calculateDiskCacheSize(File dir) {
long available = ChatUtils.getUsableSpace(dir);
// Target 2% of the total space.
long size = available / 50;
// Bound inside min/max size for disk cache.
return Math.max(Math.min(size, MAX_DISK_CACHE_SIZE), MIN_DISK_CACHE_SIZE);
}