热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Glide4.11.0

目录概述原理浅析with:创建RequestManagerload:创建RequestBuilderinto:创建Request

目录

概述

原理浅析

with:创建RequestManager

load:创建RequestBuilder

into:创建Request

重要类:

1.Glide:

2.GlideBuilder:

3.RequestManagerRetriever:

4.RequestManager:

5.RequestBuilder:

6.Request:

7.SingleRequest:

8.Target:

9.AppGlideModule

10.MemorySizeCalculator

缓存机制

1.Glide一级缓存:ActiveResources

2.Glide二级缓存:MemoryCache

3.Glide三级缓存:DiskLruCache

4.缓存加载

5.高级技巧

基本用法

导入库:

加载图片:

1.占位图 / 错误图 / 后备回调图

2.指定加载图片大小

3.指定加载图片格式

4.加载Gif图片

5.加载视频文件

6.RequestOptions

7.TransitionOptions

8.缩略图

9.重用 / 取消加载

10.RecyclerView中使用Glide

11.RecyclerView中使用Glide预加载

回调监听

1.into(Target target)方法:

2.preload()方法:

3.downloadOnly()方法:(待完善……!)

4.listener()方法:

图片变换

1.图片变换的基本用法

2.图片变换的自定义:

3.图片变换开源库(重点推荐):

自定义配置

1.AppGlideModule(用于Application)

2.LibraryGlideModule(用于Library)

3.变更Glide配置

4.替换Glide组件

5.依赖 Glide 注解解析器

6.添加混淆

7.Glide v4禁用清单解析:

8.依赖冲突




概述

Picasso使用简单,代码简洁,依赖包最小;功能简单,性能较差,没有实现本地缓存。

Glide:  支持Gif  WebP  Video,生命周期继承,高效缓存策略(支持Memory和Disk缓存;支持多种规格图片缓存;内存开销小,默认RGB_565);方法多,比Picasso复杂,比Fresco简单,性能优于Picasso,劣于Fresco。

Fresco(Facebook): 大大减少OOM,底层使用 C++技术解决图片缓存问题,性能优,几乎全部功能都能在 xml 上定制;用法复杂,依赖包(2M ~ 3M)

Picasso And Glide都可以实现一行代码解决加载图片!引入第三方Lib够用即可(个人愚见),综合考虑性能、易用性、包大小等方面,个人推荐Glide。

Glide内存消耗:

Glide默认Bitmap格式是RGB_565,Picasso默认Bitmap格式是ARGB_8888Glide加载的图片质量要差于Picasso,而内存开销要小一半。

更改Glide默认格式为ARGB_8888,更改之后,Picasso的内存开销仍然远大于Glide

Glide.with(this).load("http://url/cover.jpg").apply(new RequestOptions().format(DecodeFormat.PREFER_ARGB_8888)).into(imageView);

更改之后的内存消耗对比图:Picasso的内存开销仍然远大于Glide原因在于Picasso是加载了全尺寸的图片到内存,然后让GPU来实时重绘大小。而Glide加载的大小和ImageView的大小是一致的(可以自动计算出任意情况下的ImageView大小),因此更小。


原理浅析


with:创建RequestManager

Glide#with() 方法会将 RequestManager 的创建委托给 RequestManagerRetriever,RequestManagerRetriever 为单例类,调用 get(Context) 创建 RequestManager。

Glide.with()方法可以接收Context、Activity、Fragment或者当前应用程序的ApplicationContext类型参数。注意with()方法中传入的实例会决定Glide加载图片的生命周期,如果传入的是Activity或者Fragment的实例,那么当这个Activity或Fragment被销毁的时候,图片加载也会停止。如果传入的是ApplicationContext,那么只有当应用程序被杀掉的时候,图片加载才会停止。

一个Activity / Fragment对应一个RequestManager(单例,生命周期);子线程发起或者Application发起,对应全局RequestManager(单例,生命周期)。


load:创建RequestBuilder

load() 方法返回一个RequestBuilder用于指定待加载的图片资源。Glide支持加载各种各样的图片资源,包括网络图片、本地图片、应用资源、二进制流、Uri对象等等。因此load()方法也有很多个方法重载:

RequestBuilder load(@Nullable Bitmap bitmap);
RequestBuilder load(@Nullable Drawable drawable);RequestBuilder load(@Nullable String string);// 加载Uri对象RequestBuilder load(@Nullable Uri uri);// 加载本地图片RequestBuilder load(@Nullable File file);// 加载应用资源RequestBuilder load(@RawRes @DrawableRes @Nullable Integer resourceId);// 加载网络URLRequestBuilder load(@Nullable URL url);// 加载二进制流RequestBuilder load(@Nullable byte[] model);RequestBuilder load(@Nullable Object model);
RequestBuilder downloadOnly();RequestBuilder download(@Nullable Object model);

into:创建Request

返回一个Target接口类型,该方法最终创建Request并启动,同时也指定了接收请求结果的 Target,into(ImageView) 作为辅助方法,接受一个 ImageView 参数并为其请求的资源类型包装了一个合适的 ImageViewTarget。


重要类:

Glide关键三步:with,load,into

Glide.with(this).load("http://url/cover.jpg").into(imageView);

1.Glide:

