SkillAgentSearch skills...

ImageLoaderUtil

图片加载库的封装案例

Install / Use

/learn @soulrelay/ImageLoaderUtil
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

<font color=#C4573C size=5 face="黑体">重磅更新</font>

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

Gallery

Gallery

Gallery

重要的东西贴三遍!

----------------------------------分隔线---------------------------------------

<font color=#C4573C size=5 face="黑体">Use Gradle</font>

allprojects {
    repositories {
        jcenter()
        maven {
            name 'glide-snapshot'
            url 'http://oss.sonatype.org/content/repositories/snapshots'
        }
    }
}

dependencies {
    //compile project(':imagelib')
    compile 'com.sus.library:imagelib:1.0.1'
}

这里写图片描述

<font color=#C4573C size=5 face="黑体">前言</font>

<font color=#ff9866 size=4 face="黑体">注意:所有改动更新会同步到</font>GitHub

<font color=#C4573C size=5 face="黑体">主流图片加载库的对比</font>

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

<font color=#ff9866 size=4 face="黑体">Android-Universal-Image-Loader</font>

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

<font color=#ff9866 size=4 face="黑体">Picasso</font>

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

<font color=#ff9866 size=4 face="黑体">Glide</font>

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

<font color=#ff9866 size=4 face="黑体">Fresco</font>

  • 简介 * 作者: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++领域,阅读源码深入学习难度大

<font color=#C4573C size=5 face="黑体">按需选择图片加载库</font>

  • 图片加载需要支持Gif,之前项目中使用的Android-Universal-Image-Loader不支持Gif且Android-Universal-Image-Loader已经停止维护,遂决定替换图片加载库
  • 分析完优缺点最终选择Glide的其它理由:
  • Glide是在Picasso的基础上进行改进的(支持Gif,内存开销小),虽然500k左右的包大小相对于Picasso较大,但是这个数量级的影响可以接受
  • 初衷是想一直维持图片的原始ImageView,而 Fresco需要在布局文件中将图片控件声明为库中自定义的SimpleDraweeView,如果切库还需要更改组件,代价会很高
  • Google推荐(亲儿子),在Google很多开源项目中广泛使用
  • 但不可避免的是,Glide在使用的过程中依然存在着许多坑需要我们去填!

<font color=#C4573C size=5 face="黑体">如何更好地封装图片加载库</font>

<font color=#ff9866 size=4 face="黑体">为什么要封装?</font>

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

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

然而现在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);
    }


}

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

  • 入口唯一,所有图片加载都在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;

    @Override
    protected 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);

    }

}

效果图如下所示: 这里写图片描述

<font color=#ff9866 size=4 face="黑体">使用策略模式封装图片加载策略</font>

如果你对策略模式不是很熟,请先参考策略模式和状态模式 首先我们需要抽象出一个图片加载的基础接口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);

}

需要说明的一点是:

  • 当封装的方法参数比较少时可以按照上述方式进行抽象,如果需要传递的参数较多,可以考虑使用建造者模式建造者模式
  • 例如封装一个ImageLoaderConfiguration,包含如下参数等等,将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示
  • type 图片加载的

Related Skills

View on GitHub
GitHub Stars181
CategoryDevelopment
Updated1y ago
Forks25

Languages

Java

Security Score

60/100

Audited on Dec 24, 2024

No findings