图片加载框架-Picasso最详细的使用指南,Android高级工程师面试题-字节跳动

fit 是干什的呢?上面我们需要用resize()来指定我们需要的图片的尺寸,那就是说在程序中需要我们计算我们需要的尺寸(固定大小的除外),这样很麻烦,fit 方法就帮我们解决了这个问题。fit 它会自动测量我们的View的大小,然后内部调用reszie方法把图片裁剪到View的大小,这就帮我们做了计算size和调用resize 这2步。非常方便。代码如下:

Picasso.with(this).load(URL)

.placeholder(R.drawable.default_bg)

.error(R.drawable.error_iamge)

.fit()

.into(mImageView);

使用fit 还是会出现拉伸扭曲的情况,因此最好配合前面的centerCrop使用,代码如下:

Picasso.with(this).load(URL)

.placeholder(R.drawable.default_bg)

.error(R.drawable.error_iamge)

.fit()

.centerCrop()

.into(mImageView);

看一下对比图:

fit(会拉伸):

image_fit.png

fit & centerCrop (不会拉伸):

fit_centerCrop.png

注意:特别注意,

1,fit 只对ImageView 有效

2,使用fit时,ImageView 宽和高不能为wrap_content,很好理解,因为它要测量宽高。

4. 图片旋转Rotation()

在图片显示到ImageView 之前,还可以对图片做一些旋转操作,调用rotate(int degree)方法

Picasso.with(this).load(URL)

.placeholder(R.drawable.default_bg)

.error(R.drawable.error_iamge)

.rotate(180)

.into(mImageView);

这个方法它是以(0,0)点旋转,但是有些时候我们并不想以(0,0)点旋转,还提供了另外一个方法可以指定原点:

  • rotate(float degrees, float pivotX, float pivotY) 以(pivotX, pivotY)为原点旋转

Picasso.with(this).load(URL)

.placeholder(R.drawable.default_bg)

.error(R.drawable.error_iamge)

.rotate(180,200,100)

.into(mImageView);

5. 转换器Transformation

Transformation 这就是Picasso的一个非常强大的功能了,它允许你在load图片 -> into ImageView 中间这个过成对图片做一系列的变换。比如你要做图片高斯模糊、添加圆角、做度灰处理、圆形图片等等都可以通过Transformation来完成。

来看一个高斯模糊的例子:

1,首先定义一个转换器继承 Transformation

public static class BlurTransformation implements Transformation{

RenderScript rs;

public BlurTransformation(Context context) {

super();

rs = RenderScript.create(context);

}

@Override

public Bitmap transform(Bitmap bitmap) {

// Create another bitmap that will hold the results of the filter.

Bitmap blurredBitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true);

// Allocate memory for Renderscript to work with

Allocation input = Allocation.createFromBitmap(rs, blurredBitmap, Allocation.MipmapControl.MIPMAP_FULL, Allocation.USAGE_SHARED);

Allocation output = Allocation.createTyped(rs, input.getType());

// Load up an instance of the specific script that we want to use.

ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));

script.setInput(input);

// Set the blur radius

script.setRadius(25);

// Start the ScriptIntrinisicBlur

script.forEach(output);

// Copy the output to the blurred bitmap

output.copyTo(blurredBitmap);

bitmap.recycle();

return blurredBitmap;

}

@Override

public String key() {

return “blur”;

}

}

2, 加载图片的时候,在into 方法前面调用 transform方法 应用Transformation

Picasso.with(this).load(URL)

.placeholder(R.drawable.default_bg)

.error(R.drawable.error_iamge)

.transform(new BlurTransformation(this))

.into(mBlurImage);

看一下效果图:

transformation.png

上面为原图,下面为高斯模糊图

是不是很强大,任何复杂的变换都可以通过Transformation 来做。

还不止于此,还有更强大的功能。可以在一个请求上应用多个Transformation

比如:我想先做个度灰处理然后在做一个高斯模糊图:

1, 度灰的Transformation

public static class GrayTransformation implements Transformation{

@Override

public Bitmap transform(Bitmap source) {

int width, height;

height = source.getHeight();

width = source.getWidth();

Bitmap bmpGrayscale = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);

Canvas c = new Canvas(bmpGrayscale);

Paint paint = new Paint();

ColorMatrix cm = new ColorMatrix();

cm.setSaturation(0);

ColorMatrixColorFilter f = new ColorMatrixColorFilter(cm);

paint.setColorFilter(f);

c.drawBitmap(source, 0, 0, paint);

if(source!=null && source!=bmpGrayscale){

source.recycle();

}

return bmpGrayscale;

}

@Override

public String key() {

return “gray”;

}

}

2, 如果是多个Transformation操作,有2种方式应用

方式一:直接调用多次transform 方法,不会覆盖的。它只是保存到了一个List 里面