单例类,提供配置Encoder、Decoder、ModelLoader、Pool入口,提供创建 RequestManager 的接口(Glide#with() 方法)


2.GlideBuilder:

用于创建 Glide 实例的类,其中包含了很多个 get/set 方法,例如设置 BitmapPool、MemoryCache(内存缓存)、DiskCacheFactory(磁盘缓存)等,最终通过这些设置调用 build 方法构建 Glide。


3.RequestManagerRetriever:

根据with方法传入的参数类型,具体实施创建RequestManager操作。

(1)RequestManager fragmentGet(Context context, android.app.FragmentManager fm);

对应参数类型为Activity或者android.app.Fragment

(2)RequestManager supportFragmentGet(Context context, android.support.v4.app.FragmentManager fm);

对应参数类型为android.support.v4.app.Fragment

(3)RequestManager getApplicationManager(Context context);

子线程中调用with方法,或者传入参数为ApplicationContext


4.RequestManager:

创建 RequestBuilder(通过load方法);通过生命周期管理请求的启动结束等。


5.RequestBuilder:

构建请求,例如设置 RequestOption、缩略图、加载失败占位图等等。RequestManager 中 load 重载方法,对应 RequestBuilder 中的重载 load 方法,一般来说 load 方法之后就是调用 into 方法设置 ImageView 或者 Target,into 方法中最后会创建 Request,并启动。


6.Request:

接口类,定义了对请求的开始、结束、状态获取、回收等操作,Request中不仅包含基本的信息,还负责管理请求。


7.SingleRequest:

Request实现类,负责执行请求并将结果反映到 Target 上。

当我们使用 Glide 加载图片时,会先根据 Target 类型创建不同的 Target,然后 RequestBuilder 将这个 target 当做参数创建 Request 对象,Request 与 Target 就是这样关联起来的。这里就会先创建一个包含 Target 的 SingleRequest 对象。考虑到性能问题,可能会连续创建很多个 SingleRequest 对象,所以使用了对象池来做缓存。

我们经常在 Activity#onCreate 方法中直接使用 Glide 方法,但此时的图片大小还未确定,所以调用 Request#begin 时并不会直接发起请求,而是等待 ImageView 初始化完成,对于 ViewTarget 以及其子类来说,会注册View 的 OnPreDrawListener 事件,等待 View 初始化完成后就调用 SingleRequest#onSizeReady 方法,这个方法里就会开始加载图片了。

onSizeReady 方法并不会去直接加载图片,而是调用了 Engine#load 方法加载,这个方法差不多有二十个参数,所以 onSizeReady 方法算是用来构建参数列表并且调用 Engine#load 方法的。

clear 方法用于停止并清除请求,主要就是从 Engine 中移除掉这个任务以及回调接口。


8.Target:

目标类,代表一个可被 Glide 加载并且具有生命周期的资源,Target 负责展示占位符,加载资源到View,并为每个请求决定合适的尺寸。在调用RequestBuilder#into 方法时会根据传入参数创建对应类型的 Target 实现类。

Glide 默认提供了用于放在 ImageView 上的 ImageViewTarget(以及其各种子类)、放在 AppWidget 上的 AppWidgetTarget、用于同步加载图片的 FutureTarget(只有一个实现类:RequestFutureTarget)等等,

(1)ImageViewTarget(继承自ViewTarget —> BaseTarget)

负责把图片资源加载到ImageView上,图片数据加载完成后会回调其中的 onResourceReady 方法,第一步是将图片设置给 ImageView,第二部是判断是否需要使用动画,需要的话就执行动画。

(2)RequestFutureTarget

调用 RequestBuilder#submit 将会返回一个 FutureTarget,调用 get 方法即可获取到加载的资源对象。

try {Drawable drawable = Glide.with(this).load("http://url/cover.jpg").apply(new RequestOptions().format(DecodeFormat.PREFER_ARGB_8888)).override(Target.SIZE_ORIGINAL).submit().get();} catch (InterruptedException | ExecutionException e) {e.printStackTrace();}

(3)AppWidgetTarget:设置图片到RemoteView上。

(4)NotificationTarget:设置图片到Notification的RemoteView上。


9.AppGlideModule

继承自LibraryGlideModule(实现了RegistersComponents接口) 同时实现了AppliesOptions接口。用于延迟设置Glide相关参数。通过这个设置可以保证在 Glide 单例类初始时,所有请求发起之前应用到 Glide。

(1)RegistersComponents接口:用于管理注册 ModelLoader、Encoder、Decoder等组件的,Glide默认使用HttpUrlConnection,可以改为使用OkHttp

(2)AppliesOptions接口      :通过GlideBuilder参数,设置线程池、设置 BitmapPool/ArrayPoll等。


10.MemorySizeCalculator

用来计算 BitmapPool 、ArrayPool 以及 MemoryCache 大小。


缓存机制

Glide的缓存设计可以说是非常先进的,考虑的场景也很周全。在缓存这一功能上,Glide又将它分成了两个模块,一个是内存缓存,一个是硬盘缓存内存缓存的主要作用是防止应用重复将图片数据读取到内存当中,而硬盘缓存的主要作用是防止应用重复从网络或其他地方重复下载和读取数据,二者相互结合。

Glide在实例化时,会初始化三个与缓存相关的类对象以及一个用于计算缓存大小的类对象:

// 根据当前机器参数计算需要设置的缓存大小
if (memorySizeCalculator == null) {memorySizeCalculator = new MemorySizeCalculator.Builder(context).build();
}
// Bitmap池
if (bitmapPool == null) {int size = memorySizeCalculator.getBitmapPoolSize();if (size > 0) {bitmapPool = new LruBitmapPool(size);} else {bitmapPool = new BitmapPoolAdapter();}
}
// 创建内存缓存
if (memoryCache == null) {memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
}
// 创建硬盘缓存
if (diskCacheFactory == null) {diskCacheFactory = new InternalCacheDiskCacheFactory(context);
}

1.Glide一级缓存:ActiveResources

弱引用存储当前正在活动的资源,即正在使用的资源。生命周期短,没有大小限制,内部基于Map>> 实现存储。

从Memory、Disk、File、network获取的图片资源,使用时(例如,在页面显示)存储到ActiveResources,使用完将缓存图片从activeResources中移除,然后再将它put到LruResourceCache当中。这样也就实现了正在使用中的图片使用弱引用来进行缓存,不在使用中的图片使用LruCache来进行缓存的功能。


2.Glide二级缓存:MemoryCache

内存缓存实现类为 LruResourceCache,继承了 LruCache,LruCache基于LinkedHashMap实现LRU存储策略。LinkedHashMap是Java提供的一个很好的用来实现 LRU 算法的数据结构,其基于 HashMap 实现,同时又将 HashMap 中的 Entity 串成了一个双向链表。

LruResourceCache 中提供了一个 ResourceRemovedListener 接口,当有资源从 MemoryCache 中被移除时会回调其中的方法,Engine 中接收到这个消息后就会进行 Bitmap 的回收操作。

Glide默认开启内存缓存,只要在它还没从内存中被清除之前,下次使用Glide再加载这张图片都会直接从内存当中读取,而不用重新从网络或硬盘上读取了,这样无疑就可以大幅度提升图片的加载效率。比方说你在一个RecyclerView当中反复上下滑动,RecyclerView中只要是Glide加载过的图片都可以直接从内存当中迅速读取并展示出来,从而大大提升了用户体验。

Glide.with(this).load(url)// 关闭内存缓存.skipMemoryCache(true).into(imageView);

3.Glide三级缓存:DiskLruCache

磁盘缓存路径默认为 Context#getCacheDir() 下面的 image_manager_disk_cache 文件夹,默认缓存大小为 250MB。

磁盘缓存实现类DiskLruCacheWrapper实现了 DiskCache 接口:

File get(Key key);
void put(Key key, Writer writer);
void delete(Key key);
void clear();

调用diskCacheStrategy()方法并传入DiskCacheStrategy.NONE,就可以禁用掉Glide的硬盘缓存功能了。

Glide.with(this).load(url)// 关闭硬盘缓存.diskCacheStrategy(DiskCacheStrategy.NONE).into(imageView);

diskCacheStrategy()方法基本上就是Glide硬盘缓存功能的一切,它可以接收四种参数:

(1)DiskCacheStrategy.NONE:      表示不缓存任何内容。

(2)DiskCacheStrategy.DATA:       表示只缓存原始图片。

(3)DiskCacheStrategy. RESOURCE: 表示只缓存转换之后的图片。

(3)DiskCacheStrategy. AUTOMATIC:表示根据数据源自动选择磁盘缓存策略(默认选择)。尝试对本地和远程图片使用最佳的策略。当你加载远程数据(比如,从URL下载)时,AUTOMATIC 策略仅会存储未被你的加载过程修改过的原始数据;对于本地数据,AUTOMATIC 策略则会仅存储变换过的缩略图。

(4)DiskCacheStrategy.ALL :        表示既缓存原始图片,也缓存转换过后的图片。

当我们使用Glide去加载一张图片的时候,Glide默认并不会将原始图片展示出来,而是会对图片进行压缩和转换。而Glide默认情况下在硬盘缓存的就是转换过后的图片,我们通过调用diskCacheStrategy()方法则可以改变这一默认行为。

Glide使用的是自己编写的DiskLruCache工具类,而非Google提供的现成的工具类DiskLruCache。但是基本的实现原理都是差不多的。

Picasso和Glide在磁盘缓存策略上有很大的不同。Picasso缓存的是全尺寸的,而Glide缓存的是跟ImageView尺寸相同的。

将ImageView调整成不同大小,但不管大小如何Picasso只缓存一个全尺寸的。Glide则不同,它会为每种大小的ImageView缓存 一次。尽管一张图片已经缓存了一次,但是假如你要在另外一个地方再次以不同尺寸显示,需要重新下载,调整成新尺寸的大小,然后将这个尺寸的也缓存起来。

for example:假如在第一个页面有一个200x200的ImageView,在第二个页面有一个100x100的ImageView,这两个ImageView本来是要显示同一张图片,却需要下载两次。


4.缓存加载

默认情况下,Glide 会在开始一个新的图片请求之前检查以下多级的缓存:

(1)活动资源 (Active Resources) - 现在是否有另一个 View 正在展示这张图片?

(2)内存缓存 (Memory cache) - 该图片是否最近被加载过并仍存在于内存中?

(3)资源类型(Resource)- 该图片是否之前曾被解码、转换并写入过磁盘缓存?

(4)数据来源 (Data) - 构建这个图片的资源是否之前曾被写入过文件缓存?

前两步检查图片是否在内存中,如果是则直接返回图片;后两步则检查图片是否在磁盘上,以便快速但异步地返回图片。如果四个步骤都未能找到图片,则Glide会返回到原始资源以取回数据(原始文件,Uri, Url等)。


5.高级技巧

场景描述:

如果应用服务器给访问的图片URL加密,打个比方:在图片URL后面加上用户token参数,作为新的图片开放访问URL,而token作为一个验证身份的参数并不是一成不变的,很有可能时时刻刻都在变化。而如果token变了,那么图片的url也就跟着变了,图片url变了,缓存Key也就跟着变了。结果明明是同一张图片,就因为token不断在改变,导致Glide的缓存功能完全失效了。

解决方案:重写getCacheKey()方法:

public class MyGlideUrl extends GlideUrl {private String mUrl;public MyGlideUrl(String url) {super(url);mUrl = url;}@Overridepublic String getCacheKey() {return mUrl.replace(findTokenParam(), "");}private String findTokenParam() {String tokenParam = "";int tokenKeyIndex = mUrl.indexOf("?token=") >= 0 ? mUrl.indexOf("?token=") : mUrl.indexOf("&token=");if (tokenKeyIndex != -1) {int nextAndIndex = mUrl.indexOf("&", tokenKeyIndex + 1);if (nextAndIndex != -1) {tokenParam = mUrl.substring(tokenKeyIndex + 1, nextAndIndex + 1);} else {tokenParam = mUrl.substring(tokenKeyIndex);}}return tokenParam;}
}

在load()方法中传入这个自定义的MyGlideUrl对象,而不能再像之前那样直接传入url字符串了。不然的话Glide在内部还是会使用原始的GlideUrl类,而不是我们自定义的MyGlideUrl类。

Glide.with(this).load(new MyGlideUrl(url)).into(imageView);

基本用法


导入库:

Glide是一款由Bump Technologies开发的图片加载框架。GitHub:https://github.com/bumptech/glide 中文文档https://muyangmin.github.io/glide-docs-cn/

implementation 'com.github.bumptech.glide:glide:4.11.0'
// OkHttp3集成库
implementation "com.github.bumptech.glide:volley-integration:4.11.0"
// Volley集成库
implementation 'com.github.bumptech.glide:okhttp3-integration:4.11.0'
// RecyclerViewProvider集成库
implementation ("com.github.bumptech.glide:recyclerview-integration:4.11.0") {// Excludes the support library because it's already included by Glide.transitive = false
}
implementation 'com.github.bumptech.glide:annotations:4.11.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'

使用aar进行依赖:

implementation ("com.github.bumptech.glide:glide:4.11.0@aar") {transitive = true
}

混淆代码:

-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public class * extends com.bumptech.glide.module.AppGlideModule
-keep class com.bumptech.glide.GeneratedAppGlideModuleImpl
-keep public enum com.bumptech.glide.load.ImageHeaderParser$** {**[] $VALUES;public *;
}

Kotlin依赖:

dependencies {kapt 'com.github.bumptech.glide:compiler:4.11.0'
}

加载图片:

Glide.with(this).load(url).into(imageView);

1.占位图 / 错误图 / 后备回调图

占位图(placeholder):就是指在图片的加载过程中,我们先显示一张临时的图片,等图片加载出来了再替换成要加载的图片。

错误图(error):在请求永久性失败时展示;在请求的url 为null,且并没有设置 fallback Drawable 时展示。

后备回调图(fallback):在请求的url为 null 时展示。设计 fallback Drawable 的主要目的是允许用户传入 null 的情况。例如,一个 null 的个人资料 url 可能暗示这个用户没有设置头像,应该使用默认头像。默认情况下Glide将 null 作为错误处理,fallback允许用户在输入null情况下把fallback Drawable设置为默认图。

直接设置:

Glide.with(this).load(url).placeholder(R.drawable.placeholder).error(R.drawable.error).fallback(R.drawable.fallback).into(imageView);

RequestOptions

Glide.with(this).load(url).apply(new RequestOptions().placeholder(R.mipmap.img_placeholder).error(R.mipmap.img_error).fallback(R.drawable.img_fallback).fitCenter()).into(imageView);

在刚才的三步走之间插入了一个placeholder()方法,然后将占位图片的资源id传入到这个方法中。另外,这个占位图的用法其实也演示了Glide当中绝大多数API的用法,其实就是在load()和into()方法之间串接任意想添加的功能就可以了。

在加载错误后发起一个新的加载请求:

Glide.with(this).load(url).error(Glide.with(this).load(error_url)).into(view);

注意事项:

(1)占位图 / 错误图 / 后备回调图不是异步加载的

(2)占位图 / 错误图 / 后备回调图无法应用变换(解决方案是另外自定义View或者本地加入相应资源)

(3)不同的View上最好不要塞入同一个Drawable,应该使用资源ID或者Drawable.newDrawable进行资源拷贝


2.指定加载图片大小

场景:一张图片的尺寸是1000*1000像素,但是我们界面上的ImageView可能只有200*200像素,这个时候如果你不对图片进行任何压缩就直接读取到内存中,这就属于内存浪费了,因为程序中根本就用不到这么高像素的图片。

Glide会自动判断ImageView的大小,然后只将这么大的图片像素加载到内存当中,帮助我们节省内存开支。

如果需要强制指定加载的图片大小,可以使用如下方法:

Glide.with(this).load(url).override(100, 100).into(imageView);

override()方法指定了一个图片的尺寸,也就是说,Glide现在只会将图片加载成100*100像素的尺寸,而不会管你的ImageView的大小是多少了。


3.指定加载图片格式

Glide默认加载图片采用Drawable格式,用户可指定加载成Gif,Bitmap,Drawable,File等格式。

asGif:判断图片是否是gif格式,如果不是,则加载error图片;

asBitmap:对于gif / 视频文件,加载第一帧;

Glide.with(this).asGif() // asBitmap() asDrawable asFile().load(url).into(imageView);

4.加载Gif图片

(1)默认格式加载

Glide.with(this).load(url).into(imageView);

(2)gif格式加载:判断图片是否是gif格式,如果不是,则加载error图片

Glide.with(this).asGif().load(url).into(imageView);

(3)Bitmap格式加载:加载第一帧

Glide.with(this).asBitmap().load(url).into(imageView);

5.加载视频文件

加载视频的第一帧,缓存源文件到本地

String videoUrl = "http://gmp4.dwstatic.com/1901/6445176136.mp4";Glide.with(this).load(videoUrl).diskCacheStrategy(DiskCacheStrategy.RESOURCE).into(imgView);

6.RequestOptions

RequestOptions(请求选项)可以用于设置占位图 / 错误图 / 后备回调图,图片变换策略,缓存策略,编码配置等 ……

apply() 方法可以被调用多次,即实现多个RequestOptions组合效果。如果 不同RequestOptions 对象存在相互冲突的设置,以最后一个apply 的RequestOptions为准。

Glide.with(this).load(url).apply(new RequestOptions().placeholder(R.mipmap.img_placeholder).error(R.mipmap.img_error).fallback(R.drawable.img_fallback).diskCacheStrategy(DiskCacheStrategy.RESOURCE).format(DecodeFormat.PREFER_ARGB_8888).fitCenter()).into(imageView);

7.TransitionOptions

正常情况下,Glide先加载placeholder(占位图),等异步加载完资源图片后替换placeholder,替换的视觉效果突然且生硬,为了避免这种视觉上的突然性,可以使用TransitionOptions做淡入变换处理。

TransitionOptions 用于给一个特定的请求指定过渡。根据加载资源的类型,对应不同的TransitionOptions:BitmapTransitionOptions / DrawableTransitionOptions。对于 Bitmap 和 Drawable 之外的资源类型,可以使用 GenericTransitionOptions。

Android中的动画代价是比较大的,交叉淡入和其他涉及 alpha 变化的动画显得尤其昂贵。动画通常比图片解码本身还要耗时。请在使用 Glide 向 ListView , GridView, 或 RecyclerView 加载图片时考虑避免使用动画,尤其是大多数情况下,希望图片被尽快缓存和加载的时候。作为替代方案,请考虑预加载,这样当用户滑动到具体的 item 的时候,图片已经在内存中了。

Glide.with(this).load(url).transition(BitmapTransitionOptions.withCrossFade(200)).into(view);

Glide 的默认交叉淡入效果使用了 TransitionDrawable 。它提供两种动画模式,由 setCrossFadeEnabled() 控制。当交叉淡入被禁用时,正在过渡的图片会在原先显示的图像上面淡入。当交叉淡入被启用时,原先显示的图片会从不透明过渡到透明,而正在过渡的图片则会从透明变为不透明。

Glide4 中,默认禁用了交叉淡入。当占位符比实际加载的图片要大,或者图片部分为透明时,禁用交叉淡入会导致动画完成后占位符在图片后面仍然可见。如果你在加载透明图片时使用了占位符,你可以启用交叉淡入,具体办法是调整 DrawableCrossFadeFactory 里的参数并将结果传到 transition() 中:

DrawableCrossFadeFactory factory = new DrawableCrossFadeFactory.Builder().setCrossFadeEnabled(true).build();
GlideApp.with(this).load(url).transition(DrawableTransitionOptions.withCrossFade(factory)).diskCacheStrategy(DiskCacheStrategy.ALL).placeholder(R.color.placeholder).into(imageView);

Glide不支持多个请求间交叉淡入,解决方案:ViewSwitcher。

如果要改变你的 transition 的默认行为,以更好地控制它在不同的加载源(内存缓存,磁盘缓存,或uri)下是否被应用,可以检查一下 TransitionFactory 中传递给 build() 方法的那个 DataSource 。


8.缩略图

缩略图请求与主图请求并行启动,可以起到快速加载,减少等待的效果。

Glide.with(this).load(url).thumbnail(Glide.with(this).load(thumbnailUrl)).into(imageView);

强制在加载主图的时候,加载一个固定大小的缩略图:

Glide.with(this).load(url).thumbnail(Glide.with(this).load(url).override(100, 100)).into(imageView);

按原图的百分比加载一个缩略图:

Glide.with(this).load(url).thumbnail(0.5f).into(imageView);

9.重用 / 取消加载

重用:

Target target = Glide.with(this).load(url).into(view);
// 开启一个新的请求newUrl,目标target之前的所有请求将被取消,资源被释放
Glide.with(this).load(newUrl).into(target);

取消加载:

Glide.with(this).clear(target);
Glide.with(this).clear(view);

10.RecyclerView中使用Glide

首先,Glide 已经自动处理了 View 的复用和请求的取消。如果 url 为 null,Glide 会清空 View 的内容,或者显示 placeholder Drawable 或 fallback Drawable 的内容。

@Override
public void onBindViewHolder(ViewHolder holder, int position) {String url = urls.get(position);// Glide 已经自动处理了 View 的复用和请求的取消Glide.with(fragment).load(url).into(holder.imageView);
}

特殊场景:对于任何可复用的 View 或 Target ,如果它们在之前的位置上,用 Glide 进行过加载操作,然后在新的位置上需要设置特殊的图片,需要调用clear终止之前的加载行为;否则,就会出现这种情况,你已经为一个 view 设置好了一个 Drawable,但该 view 在之前的位置上使用 Glide 进行过加载图片的操作,Glide 加载完毕后可能会将这个 view 改回成原来的内容。

@Override
public void onBindViewHolder(ViewHolder holder, int position) {if (isImagePosition(position)) {String url = urls.get(position);Glide.with(fragment).load(url).into(holder.imageView);} else {Glide.with(fragment).clear(holder.imageView);holder.imageView.setImageDrawable(specialDrawable);}
}

11.RecyclerView中使用Glide预加载

用户滑动 RecyclerView 时自动加载稍微超前一些的图片,并配合使用正确的图片尺寸和高效率的磁盘缓存策略,达到显著减少用户滑动图片列表时看到的加载指示器的数量。

(1)创建一个 PreloadSizeProvider

(2)创建一个 PreloadModelProvider

(3)创建一个 RecyclerViewPreloader 并将你前两步创建的 PreloadSizeProvider 和 PreloadModelProvider 赋值进去

(4)将你的 RecyclerViewPreloader 添加到你的 RecyclerView 做为一个 scroll listener。

具体代码示例:https://github.com/bumptech/glide/tree/master/integration/recyclerview


回调监听


1.into(Target target)方法:

into()方法提供了一个接收Target参数的重载。即使我们传入的参数是ImageView,Glide也会在内部自动构建一个Target对象。只要能够掌握自定义Target技术的话,就可以更加随心所欲地控制Glide的回调了,目的是为了将Target作用在任意View上。

Target的继承结构还是相当复杂的,实现Target接口的子类非常多。大多数都是Glide已经实现好的具备完整功能的Target子类,如果我们要进行自定义的话,通常只需要自定义CustomViewTarget

public class TargetView extends LinearLayout {private CustomViewTarget viewTarget;public TargetView(Context context, AttributeSet attrs) {super(context, attrs);viewTarget = new CustomViewTarget(this) {@Overridepublic void onLoadFailed(@Nullable Drawable errorDrawable) { }@Overridepublic void onResourceReady(@NonNull Drawable resource, @Nullable Transition transition) {TargetView mLayout = getView();mLayout.setImageAsBackground(resource);}@Overrideprotected void onResourceCleared(@Nullable Drawable placeholder) { }};}public CustomViewTarget getTarget() {return viewTarget;}public void setImageAsBackground(Drawable resource) {setBackground(resource);}
}

TargetView的构造函数中,我们创建了一个CustomViewTarget的实例,并将TargetView当前的实例this传了进去。CustomViewTarget中需要指定两个泛型,一个是View的类型,一个图片的类型(Drawable或Bitmap)。然后在onResourceReady()方法中,我们就可以通过getView()方法获取到TargetView的实例,并调用它的任意接口了。比如说这里我们调用了setImageAsBackground()方法来将加载出来的图片作为TargetView布局的背景图。

实际Activity中调用TargetView中的Target:

Glide.with(this).load(url).diskCacheStrategy(DiskCacheStrategy.NONE).into(targetView.getTarget());

2.preload()方法:

Glide专门给我们提供了预加载的接口,也就是preload()方法,需要时直接使用就可以了。preload()方法有两个方法重载,一个不带参数,表示将会加载图片的原始尺寸,另一个可以通过参数指定加载图片的宽和高。preload()方法的用法也非常简单,直接使用它来替换into()方法即可,如下所示:

Glide.with(this).load("http://baidu.com").diskCacheStrategy(DiskCacheStrategy. SOURCE).preload();

如果使用了preload()方法,最好要将diskCacheStrategy的缓存策略指定成DiskCacheStrategy.SOURCE。因为preload()方法默认是预加载的原始图片大小,而into()方法则默认会根据ImageView控件的大小来动态决定加载图片的大小。因此,如果不将diskCacheStrategy的缓存策略指定成DiskCacheStrategy.SOURCE的话,很容易会造成我们在预加载完成之后再使用into()方法加载图片,却仍然还是要从网络上去请求图片这种现象。

Glide.with(this).load("http://baidu.com").diskCacheStrategy(DiskCacheStrategy.ALL).into(imageView);

这里仍然需要将硬盘缓存策略指定成DiskCacheStrategy.SOURCE,以保证Glide一定会去读取刚才预加载的图片缓存。


3.downloadOnly()方法:(待完善……!)


4.listener()方法:

listener()方法的作用非常普遍,它可以用来监听Glide加载图片的状态。举个例子,比如说刚才使用了preload()方法来对图片进行预加载,但是怎样确定预加载有没有完成呢?还有如果Glide加载图片失败了,该怎样调试错误的原因呢?都在listener()方法当中。

listener()是结合into()方法一起使用的,当然也可以结合preload()方法一起使用。最基本的用法如下所示:

public void loadImage(ImageView view) {String url = "http://cn.bing.com/az/hprichbg/rb/TOAD_ZH-CN7336795473_1920x1080.jpg";Glide.with(this).load(url).listener(new RequestListener() {@Overridepublic boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) {return false;}@Overridepublic boolean onResourceReady(Drawable resource, Object model, Target target, DataSource dataSource, boolean isFirstResource) {return false;}}).into(view);

我们在into()方法之前串接了一个listener()方法,然后实现了一个RequestListener的实例。其中RequestListener需要实现两个方法,一个onResourceReady()方法,一个onLoadFailed()方法。从方法名上就可以看出来了,当图片加载完成的时候就会回调onResourceReady()方法,而当图片加载失败的时候就会回调onLoadFailed()方法,onLoadFailed()方法中会将失败的Exception参数传进来,这样我们就可以定位具体失败的原因了。

onResourceReady()方法和onLoadFailed()方法都有一个布尔值的返回值,返回false就表示这个事件没有被处理,还会继续向下传递,返回true就表示这个事件已经被处理掉了,从而不会再继续向下传递。举个简单点的例子,如果我们在RequestListener的onResourceReady()方法中返回了true,那么就不会再回调Target的onResourceReady()方法了。


图片变换

问题场景:

在1920*1080手机屏幕上给一个宽高设置为wrap_content的ImageView加载一张540*258的图片,结果图片放大了,why?ImageView有scaleType这个属性,在没有指定scaleType属性的情况下,ImageView默认的scaleType是FIT_CENTER,接下来看一下into()方法:

public Target into(ImageView view) {Util.assertMainThread();if (view == null) {throw new IllegalArgumentException("You must pass in a non null View");}if (!isTransformationSet && view.getScaleType() != null) {switch (view.getScaleType()) {case CENTER_CROP:applyCenterCrop();break;case FIT_CENTER:case FIT_START:case FIT_END:applyFitCenter();break;//$CASES-OMITTED$default:// Do nothing.}}return into(glide.buildImageViewTarget(view, transcodeClass));
}

如果ImageView的scaleType是CENTER_CROP,则会去调用applyCenterCrop()方法,如果scaleType是FIT_CENTER、FIT_START或FIT_END,则会去调用applyFitCenter()方法。这里的applyCenterCrop()和applyFitCenter()方法其实就是向Glide的加载流程中添加了一个图片变换操作。由于ImageView默认的scaleType是FIT_CENTER,因此会自动添加一个FitCenter的图片变换,而在这个图片变换过程中做了某些操作,导致图片充满了全屏。

解决方案:

Glide提供了专门的API来添加和取消图片变换,想要解决这个问题只需要使用如下代码即可:

Glide.with(this).load("http://baidu.com").dontTransform().into(imageView);

dontTransform()方法,表示让Glide在加载图片的过程中不进行图片变换,这样刚才调用的applyCenterCrop()、applyFitCenter()就统统无效了。

但是使用dontTransform()方法存在着一个问题,就是调用这个方法之后,所有的图片变换操作就全部失效了,那如果有一些图片变换操作是必须要执行的该怎么办呢?这种情况下只需要借助override()方法强制将图片尺寸指定成原始大小就可以了,代码如下所示:

Glide.with(this).load(url).override(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL).into(imageView);

通过override()方法将图片的宽和高都指定成Target.SIZE_ORIGINAL,问题同样被解决了。程序的最终运行结果和上图是完全一样的。


1.图片变换的基本用法

Glide从加载了原始图片到最终展示给用户之前,又进行了一些变换处理,从而能够实现一些更加丰富的图片效果,如图片圆角化、圆形化、模糊化等等。添加图片变换的用法非常简单,我们只需要调用transform()方法,并将想要执行的图片变换操作作为参数传入transform()方法即可,如下所示:

Glide.with(this).load(url).transform(...).into(imageView);

至于具体要进行什么样的图片变换操作,这个通常都是需要自己来写的。不过Glide已经内置了两种图片变换操作,可以直接拿来使用,一个是CenterCrop,一个是FitCenter。但这两种内置的图片变换操作其实都不需要使用transform()方法,Glide为了方便我们使用直接提供了现成的API:

Glide.with(this).load(url).centerCrop().into(imageView);
Glide.with(this).load(url).fitCenter().into(imageView);

centerCrop()和fitCenter()方法其实也只是对transform()方法进行了一层封装而已,它们背后的源码仍然还是借助transform()方法来实现的。


2.图片变换的自定义:

Glide定制好了一个图片变换的框架,大致的流程是我们可以获取到原始的图片,然后对图片进行变换,再将变换完成后的图片返回给Glide,最终由Glide将图片显示出来。理论上,在对图片进行变换这个步骤中我们可以进行任何的操作,你想对图片怎么样都可以。包括圆角化、圆形化、黑白化、模糊化等等,甚至你将原图片完全替换成另外一张图都是可以的。

DEMO:对图片进行圆形化变换:

自定义一个类让它继承自BitmapTransformation ,然后重写transform()方法,并在这里去实现具体的图片变换逻辑就可以了:

public class CenterCrop extends BitmapTransformation {@Overrideprotected Bitmap transform(@NonNull BitmapPool pool, @NonNull Bitmap toTransform, int outWidth, int outHeight) {int diameter = Math.min(toTransform.getWidth(), toTransform.getHeight());final Bitmap toReuse = pool.get(outWidth, outHeight, Bitmap.Config.ARGB_8888);final Bitmap result;result = toReuse;int dx = (toTransform.getWidth() - diameter) / 2;int dy = (toTransform.getHeight() - diameter) / 2;Canvas canvas = new Canvas(result);Paint paint = new Paint();BitmapShader shader = new BitmapShader(toTransform, BitmapShader.TileMode.CLAMP,BitmapShader.TileMode.CLAMP);if (dx != 0 || dy != 0) {Matrix matrix = new Matrix();matrix.setTranslate(-dx, -dy);shader.setLocalMatrix(matrix);}paint.setShader(shader);paint.setAntiAlias(true);float radius = diameter / 2f;canvas.drawCircle(radius, radius, radius, paint);pool.put(toReuse);toReuse.recycle();return result;}@Overridepublic void updateDiskCacheKey(@NonNull MessageDigest messageDigest) {}
}

继承BitmapTransformation有一个限制,就是只能对静态图进行图片变换

首先先算出原图宽度和高度中较小的值,因为对图片进行圆形化变换肯定要以较小的那个值作为直径来进行裁剪。之后从Bitmap缓存池中尝试获取一个Bitmap对象来进行重用,如果没有可重用的Bitmap对象的话就创建一个。再具体进行圆形化变换的部分,这里算出了画布的偏移值,并且根据刚才得到的直径算出半径来进行画圆。最后,尝试将复用的Bitmap对象重新放回到缓存池当中,并将圆形化变换后的Bitmap对象进行返回。

这样,一个自定义图片变换的功能就写好了。使用方法非常简单,就是把这个自定义图片变换的实例传入到transform()方法中即可,如下所示:

Glide.with(this).load("http://baidu.com").transform(new CenterCrop()).into(imageView);

3.图片变换开源库(重点推荐):

针对Glide的图片变换,推荐开源库:glide-transformationshttps://github.com/wasabeef/glide-transformations

实现了很多通用的图片变换效果,如裁剪变换、颜色变换、模糊变换等等,使得我们可以非常轻松地进行各种各样的图片变换。

implementation 'jp.wasabeef:glide-transformations:4.3.0'
// If you want to use the GPU Filters
implementation 'jp.co.cyberagent.android:gpuimage:2.1.0'

(1)模糊化处理:glide-transformations库中的BlurTransformation类,代码如下所示:

Glide.with(this).load(R.drawable.demo).apply(RequestOptions.bitmapTransform(new BlurTransformation(25, 3))).into(imageView);

注意这里我们调用的是bitmapTransform()方法而不是transform()方法,因为glide-transformations库都是专门针对静态图片变换来进行设计的。

(2)图片黑白化:glide-transformations库中的GrayscaleTransformation类,代码如下所示:

Glide.with(this).load(R.drawable.demo).apply(RequestOptions.bitmapTransform(new GrayscaleTransformation ())).into(imageView);

(3)同时执行模糊化和圆角的变换:

Glide.with(this).load(R.drawable.demo).apply(RequestOptions.bitmapTransform(new MultiTransformation(new BlurTransformation(25),new RoundedCornersTransformation(128, 0, RoundedCornersTransformation.CornerType.BOTTOM)))).into(imageView);

自定义配置

自定义模块功能可以将更改Glide配置,替换Glide组件等操作独立出来,使得我们能轻松地对Glide的各种配置进行自定义,并且又和Glide的图片加载逻辑没有任何交集,这也是一种低耦合编程方式的体现。

(1)自定义AppGlideModule

(2)自定义一个或多个 LibraryGlideModule

(3)给上述两种实现添加 @GlideModule 注解

(4)添加对 Glide 的注解解析器的依赖。

(5)在 proguard 中,添加对 AppGlideModules 的 keep


1.AppGlideModule(用于Application)

自定义一个我们自己的模块类,并让它继承抽象父类AppGlideModule:

class MyGlideModule extends AppGlideModule {@Overridepublic void applyOptions(@NonNull Context context, @NonNull GlideBuilder builder) {super.applyOptions(context, builder);}@Overridepublic void registerComponents(@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) {super.registerComponents(context, glide, registry);}
}

我们重写了applyOptions()registerComponents() 方法,这两个方法分别就是用来更改Glide配置以及替换Glide组件的。只需要在这两个方法中加入具体的逻辑,就能实现更改Glide配置或者替换Glide组件的功能了。


2.LibraryGlideModule(用于Library)


3.变更Glide配置

AppGlideModule.applyOptions():

(1)设置内存缓存

默认配置是LruResourceCache

方案一:使用MemorySizeCalculator:这个类主要关注设备的内存类型,设备 RAM 大小,以及屏幕分辨率。

@Override
public void applyOptions(@NonNull Context context, @NonNull GlideBuilder builder) {super.applyOptions(context, builder);MemorySizeCalculator calculator = new MemorySizeCalculator.Builder(context).setMemoryCacheScreens(2).build();builder.setMemoryCache(new LruResourceCache(calculator.getMemoryCacheSize()));
}

方案二:直接设置

@Override
public void applyOptions(@NonNull Context context, @NonNull GlideBuilder builder) {super.applyOptions(context, builder);builder.setMemoryCache(new LruResourceCache(MEMORY_CACHE_SIZE));
}

(2)设置Bitmap池

Glide 使用 LruBitmapPool 作为默认的 BitmapPool,LruBitmapPool 是一个内存中的固定大小的 BitmapPool,使用 LRU 算法清理。默认大小基于设备的分辨率和密度,具体的计算通过 Glide 的 MemorySizeCalculator 来完成,与 Glide 的 MemoryCache 的大小检测方法相似。

方案一:使用MemorySizeCalculator:这个类主要关注设备的内存类型,设备 RAM 大小,以及屏幕分辨率。

@Override
public void applyOptions(@NonNull Context context, @NonNull GlideBuilder builder) {super.applyOptions(context, builder);MemorySizeCalculator calculator = new MemorySizeCalculator.Builder(context).setBitmapPoolScreens(2).build();builder.setBitmapPool(new LruBitmapPool(calculator.getMemoryCacheSize()));
}

方案二:直接设置

@Override
public void applyOptions(@NonNull Context context, @NonNull GlideBuilder builder) {super.applyOptions(context, builder);builder.setBitmapPool(new LruBitmapPool(BITMAP_CACHE_SIZE));}

(3)设置磁盘缓存

Glide 使用 DiskLruCacheWrapper 作为默认的磁盘缓存。DiskLruCacheWrapper 是一个使用 LRU 算法的固定大小的磁盘缓存。默认磁盘大小为 250 MB。

Glide默认的硬盘缓存策略使用的是InternalCacheDiskCacheFactory,这种缓存会将所有Glide加载的图片都存储到当前应用的私有目录下。这是一种非常安全的做法,但同时这种做法也造成了一些不便,因为私有目录下即使是开发者自己也是无法查看的,如果想要去验证一下图片到底有没有成功缓存下来,这就有点不太好办了。

Glide 提供ExternalPreferredCacheDiskCacheFactory可以将所有Glide加载的图片都缓存到外部磁盘上。

另外,InternalCacheDiskCacheFactory和ExternalPreferredCacheDiskCacheFactory的默认硬盘缓存大小都是250M。也就是说,如果你的应用缓存的图片总大小超出了250M,那么Glide就会按照DiskLruCache算法的原则来清理缓存的图片。ExternalPreferredCacheDiskCacheFactory默认缓存路径是在sdcard/Android/包名/cache/image_manager_disk_cache目录当中,其中journal文件是DiskLruCache算法的日志文件,这个文件必不可少,且只会有一个。

所以说,也是可以对这个默认的缓存大小进行修改的,而且修改方式非常简单,只需要向ExternalPreferredCacheDiskCacheFactory或者InternalCacheDiskCacheFactory再传入一个参数就可以了,现在我们就将Glide硬盘缓存的大小调整成了500M。

public static final int DISK_CACHE_SIZE = 500 * 1024 * 1024;@Override
public void applyOptions(@NonNull Context context, @NonNull GlideBuilder builder) {super.applyOptions(context, builder);builder.setDiskCache(new ExternalPreferredCacheDiskCacheFactory(context, DISK_CACHE_SIZE));
}

(4)设置默认Options

builder.setDefaultRequestOptions(new RequestOptions().format(DecodeFormat.PREFER_RGB_565));

如果某个具体请求通过apply RequestOptions对该请求设置请求选项,会覆盖默认的Options。

(5)设置Log Level

builder.setLogLevel(Log.ERROR);


4.替换Glide组件

AppGlideModule. registerComponents ()。

组件类型:

ModelLoader:用于加载自定义的 Model(Url, Uri,任意的 POJO ) 和 Data(InputStreams, FileDescriptors)。

ResourceDecoder:用于对新的 Resources(Drawables, Bitmaps) 或新的 Data 类型(InputStreams, FileDescriptors)进行解码。

Encoder:用于向 Glide 的磁盘缓存写 Data (InputStreams, FileDesciptors)。

ResourceTranscoder:用于在不同的资源类型之间做转换,例如,从 BitmapResource 转换为 DrawableResource 。

ResourceEncoder:用于向 Glide 的磁盘缓存写 Resources(BitmapResource, DrawableResource)。

组件通过 Registry 类来注册。例如,添加一个 ModelLoader,使其能从自定义的Model对象中创建一个InputStream:

数据加载:

1)模型(Model) -> 数据(Data) (由模型加载器 ModelLoader 处理)

2)数据(Data) -> 资源(Resource) (由资源解析器 ResourceDecoder 处理)

3)资源(Resource) -> 转码后的资源(Transcoded Resource) (可选;由资源转码器 ResourceTranscoder 处理)

