开发app之第三方框架的使用

一、简介

在gitHub上你会发现有很多带有针对性处理优化的一些开源项目, 比如请求网络优化、 加载网络图片优化

等, 并且这些开源的项目都提供了依赖库可以使第三方app集成使用, 这些就属于第三方框架。

二、框架分类

针对开发app中的不同环节, 我们可以根据自己的需要来使用整套的或者单个第三方框架使用; 下面介绍几种

比较有些名气的第三方框架:

1,XUtils

项目git地址:https://github.com/wyouflf/xUtils

XUtils是一个整套第三方框架;xUtils包含了很多实用的android工具, 主要四大模块分别为DbUtils模块、

ViewUtils模块、HttpUtils模块以及BitmapUtils模块;

DbUtils模块:

android中的orm( 依赖注入) 框架, 一行代码就可以进行增删改查;

支持事务, 默认关闭;

可通过注解自定义表名, 列名, 外键, 唯一性约束,NOT NULL约束,CHECK约束等( 需要混

淆的时候请注解表名和列名) ;

支持绑定外键, 保存实体时外键关联实体自动保存或更新;

自动加载外键关联实体, 支持延时加载;

支持链式表达查询, 更直观的查询语义, 参考下面的介绍或sample中的例子。

ViewUtils模块:

android中的ioc框架, 完全注解方式就可以进行UI绑定和事件绑定;

新的事件绑定方式, 使用混淆工具混淆后仍可正常工作;

目前支持常用的11种事件绑定, 参见ViewCommonEventListener类和包

com.lidroid.xutils.view.annotation.event。

HttpUtils模块:

支持同步, 异步方式的请求;

支持大文件上传, 上传大文件不会oom;

支持GET,POST,PUT,MOVE,COPY,DELETE,HEAD请求;

下载支持301/302重定向, 支持设置是否根据Content-Disposition重命名下载的文件;

返回文本内容的GET请求支持缓存, 可设置默认过期时间和针对当前请求的过期时间。

BitmapUtils模块:

加载bitmap的时候无需考虑bitmap加载过程中出现的oom和android容器快速滑动时候出现的

图片错位等现象;

支持加载网络图片和本地图片;

内存管理使用lru算法, 更好的管理bitmap内存;

可配置线程加载线程数量, 缓存大小, 缓存路径, 加载显示动画等...

但是据反映使用BitmapUtils有时候会出现图片加载不出情况, 或者还是出现小几率的OutofMerry, 只能期

待xUtils继续更新优化了;

2,Volley

项目git地址:https://github.com/mcxiaoke/android-volley

Volley是2013年Google I/O大会上推出了一个新的网络通信框架;Volley可是说是把AsyncHttpClient(也

是一个网络封装框架)和Universal-Image-Loader(之前特别出名的一个加载网络图片的框架, 下面会介绍)的优

点集于了一身, 既可以像AsyncHttpClient一样非常简单地进行HTTP通信, 也可以像Universal-ImageLoader一样轻松加载网络上的图片; 它的设计目标就是非常适合去进行数据量不大, 但通信频繁的网络操作, 而

对于大数据量的网络操作, 比如说下载文件等,Volley的表现就会非常糟糕。

Volley 的优点:

自动调度网络请求;

高并发网络连接;

通过标准的HTTP cache coherence( 高速缓存一致性) 缓存磁盘和内存透明的响应;

支持指定请求的优先级;

网络请求cancel机制。 我们可以取消单个请求, 或者指定取消请求队列中的一个区域;

框架容易被定制, 例如, 定制重试或者回退功能;

包含了调试与追踪工具;

请求池, 连接池,Network池

Volley除了支持大数据下载的缺点外, 还有一个就是Volley默认不支持HTTPS, 因此如果使用Volley框架

在使用HTTPS请求的接口必须自己进行证书信任相关处理;

Https跟http区别:

https相对于http安全, ( 通过证书里面的密钥进行请求加密)