Picasso.with(this).load(URL)

.placeholder(R.drawable.default_bg)

.error(R.drawable.error_iamge)

.fit()

.centerCrop()

.transform(new GrayTransformation())//度灰处理

.transform(new BlurTransformation(this))//高斯模糊

.into(mBlurImage);

需要注意调用的顺序

方式二:接受一个List,将Transformation 放大list 里

List transformations = new ArrayList<>();

transformations.add(new GrayTransformation());

transformations.add(new BlurTransformation(this));

Picasso.with(this).load(URL)

.placeholder(R.drawable.default_bg)

.error(R.drawable.error_iamge)

.fit()

.centerCrop()

.transform(transformations)

.into(mBlurImage);

效果图:

gray_blur.png

如上图,第一张为度灰操作,第二张为 度灰+高斯模糊

另外发现了一个开源库,专门写了很多好玩的Transformation,有兴趣的可以看一下:

picasso-transformations

6. 请求优先级

Picasso 为请求设置有优先级,有三种优先级,LOW、NORMAL、HIGH。默认情况下都是NORMAL,除了调用fetch 方法,fetch 方法的优先级是LOW。

public enum Priority {

LOW,

NORMAL,

HIGH

}

可以通过priority方法设置请求的优先级,这会影响请求的执行顺序,但是这是不能保证的,它只会往高的优先级靠拢。代码如下:

Picasso.with(this).load(URL)

.placeholder(R.drawable.default_bg)

.error(R.drawable.error_iamge)

.priority(Picasso.Priority.HIGH)

// .priority(Picasso.Priority.LOW)

.into(mImageView);

7. Tag管理请求

Picasso 允许我们为一个请求设置tag来管理请求,看一下对应的几个方法:

下面3个方法是Picasso这个类的:

  • cancelTag(Object tag) 取消设置了给定tag的所有请求

  • pauseTag(Object tag) 暂停设置了给定tag 的所有请求

  • resumeTag(Object tag) resume 被暂停的给定tag的所有请求

还有一个方法是RequestCreator的:

  • tag(Object tag) 为请求设置tag

几个方法的意思也很明确,就是我们可以暂停、resume、和取消请求,可以用在哪些场景呢?

场景一: 比如一个照片流列表,当我们快速滑动列表浏览照片的时候,后台会一直发起请求加载照片的,这可能会导致卡顿,那么我们就可以为每个请求设置一个相同的Tag,在快速滑动的时候,调用pauseTag暂停请求,当滑动停止的时候,调用resumeTag恢复请求,这样的体验是不是就会更好一些呢。

Adapter中添加如下代码:

Picasso.with(this).load(mData.get(position))

.placeholder(R.drawable.default_bg)

.error(R.drawable.error_iamge)

.tag(“PhotoTag”)

.into(holder.mImageView);

Activity中为RecyclerView添加滑动监听:

mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {

@Override

public void onScrollStateChanged(RecyclerView recyclerView, int newState) {

final Picasso picasso = Picasso.with(MainActivity.this);

if (newState == SCROLL_STATE_IDLE) {

picasso.resumeTag(“PhotoTag”);

} else {

picasso.pauseTag(“PhotoTag”);

}

}

});

场景二: 比如一个照片流列表界面,在弱网环境下,加载很慢,退出这个界面时可能会有很多请求没有完成,这个时候我们就可 以通过tag 来取消请求了。

@Override

protected void onDestroy() {

super.onDestroy();

Picasso.with(this).cancelTag(“PhotoTag”);

}

8. 同步/异步加载图片

Picasso 加载图片也有同步/异步两种方式

**1,get() 同步 **

很简单,同步加载使用get() 方法,返回一个Bitmap 对象,代码如下:

try {

Bitmap bitmap = Picasso.with(this).load(URL).get();

} catch (IOException e) {

e.printStackTrace();

}

注意:使用同步方式加载,不能放在主线程来做。

2,异步的方式加载图片,fetch()

一般直接加载图片通过into显示到ImageView 是异步的方式,除此之外,还提供了2个异步的方法:

  • fetch() 异步方式加载图片

  • fetch(Callback callback) 异步方式加载图片并给一个回调接口。

Picasso.with(this).load(URL).fetch(new Callback() {

@Override

public void onSuccess() {

//加载成功

}

@Override

public void onError() {

//加载失败

}

});

这里就要吐槽一下接口设计了,回调并没有返回Bitmap, 不知道作者是怎么考虑的,只是一个通知效果,知道请求失败还是成功。

**fetch 方法异步加载图片并没有返回Bitmap,这个方法在请求成功之后,将结果存到了缓存,包括磁盘和内存缓存。所以使用这种方式加载图片适用于这种场景:知道稍后会加载图片,使用fetch 先加载缓存,起到一个预加载的效果。 **