编码器(Encoder)可以在步骤2之前往Glide的磁盘缓存中写入数据;资源编码器(ResourceEncoder)可以在步骤3之前往Glide的磁盘缓存写入资源。

替换组件:

默认情况下,Glide使用的是基于原生HttpURLConnection进行订制的HTTP通讯组件,但是现在大多数的Android开发者都更喜欢使用OkHttp:

@Override
public void registerComponents(@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) {super.registerComponents(context, glide, registry);registry.replace(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory());
}

OkHttp3 集成库:implementation 'com.github.bumptech.glide:okhttp3-integration:4.11.0'

Volley 集成库:implementation "com.github.bumptech.glide:volley-integration:4.11.0"


5.依赖 Glide 注解解析器

implementation 'com.github.bumptech.glide:annotations:4.11.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'

6.添加混淆

-keep public class * extends com.bumptech.glide.module.AppGlideModule
-keep class com.bumptech.glide.GeneratedAppGlideModuleImpl

7.Glide v4禁用清单解析:

Glide v3版本以前,如果想要让自定义GlideModule生效,还得在AndroidManifest.xml文件当中加入如下配置才行:

标签中加入一个meta-data配置项,其中android:name指定成自定义的MyGlideModule的完整路径,android:value必须指定成GlideModule,这个是固定值。