volley框架内也有对加载网络图片进行处理的ImageLoader,NetworkImagerView,setImagUrl("图片

地址");

3,Universal-Image-Loader

项目git地址:https://github.com/nostra13/Android-Universal-Image-Loader

Universal-Image-Loader是之前很多开发者最常用的图片异步加载、 缓存的开源框架之一, 由于其对加载图片

内存优化处理非常好, 所以很多app都可以发现它的影子;

原理图:

ImageLoad的使用:(1),下载git项目或者jar包;(2),AndroidManifest中配置权限;

(3),在Application中初始化;

public static voidinitImageLoader(Context context) {

ImageLoaderConfiguration.Builder config =newImageLoaderConfiguration.Builder(context);

config.threadPriority(Thread.NORM_PRIORITY-2);//设置线程池线程数

config.denyCacheImageMultipleSizesInMemory();//清空cache缓存

config.diskCacheFileNameGenerator(newMd5FileNameGenerator());//存储文件名md5加密

config.diskCacheSize(50*1024*1024);//硬盘最大缓存为50 MiB

config.tasksProcessingOrder(QueueProcessingType.LIFO);//线程为后进先出型

config.writeDebugLogs();// Remove for release app

// Initialize ImageLoader with configuration.

ImageLoader.getInstance().init(config.build());

}(4),加载网络图片;

ImageLoader imageLoader = ImageLoader.getInstance();

image

Loader.displayImage("http://pic.tuanche.com/ams/20160215/14555012465214662.png",img_test);(5),加载本地图片;

ImageLoader.getInstance().loadImage("本地路径",newSimpleImageLoadingListener() {

@Override

public voidonLoadingComplete(String imageUri, View view, Bitmap loadedImage) {

}@

Override

public voidonLoadingFailed(String imageUri, View view, FailReason failReason) {

}

});

常用的两种框架介绍(图片处理和网络请求框架)

4,Fresco(也是我们项目中要使用的)

项目git地址:https://github.com/facebook/fresco

Fresco是facebook推出的一款强大的图片加载的框架; 主要有Image Pipeline和Drawees两大模块;

Image Pipeline模块:

Fresco中设计有一个叫做Image Pipeline的模块。 它负责从网络, 从本地文件系统, 本地资源加载图

片。 为了最大限度节省空间和CPU时间, 它含有3级缓存设计(2级内存,1级磁盘) 。

Drawees模块:

Fresco中设计有一个叫做Drawees模块, 它会在图片加载完成前显示占位图, 加载成功后自动替换为目标

图片。 当图片不再显示在屏幕上时, 它会及时地释放内存和空间占用。

Fresco特性

(1)内存管理

解压后的图片, 即Android中的Bitmap, 占用大量的内存。 大的内存占用势必引发更加频繁的GC。 在5.0以下,GC将会

显著地引发界面卡顿。

在5.0以下系统,Fresco将图片放到一个特别的内存区域。 当然, 在图片不显示的时候, 占用的内存会自动被释放。 这

会使得APP更加流畅, 减少因图片内存占用而引发的OOM。

(2)图片加载

Fresco的Image Pipeline允许你用很多种方式来自定义图片加载过程, 比如:

为同一个图片指定不同的远程路径, 或者使用已经存在本地缓存中的图片

先显示一个低清晰度的图片, 等高清图下载完之后再显示高清图

加载完成回调通知

对于本地图, 如有EXIF缩略图, 在大图加载完成之前, 可先显示缩略图

缩放或者旋转图片

对已下载的图片再次处理

(3)图片绘制

Fresco的Drawees设计, 带来一些有用的特性:

自定义居中焦点

圆角图, 当然圆圈也行

下载失败之后, 点击重现下载

自定义占位图, 自定义overlay,或者进度条

指定用户按压时的overlay

(4)图片的渐进式呈现

渐进式的JPEG图片格式已经流行数年了, 渐进式图片格式先呈现大致的图片轮廓, 然后随着图片下载的继续, 呈现

逐渐清晰的图片, 这对于移动设备, 尤其是慢网络有极大的利好, 可带来更好的用户体验。

Android本身的图片库不支持此格式, 但是Fresco支持。 使用时, 和往常一样, 仅仅需要提供一个图片的URI即可, 剩

下的事情,Fresco会处理。

(5)支持gif等动态图加载;

Fresco的使用:(1),gitHub下载依赖库或者jar包;Anroid Studio可以通过compile方式加载:

compile'com.facebook.fresco:fresco:0.8.1'

(2),添加网络权限;

(3),在Application中初始化Fresco;Fresco.initialize(this);

(3),layout布局文件中声明控件;

android:id="@+id/image_view"

android:layout_width="300dp"

android:layout_height="300dp"

fresco:placeholderImage="@mipmap/ic_launcher"/>(4),加载网络图片

Uriuri =Uri.parse("http://pic1.nipic.com/2008-09-08/200898163242920_2.jpg");

image_view.setImageURI(uri);

注: 加载本地图片把Url地址改成本地地址即可;

当然上面提到的SimpleDraweeView只是Drawee其中的控件, 没有什么很特别的需求使用它就够了, 下面贴一下它里面的一

些属性:

android:id="@+id/image_view"

android:layout_width="300dp"

android:layout_height="300dp"

fresco:fadeDuration="300"

fresco:actualImageScaleType="focusCrop"

fresco:placeholderImage="@color/wait_color"

fresco:placeholderImageScaleType="fitCenter"

fresco:failureImage="@drawable/error"

fresco:failureImageScaleType="centerInside"

fresco:retryImage="@drawable/retrying"

fresco:retryImageScaleType="centerCrop"

fresco:progressBarImage="@drawable/progress_bar"

fresco:progressBarImageScaleType="centerInside"

fresco:progressBarAutoRotateInterval="1000"

fresco:backgroundImage="@color/blue"

fresco:overlayImage="@drawable/watermark"

fresco:pressedStateOverlayImage="@color/red"

fresco:roundAsCircle="false"

fresco:roundedCornerRadius="1dp"

fresco:roundTopLeft="true"

fresco:roundTopRight="false"

fresco:roundBottomLeft="false"

fresco:roundBottomRight="true"

fresco:roundWithOverlayColor="@color/corner_color"

fresco:roundingBorderWidth="2dp"

fresco:roundingBorderColor="@color/border_color"

/>

简单介绍一下上面的属性:

placeholderImage就是所谓的展位图啦, 在图片没有加载出来之前你看到的就是它

failureIamge看到名字就知道是什么了, 图片加载失败时显示的图片就是它了

retryImage图片加载失败时显示, 提示用户点击重新加载, 重复加载4次还是没有加载出来的时候才会显示failureImage的图片

progressBarImage进度条图片

backgroundImage背景图片, 这里的背景图片首先被绘制

overlayImage设置叠加图, 在xml中只能设置一张叠加图片, 如果需要多张图片的话, 需要在java代码中设置哦

pressedStateOverlayImage设置点击状态下的叠加图, 此叠加图不能缩放

ImageScaleType这个就是各种各样的图片缩放样式了,center,centerCrop,fouseCrop,centerInside,fitCenter,fitStart,fitEnd,fitXY

剩下的属性就是对圆角的处理了;

5,Okhttp(也是我们项目中要使用的)

项目git地址:https://github.com/square/okhttp

其他下载地址:https://github.com/hongyangAndroid/okhttp-utils

Okhttp是square公司提供的一个高效、 请求快速、 多功能的开源网络框架;

Okhttp支持SPDY协议(SPDY协议在性能上对HTTP做了很大的优化) ;

Okhttp主要包含如下:

一般的get请求

一般的post请求

基于Http Post的文件上传( 类似表单)

文件下载/加载图片

上传下载的进度回调

支持取消某个请求

支持自定义Callback

支持HEAD、DELETE、PATCH、PUT(请求方式)

支持session的保持(Session主要维持用户登录状态)

支持自签名网站https的访问, 提供方法设置下证书就行

Okhttp的使用:(1),加载Okhttp依赖库;

compile'com.squareup.okhttp3:okhttp:3.4.1'(2),在Application中配置生成OkhttpClient;

OkHttpClient okHttpClient= newOkHttpClient.Builder()

// .addInterceptor(new LoggerInterceptor("TAG"))

.connectTimeout(10000L, TimeUnit.MILLISECONDS)

.readTimeout(10000L, TimeUnit.MILLISECONDS)

//其他配置

.build();

OkHttpUtils.initClient(okHttpClient);(3),GET请求

String url="http://www.csdn.net/";

OkHttpUtils

.get()

.url(url)

.addParams("username","hyman")

.addParams("password","123")

.build()

.execute(newStringCallback()

{

@Override

public voidonError(Request request, Exception e)

{ } @

Override

public voidonResponse(String response)

{ }

});

POST请求:

OkHttpUtils

.post()

.url(url)

.addParams("username","hyman")

.addParams("password","123")

.build()

.execute(callback);

概述:

  图片加载的工作流(task flow)都是3级缓存的流程;图片的内存缓存一定是LruCache实现;图片下载和读取线程的调度一定是通过线程池管理

画图说明图片加载原理

Glide的使用

详情查看https://github.com/bumptech/glide

  • 介绍:

    • 专注于处理平滑滑动的图片类库
    • 默认使用HttpUrlConnection下载图片
    • 支持设置渐渐显示的动画
    • 支持设置加载中的图片
  • 添加依赖

    compile 'com.github.bumptech.glide:glide:3.7.0'
    
  • 使用Glide加载图片

     Glide.with(this).load("").centerCrop()//设置从中间剪切.placeholder(R.mipmap.ic_launcher)//设置加载过程中显示的图片.error(R.mipmap.ic_launcher)//设置加载失败显示的图片.crossFade()//设置渐渐显示的效果.into(image);
    

Picasso的使用

详情查看https://github.com/square/picasso

  • 介绍:

    • Square开源的比较早的图片加载类库
    • 自动处理adapter中的ImageView的回收时取消下载图片
    • 支持加载多种来源的图片,比如网络,sd卡,res资源
    • 支持设置占位图片
    • 支持对图片的自定义处理
  • 添加依赖

    compile 'com.squareup.picasso:picasso:2.5.2'
    
  • 使用Picasso加载图片

    Picasso.with(this).load("url").placeholder(R.mipmap.ic_launcher).error(R.mipmap.ic_launcher).centerCrop().noFade()//设置不需要渐渐显示的动画效果.resize(120,120)//指定压缩参考的宽高比.into(image);
    
  • 加载其他资源路径的图片

    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);
    
  • 注意:如果不设置resize(120,120),则Picasso会加载整个图片,显然这样消耗的内存比较大,一般都需要指定一下,而Glide内部已经默认参考了控件的宽高来进行缩放了。