9. 缓存(Disk 和 Memory)

Picasso 有内存缓存(Memory)和磁盘缓存( Disk), 首先来看一下源码中对于缓存的介绍:

  • LRU memory cache of 15% the available application RAM

  • Disk cache of 2% storage space up to 50MB but no less than 5MB. (Note: this is only

available on API 14+ or if you are using a standalone library that provides a disk cache on all API levels like OkHttp)

  • Three download threads for disk and network access.

可以看出,内存缓存是使用的LRU 策略的缓存实现,它的大小是内存大小的15%,可以自定义它的大小,最后在扩展那一章节再讲,磁盘缓存是磁盘容量的2%但是不超过50M,不少于5M。处理一个请求的时候,按照这个顺讯检查:memory->disk->network 。先检查有木有内存缓存,如果命中,直接返回结果,否则检查磁盘缓存,命中则返回结果,没有命中则从网上获取。

默认情况下,Picasso 内存缓存和磁盘缓存都开启了的,也就是加载图片的时候,内存和磁盘都缓存了,但是有些时候,我们并不需要缓存,比如说:加载一张大图片的时候,如果再内存中保存一份,很容易造成OOM,这时候我们只希望有磁盘缓存,而不希望缓存到内存,因此就需要我们设置缓存策略了。Picasso 提供了这样的方法。

1,memoryPolicy 设置内存缓存策略

就像上面所说的,有时候我们不希望有内存缓存,我们可以通过 memoryPolicy 来设置。MemoryPolicy是一个枚举,有两个值

**NO_CACHE:**表示处理请求的时候跳过检查内存缓存

**NO_STORE: ** 表示请求成功之后,不将最终的结果存到内存。

示例代码如下:

with(this).load(URL)

.placeholder(R.drawable.default_bg)

.error(R.drawable.error_iamge)

.memoryPolicy(MemoryPolicy.NO_CACHE,MemoryPolicy.NO_STORE) //静止内存缓存

.into(mBlurImage);

2,networkPolicy 设置磁盘缓存策略

和内存缓存一样,加载一张图片的时候,你也可以跳过磁盘缓存,和内存缓存策略的控制方式一样,磁盘缓存调用方法networkPolicy(NetworkPolicy policy, NetworkPolicy... additional) , NetworkPolicy是一个枚举类型,有三个值:

NO_CACHE: 表示处理请求的时候跳过处理磁盘缓存

** NO_STORE:** 表示请求成功后,不将结果缓存到Disk,但是这个只对OkHttp有效。

OFFLINE: 这个就跟 上面两个不一样了,如果networkPolicy方法用的是这个参数,那么Picasso会强制这次请求从缓存中获取结果,不会发起网络请求,不管缓存中能否获取到结果。

使用示例:

with(this).load(URL)

.placeholder(R.drawable.default_bg)

.error(R.drawable.error_iamge)

.memoryPolicy(MemoryPolicy.NO_CACHE,MemoryPolicy.NO_STORE)//跳过内存缓存

.networkPolicy(NetworkPolicy.NO_CACHE)//跳过磁盘缓存

.into(mBlurImage);

强制从缓存获取:

with(this).load(URL)

.placeholder(R.drawable.default_bg)

.error(R.drawable.error_iamge)

.networkPolicy(NetworkPolicy.OFFLINE)//强制从缓存获取结果

.into(mBlurImage);

10. Debug 和日志

1,缓存指示器

上一节说了,Picasso 有内存缓存和磁盘缓存,先从内存获取,没有再去磁盘缓存获取,都有就从网络加载,网络加载是比较昂贵和耗时的。因此,作为一个开发者,我们往往需要加载的图片是从哪儿来的(内存、Disk还是网络),Picasso让我们很容易就实现了。只需要调用一个方法setIndicatorsEnabled(boolean)就可以了,它会在图片的左上角出现一个带色块的三角形标示,有3种颜色,绿色表示从内存加载、蓝色表示从磁盘加载、红色表示从网络加载。

Picasso.with(this)

.setIndicatorsEnabled(true);//显示指示器

效果图:

cache_indicator.png

如上图所示,第一张图从网络获取,第二张从磁盘获取,第三张图从内存获取。

看一下源码中定义指示器的颜色:

/** Describes where the image was loaded from. */

public enum LoadedFrom {

MEMORY(Color.GREEN),

DISK(Color.BLUE),

NETWORK(Color.RED);

final int debugColor;

private LoadedFrom(int debugColor) {

this.debugColor = debugColor;

}

}

可以很清楚看出,对应三种颜色代表着图片的来源。

2,日志