Glide v4 的 AppGlideModule 和 LibraryGlideModule,你可以完全禁用清单解析。这样可以改善 Glide 的初始启动时间,并避免尝试解析元数据时的一些潜在问题。

@Override
public boolean isManifestParsingEnabled() {// 如果依赖中使用了Glide的 annotation processor,则需要在AppGlideModule实现类里返回falsereturn false;
}

8.依赖冲突

AppGlideModule 实现是一个信号,它会让 Glide 的注解解析器生成一个单一的所有已发现的 LibraryGlideModules 的联合类。对于一个特定的应用,只能存在一个 AppGlideModule。避免在Library中使用AppGlideModule,否则将会阻止依赖该Library的任何App对Glide进行配置诸如 Glide 缓存大小和位置之类的选项。如果两个程序Library都包含 AppGlideModule,App将无法在同时依赖两个Library的情况下通过编译。

使用@Excludes(排除)解决一个AppGlideModule包含多个LibraryGlideModule情况下,LibraryGlideModule之间组件冲突的问题。

@Excludes({com.example.DemoLibraryGlideModule1, com.example.DemoLibraryGlideModule2})
@GlideModule
public class MyGlideModule extends AppGlideModule {

 


推荐阅读
  • EPICS Archiver Appliance存储waveform记录的尝试及资源需求分析
    本文介绍了EPICS Archiver Appliance存储waveform记录的尝试过程,并分析了其所需的资源容量。通过解决错误提示和调整内存大小,成功存储了波形数据。然后,讨论了储存环逐束团信号的意义,以及通过记录多圈的束团信号进行参数分析的可能性。波形数据的存储需求巨大,每天需要近250G,一年需要90T。然而,储存环逐束团信号具有重要意义,可以揭示出每个束团的纵向振荡频率和模式。 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • 本文介绍了Android 7的学习笔记总结,包括最新的移动架构视频、大厂安卓面试真题和项目实战源码讲义。同时还分享了开源的完整内容,并提醒读者在使用FileProvider适配时要注意不同模块的AndroidManfiest.xml中配置的xml文件名必须不同,否则会出现问题。 ... [详细]
  • 本文介绍了在CentOS上安装Python2.7.2的详细步骤,包括下载、解压、编译和安装等操作。同时提供了一些注意事项,以及测试安装是否成功的方法。 ... [详细]
  • Windows7 64位系统安装PLSQL Developer的步骤和注意事项
    本文介绍了在Windows7 64位系统上安装PLSQL Developer的步骤和注意事项。首先下载并安装PLSQL Developer,注意不要安装在默认目录下。然后下载Windows 32位的oracle instant client,并解压到指定路径。最后,按照自己的喜好对解压后的文件进行命名和压缩。 ... [详细]
  • 海马s5近光灯能否直接更换为H7?
    本文主要介绍了海马s5车型的近光灯是否可以直接更换为H7灯泡,并提供了完整的教程下载地址。此外,还详细讲解了DSP功能函数中的数据拷贝、数据填充和浮点数转换为定点数的相关内容。 ... [详细]
  • 本文介绍了在CentOS 6.4系统中更新源地址的方法,包括备份现有源文件、下载163源、修改文件名、更新列表和系统,并提供了相应的命令。 ... [详细]
  • Nginx使用AWStats日志分析的步骤及注意事项
    本文介绍了在Centos7操作系统上使用Nginx和AWStats进行日志分析的步骤和注意事项。通过AWStats可以统计网站的访问量、IP地址、操作系统、浏览器等信息,并提供精确到每月、每日、每小时的数据。在部署AWStats之前需要确认服务器上已经安装了Perl环境,并进行DNS解析。 ... [详细]
  • 阿里Treebased Deep Match(TDM) 学习笔记及技术发展回顾
    本文介绍了阿里Treebased Deep Match(TDM)的学习笔记,同时回顾了工业界技术发展的几代演进。从基于统计的启发式规则方法到基于内积模型的向量检索方法,再到引入复杂深度学习模型的下一代匹配技术。文章详细解释了基于统计的启发式规则方法和基于内积模型的向量检索方法的原理和应用,并介绍了TDM的背景和优势。最后,文章提到了向量距离和基于向量聚类的索引结构对于加速匹配效率的作用。本文对于理解TDM的学习过程和了解匹配技术的发展具有重要意义。 ... [详细]
  • 基于PgpoolII的PostgreSQL集群安装与配置教程
    本文介绍了基于PgpoolII的PostgreSQL集群的安装与配置教程。Pgpool-II是一个位于PostgreSQL服务器和PostgreSQL数据库客户端之间的中间件,提供了连接池、复制、负载均衡、缓存、看门狗、限制链接等功能,可以用于搭建高可用的PostgreSQL集群。文章详细介绍了通过yum安装Pgpool-II的步骤,并提供了相关的官方参考地址。 ... [详细]
  • 本文详细介绍了如何使用MySQL来显示SQL语句的执行时间,并通过MySQL Query Profiler获取CPU和内存使用量以及系统锁和表锁的时间。同时介绍了效能分析的三种方法:瓶颈分析、工作负载分析和基于比率的分析。 ... [详细]
  • 本文讨论了如何使用Web.Config进行自定义配置节的配置转换。作者提到,他将msbuild设置为详细模式,但转换却忽略了带有替换转换的自定义部分的存在。 ... [详细]
  • 本文介绍了使用Spark实现低配版高斯朴素贝叶斯模型的原因和原理。随着数据量的增大,单机上运行高斯朴素贝叶斯模型会变得很慢,因此考虑使用Spark来加速运行。然而,Spark的MLlib并没有实现高斯朴素贝叶斯模型,因此需要自己动手实现。文章还介绍了朴素贝叶斯的原理和公式,并对具有多个特征和类别的模型进行了讨论。最后,作者总结了实现低配版高斯朴素贝叶斯模型的步骤。 ... [详细]
author-avatar
萧俊瑄俊明纬伦
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有