Fresco的使用

详情查看https://github.com/facebook/fresco

  • 介绍:

    • Facebook开源的专注于优化java堆内存,最大程度减少OOM
    • 在Android4.4以及以下,将图片存储在Android的一块特殊的内存区域,这会让图片处理更加快速
    • 支持Gif和WebP格式的图片
  • 添加依赖

    compile 'com.facebook.fresco:fresco:0.11.0'
    
  • 首先初始化Fresco,一般在Application的onCreate中初始化

    //先初始化Fresco
    Fresco.initialize(this);
    
  • 使用Fresco提供的SimpleDraweeView显示图片

      draweeView.setImageURI("url");
    
  • 由于使用的是自定义控件加载图片,那么通过定义属性来进行设置:

    <com.facebook.drawee.view.SimpleDraweeViewandroid:id="@+id/image_view"android:layout_width="300dp"android:layout_height="300dp"fresco:fadeDuration="300"fresco:actualImageScaleType="focusCrop"fresco:placeholderImage="@color/wait_color"fresco:placeholderImageScaleType="fitCenter"fresco:failureImage="@drawable/error"fresco:failureImageScaleType="centerInside"fresco:retryImage="@drawable/retrying"fresco:retryImageScaleType="centerCrop"fresco:progressBarImage="@drawable/progress_bar"fresco:progressBarImageScaleType="centerInside"fresco:progressBarAutoRotateInterval="1000"fresco:backgroundImage="@color/blue"fresco:overlayImage="@drawable/watermark"fresco:pressedStateOverlayImage="@color/red"fresco:roundAsCircle="false"fresco:roundedCornerRadius="1dp"fresco:roundTopLeft="true"fresco:roundTopRight="false"fresco:roundBottomLeft="false"fresco:roundBottomRight="true"fresco:roundWithOverlayColor="@color/corner_color"fresco:roundingBorderWidth="2dp"fresco:roundingBorderColor="@color/border_color"
    />属性解释:
    placeholderImage就是所谓的展位图啦,在图片没有加载出来之前你看到的就是它failureIamge看到名字就知道是什么了,图片加载失败时显示的图片就是它了retryImage图片加载失败时显示,提示用户点击重新加载,重复加载4次还是没有加载出来的时候才会显示failureImage的图片progressBarImage进度条图片
    backgroundImage背景图片,这里的背景图片首先被绘制overlayImage设置叠加图,在xml中只能设置一张叠加图片,如果需要多张图片的话,需要在java代码中设置哦
    pressedStateOverlayImage设置点击状态下的叠加图,此叠加图不能缩放ImageScaleType这个就是各种各样的图片缩放样式了,center,centerCrop,fouseCrop,centerInside,fitCenter,fitStart,fitEnd,fitXY

图片加载总结

  • UniversalImageLoader:老牌优秀的图片加载类库,特点是配置项丰富,支持圆形图片效果显示以及添加图片加载动画。
  • Picasso : Square公司出品。也是很早期出现的图片加载库。默认加载图片不会压缩,并且图片渲染模式是ARGB_8888,占用内存相比Glide稍微高一点,但是可以指定图片加载的宽高,便会依据图片的宽高进行缩放。
  • Glide:专门为优化Picasso而生,所以API和Picasso简直一模一样。内部会自动根据图片的宽高来压缩图片,并且图片渲染模式为RGB_565,内存占用会减少一半,专门针对滑动中的图片加载有优化。和Picasso相比,推荐使用Glide。
  • Fresco : Facebook公司开源的。特点是在android4.4以及以下,将图片的放入Android native的C++内存中,而不是Java堆内存,所以占用的Java堆内存很小,大大减小了程序出现OOM的几率;支持WebP和Gif显示;支持多种图片的显示配置,比如圆形。
  • 重磅更新 2017-02-16

    • 前言
    • 主流图片加载库的对比
      • Android-Universal-Image-Loader
      • Picasso
      • Glide
      • Fresco
    • 按需选择图片加载库
    • 如何更好地封装图片加载库
      • 为什么要封装
      • 使用策略模式封装图片加载策略
    • 源码地址
    • 部分参考链接
    • 更新
      • 2016-12-09 ll You must not call setTag on a view Glide is targeting
      • 2016-12-13 ll 添加loadGifWithPrepareCall方法
      • 2016-12-26 ll 更新loadGifWithProgress方法
        • 2017-1-10 ll 统一加载图片进度回调方法为loadImageWithProgress弃用并删除loadGifWithProgress方法
      • 2016-12-26 ll 自定义GlideModule 并将 Glide与okhttp3集成
      • 2017-1-6 ll GIF帧显示不完全 2017-1-10补充说明PS
        • 2017-1-10补充说明PS
      • 2017-1-6 ll You cannot start a load for a destroyed activity
      • 2017-1-10 ll 简单说说图片适配的问题
      • 2017-1-10 ll 添加saveImage方法实现图片的本地自定义保存功能

重磅更新 || 2017-02-16

使用ImageLoaderUtil实现一个真正意义的图集功能,持续完善和更新中

Gallery

Gallery

Gallery

重要的东西贴三遍!

前言

  • 图片加载是Android开发中最最基础的功能,为了降低开发周期和难度,我们经常会选用一些图片加载的开源库
  • 选取第三方SDK需要谨慎
  • 二次封装

注意:所有改动更新会同步到GitHub

主流图片加载库的对比

  • 共同点

    • 使用简单:一句话实现图片的获取和显示
    • 可配置性高:可配置各种解码、缓存、下载机制
    • 自适应程度高:根据系统性能调整配置策略(如CPU核数决定最大并发数、内存决定内存缓存大小、网络状态变化调整最大并发数)
    • 多级缓存
    • 支持多种数据源
    • 支持多种Displayer
    • 兼容性好(可以配合okhttp等库进行使用)