上面的指示器能够很好的帮助我们看出图片的来源,但是有时候我们需要更详细的信息,Picasso,可以打印一些日志,比如一些关键方法的执行时间等等,我们只需要调用setLoggingEnabled(true)方法,然后App在加载图片的过程中,我们就可以从logcat 看到一些关键的日志信息。

Picasso.with(this)

.setLoggingEnabled(true);//开启日志打印

11. Picasso 扩展

到目前为止,Picasso的基本使用已经讲得差不多了,但是在实际项目中我们这可能还满足不了我们的需求,我们需要对它做一些自己的扩展,比如我们需要换缓存的位置、我们需要扩大缓存、自定义线程池、自定义下载器等等。这些都是可以的,接下来我们来看一下可以做哪些方面的扩展。

1,用Builder 自己构造一个Picasso Instance

我们来回顾一下前面是怎么用Picasso 加载图片的:

Picasso.with(this)

.load(“http://ww3.sinaimg.cn/large/610dc034jw1fasakfvqe1j20u00mhgn2.jpg”)

.into(mImageView);

总共3步:

1,用with方法获取一个Picasso 示例

2,用load方法加载图片

3,用into 放法显示图片

首先Picasso是一个单例模式,我们每一次获取的示例都是默认提供给我们的实例。但是也可以不用它给的Instance,我们直接用builder来构造一个Picasso:

Picasso.Builder builder = new Picasso.Builder(this);

//构造一个Picasso

Picasso picasso = builder.build();

//加载图片

picasso.load(URL)

.into(mImageView);

这样我们就构造了一个局部的Picasso实例,当然了,我们直接用new 了一个builder,然后build()生成了一个Picasso。这跟默认的通过with方法获取的实例是一样的。那么现在我们就可以配置一些自定义的功能了。

2, 配置自定义下载器 downLoader

如果我们不想用默认提供的Downloader,那么我们可以自定义一个下载器然后配置进去。举个例子:

(1) 先自定义一个Downloader(只是举个例子,并没有实现):

/**

  • Created by zhouwei on 17/2/26.

*/

public class CustomDownloader implements Downloader {

@Override

public Response load(Uri uri, int networkPolicy) throws IOException {

return null;

}

@Override

public void shutdown() {

}

}

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
img

最后,面试前该准备哪些资源复习?

其实客户端开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。

这里再分享一下我面试期间的复习路线:(以下体系的复习资料是我从各路大佬收集整理好的)

《Android开发七大模块核心知识笔记》

面试字节两轮后被完虐,字节面试官给你的技术面试指南,请查收

面试字节两轮后被完虐,字节面试官给你的技术面试指南,请查收

《960全网最全Android开发笔记》

面试字节两轮后被完虐,字节面试官给你的技术面试指南,请查收

《379页Android开发面试宝典》

历时半年,我们整理了这份市面上最全面的安卓面试题解析大全
包含了腾讯、百度、小米、阿里、乐视、美团、58、猎豹、360、新浪、搜狐等一线互联网公司面试被问到的题目。熟悉本文中列出的知识点会大大增加通过前两轮技术面试的几率。

《507页Android开发相关源码解析》

只要是程序员,不管是Java还是Android,如果不去阅读源码,只看API文档,那就只是停留于皮毛,这对我们知识体系的建立和完备以及实战技术的提升都是不利的。

真正最能锻炼能力的便是直接去阅读源码,不仅限于阅读各大系统源码,还包括各种优秀的开源库。

59)]
[外链图片转存中…(img-h7CCLY3W-1712078595959)]
[外链图片转存中…(img-VsKdPOpX-1712078595959)]
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
[外链图片转存中…(img-HYqMh9XW-1712078595960)]

最后,面试前该准备哪些资源复习?

其实客户端开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。

这里再分享一下我面试期间的复习路线:(以下体系的复习资料是我从各路大佬收集整理好的)

《Android开发七大模块核心知识笔记》

[外链图片转存中…(img-0vqfliGU-1712078595960)]

[外链图片转存中…(img-7PDMLZz7-1712078595960)]

《960全网最全Android开发笔记》

[外链图片转存中…(img-CKWKXexn-1712078595961)]

《379页Android开发面试宝典》

历时半年,我们整理了这份市面上最全面的安卓面试题解析大全
包含了腾讯、百度、小米、阿里、乐视、美团、58、猎豹、360、新浪、搜狐等一线互联网公司面试被问到的题目。熟悉本文中列出的知识点会大大增加通过前两轮技术面试的几率。

《507页Android开发相关源码解析》

只要是程序员,不管是Java还是Android,如果不去阅读源码,只看API文档,那就只是停留于皮毛,这对我们知识体系的建立和完备以及实战技术的提升都是不利的。

真正最能锻炼能力的便是直接去阅读源码,不仅限于阅读各大系统源码,还包括各种优秀的开源库。

本文已被CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值