Android-Universal-Image-Loader

  • 简介

    • 作者:nostra13
    • 面世时间:2011
    • star数(截止到发稿):14509
    • https://github.com/nostra13/Android-Universal-Image-Loader
  • 优点 
    • 支持下载进度监听(ImageLoadingListener)
    • 可在View滚动中暂停图片加载(PauseOnScrollListener)
    • 默认实现多种内存缓存算法(最大最先删除,使用最少最先删除,最近最少使用,先进先删除,当然自己也可以配置缓存算法)
  • 缺点 
    • 从2015.11.27之后不再维护,项目中不建议使用

Picasso

  • 简介

    • 作者:JakeWharton(Square)
    • 面世时间:2012
    • star数(截止到发稿):12076
    • https://github.com/square/picasso
  • 优点 
    • 包较小(100k)
    • 取消不在视野范围内图片资源的加载
    • 使用最少的内存完成复杂的图片转换
    • 自动添加二级缓存
    • 任务调度优先级处理
    • 并发线程数根据网络类型调整
    • 图片的本地缓存交给同为Square出品的okhttp处理,控制图片的过期时间
  • 缺点 
    • 功能较为简单
    • 自身无实现“本地缓存”

Glide

  • 简介

    • 作者:Sam sjudd (Google)
    • 面世时间:2013
    • star数(截止到发稿):12067
    • https://github.com/bumptech/glide
  • 优点 
    • 多种图片格式的缓存,适用于更多的内容表现形式(如Gif、WebP、缩略图、Video)
    • 生命周期集成(根据Activity或者Fragment的生命周期管理图片加载请求)
    • 高效处理Bitmap(bitmap的复用和主动回收,减少系统回收压力)
    • 高效的缓存策略,灵活(Picasso只会缓存原始尺寸的图片,Glide缓存的是多种规格),加载速度快且内存开销小(默认Bitmap格式的不同,使得内存开销是Picasso的一半)
  • 缺点 
    • 方法较多较复杂,因为相当于在Picasso上的改进,包较大(500k),影响不是很大

Fresco

  • 简介

    • 作者:Facebook
    • 面世时间:2015
    • star数(截止到发稿):11235
    • https://github.com/facebook/fresco
  • 优点 
    • 最大的优势在于5.0以下(最低2.3)的bitmap加载。在5.0以下系统,Fresco将图片放到一个特别的内存区域(Ashmem区)
    • 大大减少OOM(在更底层的Native层对OOM进行处理,图片将不再占用App的内存)
    • 适用于需要高性能加载大量图片的场景
  • 缺点 
    • 包较大(2~3M)
    • 用法复杂
    • 底层涉及c++领域,阅读源码深入学习难度大

按需选择图片加载库

  • 图片加载需要支持Gif,之前项目中使用的Android-Universal-Image-Loader不支持Gif且Android-Universal-Image-Loader已经停止维护,遂决定替换图片加载库
  • 分析完优缺点最终选择Glide的其它理由:

    • Glide是在Picasso的基础上进行改进的(支持Gif,内存开销小),虽然500k左右的包大小相对于Picasso较大,但是这个数量级的影响可以接受
    • 初衷是想一直维持图片的原始ImageView,而 Fresco需要在布局文件中将图片控件声明为库中自定义的SimpleDraweeView,如果切库还需要更改组件,代价会很高
    • Google推荐(亲儿子),在Google很多开源项目中广泛使用
  • 但不可避免的是,Glide在使用的过程中依然存在着许多坑需要我们去填!

如何更好地封装图片加载库

为什么要封装?

先从现在面对的情形来看,项目中使用图片加载的地方都是使用的类似下面的语句

ImageLoader.getInstance().displayImage(imageUrl, imageView,options);
  • 1
  • 1

然而现在ImageLoader已经停止维护且已经无法满足项目需求,我们需要替换,这时你会发现如果换库的话,所有涉及到的地方都要修改(Android-Universal-Image-Loader已经和图片加载的业务逻辑严重地耦合在一起了),工作量可见一斑,这就是不封装在切库时面临的窘境! 
那怎么解决那? 
计算机史上有个万能的解决方案就是,如果原有层面解决不了问题,那么就请再加一层!

/*** Created by soulrelay on 2016/10/11 13:42.* Class Note:* use this class to load image,single instance*/
public class ImageLoaderUtil {//图片默认加载类型 以后有可能有多种类型public static final int PIC_DEFAULT_TYPE = 0;//图片默认加载策略 以后有可能有多种图片加载策略public static final int LOAD_STRATEGY_DEFAULT = 0;private static ImageLoaderUtil mInstance;private BaseImageLoaderStrategy mStrategy;public ImageLoaderUtil() {mStrategy = new GlideImageLoaderStrategy();}//单例模式,节省资源public static ImageLoaderUtil getInstance() {if (mInstance == null) {synchronized (ImageLoaderUtil.class) {if (mInstance == null) {mInstance = new ImageLoaderUtil();return mInstance;}}}return mInstance;}/*** 统一使用App context* 可能带来的问题:http://stackoverflow.com/questions/31964737/glide-image-loading-with-application-context** @param url* @param placeholder* @param imageView*/public void loadImage(String url, int placeholder, ImageView imageView) {mStrategy.loadImage(imageView.getContext(), url, placeholder, imageView);}public void loadGifImage(String url, int placeholder, ImageView imageView) {mStrategy.loadGifImage(url, placeholder, imageView);}public void loadImage(String url, ImageView imageView) {mStrategy.loadImage(url, imageView);}/*** 展示图片加载进度*/public void loadImageWithProgress(String url, ImageView imageView, ProgressLoadListener listener) {mStrategy.loadImageWithProgress(url,imageView,listener);}public void loadGifWithProgress(String url, ImageView imageView, ProgressLoadListener listener) {mStrategy.loadGifWithProgress(url,imageView,listener);}/*** 策略模式的注入操作** @param strategy*/public void setLoadImgStrategy(BaseImageLoaderStrategy strategy) {mStrategy = strategy;}/*** 清除图片磁盘缓存*/public void clearImageDiskCache(final Context context) {mStrategy.clearImageDiskCache(context);}/*** 清除图片内存缓存*/public void clearImageMemoryCache(Context context) {mStrategy.clearImageMemoryCache(context);}/*** 根据不同的内存状态,来响应不同的内存释放策略** @param context* @param level*/public void trimMemory(Context context, int level) {mStrategy.trimMemory(context, level);}/*** 清除图片所有缓存*/public void clearImageAllCache(Context context) {clearImageDiskCache(context.getApplicationContext());clearImageMemoryCache(context.getApplicationContext());}/*** 获取缓存大小** @return CacheSize*/public String getCacheSize(Context context) {return mStrategy.getCacheSize(context);}}
  • 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
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 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
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117

所有需要图片显示的地方使用如下方法进行调用:

  • 入口唯一,所有图片加载都在ImageLoaderUtil这一个地方统一管理,使用了单例模式(据说单元素的枚举类型已经成为实现Singleton的最佳方法,你可以试试 ),
  • 高效地封装减少了切库(只需要切换图片加载策略)带来的代价,默认采用GlideImageLoaderStrategy

总结:外部表现一致,内部灵活处理原则。

/*** 图片加载库的封装演示案例* Created by soulrelay on 2016/12/11 19:18*/
public class MainActivity extends AppCompatActivity {@BindView(R.id.iv_normal)ImageView ivNormal;@BindView(R.id.iv_gif)ImageView ivGif;@BindView(R.id.iv_gif1)ImageView ivGif1;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);ButterKnife.bind(this);initView();}private void initView() {ImageLoaderUtil.getInstance().loadImage("http://image.sports.baofeng.com/25a3dbb0c99c5e48e52e60941ed230be", R.drawable.bg_default_video_common_small, ivNormal);ImageLoaderUtil.getInstance().loadImage("http://image.sports.baofeng.com/19ce5d6ac3b4fff255196f200b1d3079", R.drawable.bg_default_video_common_small, ivGif);ImageLoaderUtil.getInstance().loadGifImage("http://image.sports.baofeng.com/19ce5d6ac3b4fff255196f200b1d3079", R.drawable.bg_default_video_common_small, ivGif1);}}
  • 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
  • 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

效果图如下所示: 

使用策略模式封装图片加载策略

如果你对策略模式不是很熟,请先参考策略模式和状态模式 
首先我们需要抽象出一个图片加载的基础接口BaseImageLoaderStrategy 
基本功能主要包括

  • 正常加载图片
  • 针对于GIF图片的特殊加载
  • 加载图片的进度回调
  • 清除缓存
  • 获取缓存大小等
  • 其它特殊需求自己封装,最好不要破坏策略模式的整体结构
/*** Created by soulrelay on 2016/10/11.* Class Note:* abstract class/interface defined to load image* (Strategy Pattern used here)*/
public interface BaseImageLoaderStrategy {//无占位图void loadImage(String url, ImageView imageView);void loadImage(String url, int placeholder, ImageView imageView);void loadImage(Context context, String url, int placeholder, ImageView imageView);void loadGifImage(String url, int placeholder, ImageView imageView);void loadImageWithProgress(String url, ImageView imageView, ProgressLoadListener listener);void loadGifWithProgress(String url, ImageView imageView, ProgressLoadListener listener);//清除硬盘缓存void clearImageDiskCache(final Context context);//清除内存缓存void clearImageMemoryCache(Context context);//根据不同的内存状态,来响应不同的内存释放策略void trimMemory(Context context, int level);//获取缓存大小String getCacheSize(Context context);}
  • 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
  • 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

需要说明的一点是:

  • 当封装的方法参数比较少时可以按照上述方式进行抽象,如果需要传递的参数较多,可以考虑使用建造者模式建造者模式
  • 例如封装一个ImageLoaderConfiguration,包含如下参数等等,将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示 
    • type 图片加载的类型(大图、小图、中图)
    • url 需要解析的url
    • placeHolder 当没有成功加载的时候显示的图片
    • imgView ImageView的实例
    • loadStrategy 加载策略
  • 当然这里我没有使用建造模式,考虑到目前使用的对象还不算复杂(传参比较简单),而且如果使用建造者模式有可能每次都要new一个新的对象实例,虽然开销可以接受
  • 使用ImageLoaderUtil的过程中,注意内存泄露的问题(静态单例的生命周期与App一样,当一个单例的对象长久不用时,不会被垃圾收集机制回收)

然后基于每个图片库的各自方式来进行相应策略的封装,需要使用哪种策略,只需要通过ImageLoaderUtil的setLoadImgStrategy(BaseImageLoaderStrategy strategy)方法将相应的策略注入,相关类图关系如下所示:

不同的图片加载库实现不同的图片加载策略 
这里只是给出Glide的图片加载策略类GlideImageLoaderStrategy作为参考

  • Glide依赖v4包,且需要配置android.permission.INTERNET和android.permission.WRITE_EXTERNAL_STORAGE(忘记配置权限,图片加载不出来,还看不出什么异常)
  • 其中部分方法使用到了RequestListener的回调(这里是因为项目中的一些特殊需求而添加,如统计图片首次加载时长来测试一下图片cdn服务器的速度等)
  • 在使用Glide的过程中遇到了一些问题,部分已经在注释中说明
  • 之所以针对gif单独封装,是因为在使用的过程中发现,当在列表中加载大量gif会有OOM的问题,所以通过asGif进行特殊标明,即使这样也会出现类似问题,同时暂时通过skipMemoryCache(true)跳过内存缓存,之后有更好的办法会继续补充,各位看官如有良策,希望可以不吝赐教
  • Glide本身不提供图片的progress回调,所以关于进度回调的解决方案参照的是 
    ProgressGlide,并做了些许改动集成到项目中
  • 期间发现了一个很好的问题Android的App中线程池的使用,具体使用多少个线程池?,其中一个答主的关于图片加载库线程池策略的分析很好,值得体会,简单摘录如下: 
    • UIL的线程池处理非常简单粗暴,没有根据CPU数量来选择,也没有根据网络状况的变化进行调整;
    • Picasso的线程池会根据网络状况的变化进行调整,在Wifi下线程数为4,而4G下线程数为3, 3G下为2, 2G下为1,默认状况为3;
    • Glide加载缓存未命中的线程池会根据根据CPU的数量和Java虚拟机中可用的处理器数量来选择合适的线程数,但是最多不超过4;而加载缓存命中的图片的线程池默认大小为1.
/*** Created by soulrelay on 2016/10/11 13:48.* Class Note:* using {@link Glide} to load image*/
public class GlideImageLoaderStrategy implements BaseImageLoaderStrategy {@Overridepublic void loadImage(String url, int placeholder, ImageView imageView) {loadNormal(imageView.getContext(), url, placeholder, imageView);}@Overridepublic void loadImage(Context context, String url, int placeholder, ImageView imageView) {loadNormal(context, url, placeholder, imageView);}/*** 无holder的gif加载** @param url* @param imageView*/@Overridepublic void loadImage(String url, ImageView imageView) {Glide.with(imageView.getContext()).load(url).dontAnimate().placeholder(imageView.getDrawable()).diskCacheStrategy(DiskCacheStrategy.SOURCE).into(imageView);}@Overridepublic void loadGifImage(String url, int placeholder, ImageView imageView) {loadGif(imageView.getContext(), url, placeholder, imageView);}@Overridepublic void loadImageWithProgress(String url, final ImageView imageView, final ProgressLoadListener listener) {Glide.with(imageView.getContext()).using(new ProgressModelLoader(new ProgressUIListener() {@Overridepublic void update(final int bytesRead, final int contentLength) {imageView.post(new Runnable() {@Overridepublic void run() {listener.update(bytesRead, contentLength);}});}})).load(url).asBitmap().dontAnimate().listener(new RequestListener<Object, Bitmap>() {@Overridepublic boolean onException(Exception e, Object model, Target<Bitmap> target, boolean isFirstResource) {listener.onException();return false;}@Overridepublic boolean onResourceReady(Bitmap resource, Object model, Target<Bitmap> target, boolean isFromMemoryCache, boolean isFirstResource) {listener.onResourceReady();return false;}}).into(imageView);}@Overridepublic void loadGifWithProgress(String url, final ImageView imageView, final ProgressLoadListener listener) {Glide.with(imageView.getContext()).using(new ProgressModelLoader(new ProgressUIListener() {@Overridepublic void update(final int bytesRead, final int contentLength) {imageView.post(new Runnable() {@Overridepublic void run() {listener.update(bytesRead, contentLength);}});}})).load(url).asGif().skipMemoryCache(true).dontAnimate().listener(new RequestListener<String, GifDrawable>() {@Overridepublic boolean onException(Exception e, String model, Target<GifDrawable> target, boolean isFirstResource) {listener.onException();return false;}@Overridepublic boolean onResourceReady(GifDrawable resource, String model, Target<GifDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {listener.onResourceReady();return false;}}).into(imageView);}@Overridepublic void clearImageDiskCache(final Context context) {try {if (Looper.myLooper() == Looper.getMainLooper()) {new Thread(new Runnable() {@Overridepublic void run() {Glide.get(context.getApplicationContext()).clearDiskCache();}}).start();} else {Glide.get(context.getApplicationContext()).clearDiskCache();}} catch (Exception e) {e.printStackTrace();}}@Overridepublic void clearImageMemoryCache(Context context) {try {if (Looper.myLooper() == Looper.getMainLooper()) { //只能在主线程执行Glide.get(context.getApplicationContext()).clearMemory();}} catch (Exception e) {e.printStackTrace();}}@Overridepublic void trimMemory(Context context, int level) {Glide.get(context).trimMemory(level);}@Overridepublic String getCacheSize(Context context) {try {return CommonUtils.getFormatSize(CommonUtils.getFolderSize(Glide.getPhotoCacheDir(context.getApplicationContext())));} catch (Exception e) {e.printStackTrace();}return "";}/*** load image with Glide*/private void loadNormal(final Context ctx, final String url, int placeholder, ImageView imageView) {/***  为其添加缓存策略,其中缓存策略可以为:Source及None,None及为不缓存,Source缓存原型.如果为ALL和Result就不行.然后几个issue的连接:https://github.com/bumptech/glide/issues/513https://github.com/bumptech/glide/issues/281https://github.com/bumptech/glide/issues/600modified by xuqiang*///去掉动画 解决与CircleImageView冲突的问题 这个只是其中的一个解决方案//使用SOURCE 图片load结束再显示而不是先显示缩略图再显示最终的图片(导致图片大小不一致变化)final long startTime = System.currentTimeMillis();Glide.with(ctx).load(url).dontAnimate().placeholder(placeholder).diskCacheStrategy(DiskCacheStrategy.SOURCE).listener(new RequestListener<String, GlideDrawable>() {@Overridepublic boolean onException(Exception e, String model, Target<GlideDrawable> target, boolean isFirstResource) {return false;}@Overridepublic boolean onResourceReady(GlideDrawable resource, String model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {return false;}}).into(imageView);}/*** load image with Glide*/private void loadGif(final Context ctx, String url, int placeholder, ImageView imageView) {final long startTime = System.currentTimeMillis();Glide.with(ctx).load(url).asGif().dontAnimate().placeholder(placeholder).skipMemoryCache(true).diskCacheStrategy(DiskCacheStrategy.SOURCE).listener(new RequestListener<String, GifDrawable>() {@Overridepublic boolean onException(Exception e, String model, Target<GifDrawable> target, boolean isFirstResource) {return false;}@Overridepublic boolean onResourceReady(GifDrawable resource, String model, Target<GifDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {return false;}}).into(imageView);}}
  • 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
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 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
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190

源码地址

ImageLoaderUtil

部分参考链接

http://www.jianshu.com/p/97994c9693f9 
https://www.zhihu.com/question/37804956 
http://www.jianshu.com/p/e26130a93289 
http://www.cnblogs.com/android-blogs/p/5737611.html

更新

2016-12-09 ll You must not call setTag() on a view Glide is targeting

项目中在使用Glide图片加载框架时遇到该错误 
报错原因大致是因为Glide加载的iamgeView调用了setTag()方法导致的错误,因为Glide已经默认为ImageView设置的Tag

相关解决方案已经在Glide 3.6.0(issue #370)被引进,实测可行 
在AndroidManifest.xml中加入

<applicationandroid:name=".App">
  • 1
  • 2
  • 1
  • 2

然后在App中添加如下代码:

public class App extends Application {@Override public void onCreate() {super.onCreate();ViewTarget.setTagId(R.id.glide_tag);}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

在src/main/values/ids.xml添加如下代码:

<resources><item type="id" name="glide_tag" />
</resources>
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

2016-12-13 ll 添加loadGifWithPrepareCall方法

2016.12.13

只想知道图片是否准备完毕(包括来自网络或者sdcard),区别于loadImageWithProgress和loadGifWithProgress的进度回调

Tips:使用Glide加载图片注意ImageView的Scaletype的设置

public interface BaseImageLoaderStrategy {void loadGifWithPrepareCall(String url, ImageView imageView, SourceReadyListener listener);
}
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3
public class GlideImageLoaderStrategy implements BaseImageLoaderStrategy {@Overridepublic void loadGifWithPrepareCall(String url, ImageView imageView, final SourceReadyListener listener) {Glide.with(imageView.getContext()).load(url).asGif().dontAnimate().skipMemoryCache(true).diskCacheStrategy(DiskCacheStrategy.SOURCE).listener(new RequestListener<String, GifDrawable>() {@Overridepublic boolean onException(Exception e, String model, Target<GifDrawable> target, boolean isFirstResource) {return false;}@Overridepublic boolean onResourceReady(GifDrawable resource, String model, Target<GifDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {listener.onResourceReady(resource.getIntrinsicWidth(),resource.getIntrinsicHeight());return false;}}).into(imageView);}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
public class ImageLoaderUtil {public void loadGifWithPrepareCall(String url, ImageView imageView, SourceReadyListener listener) {mStrategy.loadGifWithPrepareCall(url,imageView,listener);}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

2016-12-26 ll 更新loadGifWithProgress方法

2017-1-10 ll 统一加载图片进度回调方法为loadImageWithProgress,弃用并删除loadGifWithProgress方法

具体细节查看GitHub最新代码

2016-12-26 ll 自定义GlideModule 并将 Glide与okhttp3集成

1.自定义一个GlideModule

/*** DES:自定义一个GlideModule* <p>* GlideModule 是一个抽象方法,全局改变 Glide 行为的一个方式,* 通过全局GlideModule 配置Glide,用GlideBuilder设置选项,用Glide注册ModelLoader等。* <p>*/
public class MyGlideModule implements GlideModule {@Overridepublic void applyOptions(Context context, GlideBuilder builder) {// Apply options to the builder here.int maxMemory = (int) Runtime.getRuntime().maxMemory();//获取系统分配给应用的总内存大小int memoryCacheSize = maxMemory / 8;//设置图片内存缓存占用八分之一//设置内存缓存大小builder.setMemoryCache(new LruResourceCache(memoryCacheSize));builder.setBitmapPool(new LruBitmapPool(memoryCacheSize));}@Overridepublic void registerComponents(Context context, Glide glide) {// register ModelLoaders here.}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

2.AndroidManifest.xml注册

<manifest ...><!-- ... permissions --><application ...><!-- 自定义GlideModule --><meta-data
            android:name="com.baofeng.soulrelay.utils.imageloader.MyGlideModule"android:value="GlideModule" /><!-- 自定义GlideModule --><!-- ... activities and other components --></application>
</manifest>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

3、 Glide与OkHttp3集成

   compile 'com.squareup.okhttp3:okhttp:3.4.2'compile 'com.github.bumptech.glide:okhttp3-integration:1.4.0@aar'
  • 1
  • 2
  • 1
  • 2

4、添加混淆处理

#--------------------Glide-----------------------#
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {**[] $VALUES;public *;
}-keepnames class com.baofeng.soulrelay.utils.imageloader.MyGlideModule
# or more generally:
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep class com.bumptech.glide.integration.okhttp3.OkHttpGlideModule#--------------------Glide-----------------------#
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

2017-1-6 ll GIF帧显示不完全 2017-1-10补充说明PS

相关问题在issues1649中被提到和解决(目前glide:3.7.0的确存在这个问题) 
具体解决方法是: 
glide:3.8.0-SNAPSHOT修复了关于GIF展示的一些bug,实测可用 
Gradle配置修改如下:

  • Add the snapshot repo to your list of repositories:
repositories {jcenter()maven {name 'glide-snapshot'url 'http://oss.sonatype.org/content/repositories/snapshots'}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • And then change your dependencies to the v3 snapshot version:
dependencies {compile 'com.github.bumptech.glide:glide:3.8.0-SNAPSHOT'compile 'com.github.bumptech.glide:okhttp-integration:1.5.0-SNAPSHOT'
}
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

2017-1-10补充说明PS

PS:提供一个gif图 帧提取工具GIFFrame.exe 
据我分析,那些没有显示完整的GIF图片,里面的部分帧图片本身就不是完整的,但是之前的Glide并没有做很好的处理,所以显示效果有缺陷,当然最新的3.8.0-SNAPSHOT解决了这个问题,但是在显示的时候仍有瑕疵(有一些重叠,当然我觉得这也跟gif图的做工有关)

2017-1-6 ll You cannot start a load for a destroyed activity

完整异常信息:

FATAL EXCEPTION: main
Process: com.sports.baofeng, PID: 9170
java.lang.IllegalArgumentException: You cannot start a load for a destroyed activity
at com.bumptech.glide.d.k.b(SourceFile:134)
at com.bumptech.glide.d.k.a(SourceFile:102)
at com.bumptech.glide.d.k.a(SourceFile:87)
at com.bumptech.glide.i.c(SourceFile:629)
at com.storm.durian.common.utils.imageloader.b.a(SourceFile:1194)
at com.storm.durian.common.utils.imageloader.c.a(SourceFile:52)
at com.sports.baofeng.specialtopic.SpecialTopicDetailFixActivity.a(SourceFile:311)
at com.sports.baofeng.specialtopic.SpecialTopicDetailFixActivity.a(SourceFile:1347)
at com.sports.baofeng.specialtopic.d.a(SourceFile:1052)
at com.sports.baofeng.specialtopic.c$1.a(SourceFile:1064)
at com.storm.durian.common.b.b$1.onPostExecute(SourceFile:57)
at android.os.AsyncTask.finish(AsyncTask.java:651)
at android.os.AsyncTask.access$500(AsyncTask.java:180)
at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:668)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:158)
at android.app.ActivityThread.main(ActivityThread.java:7225)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

以上异常出现的几率为random 
初步分析原因是:进入页面然后又迅速退出,导致AsyncTask中的onPostExecute在调用Glide加载图片时出现如上异常,当然也跟对AsyncTask的管理有关 
同样的问题参考issues138 
简单的摘要:

you fire an async task and then finish() in which case you just need to pass getApplicationContext instead of this when creating the callback/asynctask

按照上面这个理解的话,如果是在AsyncTask中的onPostExecute执行时 
调用Glide加载图片,context最好使用ApplicationContext

对ImageLoaderUtil做如下更新,添加方法

  • BaseImageLoaderStrategy
 //这里的context指定为ApplicationContextvoid loadImageWithAppCxt(String url, ImageView imageView);
  • 1
  • 2
  • 1
  • 2
  • GlideImageLoaderStrategy
  @Overridepublic void loadImageWithAppCxt(String url, ImageView imageView) {Glide.with(imageView.getContext().getApplicationContext()).load(url).dontAnimate().placeholder(imageView.getDrawable()).diskCacheStrategy(DiskCacheStrategy.SOURCE).into(imageView);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • ImageLoaderUtil
 public void loadImageWithAppCxt(String url, ImageView imageView) {mStrategy.loadImageWithAppCxt(url,imageView);}
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

2017-1-10 ll 简单说说图片适配的问题

过多的概念不赘述,可以先参考Android屏幕适配全攻略(最权威的官方适配指导) 
这里主要描述一种现象,明白的话自然觉得很简单!

    <ImageView
                android:id="@+id/iv_gif"android:layout_width="wrap_content"android:layout_height="wrap_content"android:scaleType="centerInside"/>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

假设现在要加载一张200px * 200px的GIF图片(图片基于1280 * 720),这张图片的宽高设置为wrap_content,如果在1920 * 1080分辨率的手机上显示,相对于1280 * 720(假设屏幕尺寸相同),在视觉效果上会显得小,这其实是Android系统基于手机像素密度的一种自适配,单一变化条件下,1920 * 1080分辨率的手机的像素密度是1280 * 720的1.5倍 
假设如果系统的自适配让你觉得在高分辨率手机上显得图片过小(像素密度高,200个像素显示起来就比较挤),可以通过自己的计算来改变这种现象 
ImageLoaderUtil提供如下加载成功回调的方法(并且会把图片的宽高告诉你):这里有个参数设置,看需求来计算,粗略点可以只使用宽度比例来算,如下面的例子显示,参数为AppParams.screenWidth / 720,当然也可以获取屏幕密度,1920 * 1080的屏幕密度为3,1280 * 720的为2,所以参数可以设置为AppParams.density/2(在两种分辨率上看着视觉上一样)

    ImageLoaderUtil.getInstance().loadGifWithPrepareCall(url, mImageView, new SourceReadyListener() {@Overridepublic void onResourceReady(int width, int height) {ViewGroup.LayoutParams params = mImageView.getLayoutParams();params.height = height * AppParams.screenWidth / 720;params.width = width * AppParams.screenWidth / 720;mImageView.setLayoutParams(params);progressBar.setVisibility(View.GONE);}});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

2017-1-10 ll 添加saveImage方法,实现图片的本地自定义保存功能

已同步到GitHub ImageLoaderUtil

  • ImageLoaderUtil相关接口:
  /*** @param context* @param url 图片url* @param savePath 保存路径* @param saveFileName 保存文件名* @param listener 文件保存成功与否的监听器*/public void saveImage(Context context, String url, String savePath, String saveFileName, ImageSaveListener listener) {mStrategy.saveImage(context, url, savePath, saveFileName, listener);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 在工作线程中调用示例如下:
ImageLoaderUtil.getInstance().saveImage(getActivity(), url,Environment.getExternalStorageDirectory().getAbsolutePath() + "/bfsports","bfsports" + System.currentTimeMillis(), new ImageSaveListener() {@Overridepublic void onSaveSuccess() {handler.obtainMessage(MSG_PIC_SAVE_SUCC).sendToTarget();}@Overridepublic void onSaveFail() {handler.obtainMessage(MSG_PIC_SAVE_FAIL).sendToTarget();}});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

APP图片加载库(框架)和缓存相关推荐

  1. Android图片加载库—Picasso一个强大的图像下载和缓存库

    介绍 GitHub源码 点击查看 Picasso是一款强大的图片下载和缓存开源软件,只能在Android平台上使用,由Square开发.使用Picasso可以添加一些必须的特性和视觉效果到Androi ...

  2. Android开源框架——图片加载库Glide

    Glide是有google开发的图片加载库,支持图片加载与处理,包括动态图片的加载,以及视频的解码. 开源地址:https://github.com/bumptech/glide build.grad ...

  3. Coil - Google推荐的协程图片加载库

    随着Kotlin的转正,Glide不再是最佳选择.看一下Google极力推荐的Coil框架. GitHub:Coilhttps://coil-kt.github.io/coil/ Pangu-Immo ...

  4. Android图片加载库的封装实战

    重磅更新 2017-02-16 2017-05-09 优化圆形图片加载 更新demo 前言 主流图片加载库的对比 Android-Universal-Image-Loader Picasso Glid ...

  5. android图片加载库的使用对比

    1.Android-Universal-Image-Loader giehub地址::https://github.com/nostra13/Android-Universal-Image-Loade ...

  6. FaceBook推出的Android图片加载库-Fresco

    源文件:http://www.mamicode.com/info-detail-544084.html 在Android设备上面,快速高效的显示图片是极为重要的.过去的几年里,我们在如何高效的存储图像 ...

  7. Android图片加载库Fresco

    在Android设备上面,快速高效的显示图片是极为重要的.过去的几年里,我们在如何高效的存储图像这方面遇到了很多问题.图片太大,但是手机的内存却很小.每一个像素的R.G.B和alpha通道总共要占用4 ...

  8. fackbook的Fresco (FaceBook推出的Android图片加载库-Fresco)

    [Android开发经验]FaceBook推出的Android图片加载库-Fresco 欢迎关注ndroid-tech-frontier开源项目,定期翻译国外Android优质的技术.开源库.软件架构 ...

  9. FaceBook推出的Android图片加载库Fresco

    在Android设备上面,快速高效的显示图片是极为重要的.过去的几年里,我们在如何高效的存储图像这方面遇到了很多问题.图片太大,但是手机的内存却很小.每一个像素的R.G.B和alpha通道总共要占用4 ...

  10. Fresco-FaceBook推出的Android图片加载库

    在Android设备上面,快速高效的显示图片是极为重要的.过去的几年里,我们在如何高效的存储图像这方面遇到了很多问题.图片太大,但是手机的内存却很小.每一个像素的R.G.B和alpha通道总共要占用4 ...

最新文章

  1. WPF 4 Ribbon 开发 之 应用程序菜单(Application Menu)
  2. 两个整数相加减是否溢出
  3. 在控制台程序中隐藏控制台窗口
  4. java传参怎么理解_如何理解Java的值传递
  5. wget: command not found
  6. 08TensorFlow2.0基础--8.6tensoflow-gpu和cpu
  7. Diagrams for Mac(原生流程图制作工具)
  8. this绑定丢失的问题
  9. PMP笔记 第6章 项目进度管理
  10. 计算标准累积正态分布_神说要有正态分布,于是就有了正态分布。
  11. 电信怎么关闭信息接受服务器,怎么关闭短信接收功能
  12. 【Android FFMPEG 开发】FFMPEG 音频重采样 ( 初始化音频重采样上下文 SwrContext | 计算音频延迟 | 计算输出样本个数 | 音频重采样 swr_convert )
  13. John McCarthy:人工智能之父
  14. c语言的所有头文件,C语言所有头文件.doc
  15. 中鑫优配谨防黑周四出现这种走势!
  16. Android——加速传感器(ACCELEROMETER)的应用
  17. python爬取微博热门消息(一)——效果展示
  18. 范畴(Category)
  19. NVIDIA支持CUDA的显卡选型简述
  20. pli测试50题题库_马士基笔试题目

热门文章

  1. Windows11 安装 WSA 简单上手一试
  2. 最简单!阿里云服务器采用 LNMP一键安装包 配置 Linux+Nginx+Mysql+PHP
  3. Linux搭建FTP服务器
  4. MySQL5.7官网参考手册路径
  5. 步进电机驱动器细分原理_步进驱动器细分设置表说明
  6. 2021最全最新java学习指南(第1-5节),干货必须分享!
  7. python爬虫框架论文开题报告范文_基于Web爬虫系统设计开题报告
  8. 记录一次破解某加固APP的修改纪录
  9. UI - PS如何导入不同的字体
  10. 微星z370黑苹果_[原创]黑苹果Hackintosh 10.13.5 High Sierra i7 8700k z370