{"id":13644068,"url":"https://github.com/soulrelay/ImageLoaderUtil","last_synced_at":"2025-04-21T06:32:54.693Z","repository":{"id":71919156,"uuid":"75285338","full_name":"soulrelay/ImageLoaderUtil","owner":"soulrelay","description":"图片加载库的封装案例","archived":false,"fork":false,"pushed_at":"2017-07-19T05:04:08.000Z","size":579,"stargazers_count":179,"open_issues_count":3,"forks_count":25,"subscribers_count":6,"default_branch":"master","last_synced_at":"2024-11-09T16:43:44.222Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"http://blog.csdn.net/s003603u/article/details/53436089","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/soulrelay.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2016-12-01T11:16:37.000Z","updated_at":"2024-06-01T17:13:36.000Z","dependencies_parsed_at":null,"dependency_job_id":"8be45075-261e-4b88-bcd1-3f19dc0bd3df","html_url":"https://github.com/soulrelay/ImageLoaderUtil","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/soulrelay%2FImageLoaderUtil","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/soulrelay%2FImageLoaderUtil/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/soulrelay%2FImageLoaderUtil/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/soulrelay%2FImageLoaderUtil/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/soulrelay","download_url":"https://codeload.github.com/soulrelay/ImageLoaderUtil/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250008282,"owners_count":21359958,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-02T01:01:57.099Z","updated_at":"2025-04-21T06:32:51.060Z","avatar_url":"https://github.com/soulrelay.png","language":"Java","readme":"# \u003cfont color=#C4573C size=5 face=\"黑体\"\u003e重磅更新\u003c/font\u003e\n使用ImageLoaderUtil实现一个真正意义的图集功能，持续完善和更新中\n\n[Gallery](https://github.com/soulrelay/Gallery)\n\n[Gallery](https://github.com/soulrelay/Gallery)\n\n[Gallery](https://github.com/soulrelay/Gallery)\n\n重要的东西贴三遍！\n\n----------------------------------分隔线---------------------------------------\n\n## \u003cfont color=#C4573C size=5 face=\"黑体\"\u003eUse Gradle\u003c/font\u003e\n```\nallprojects {\n    repositories {\n        jcenter()\n        maven {\n            name 'glide-snapshot'\n            url 'http://oss.sonatype.org/content/repositories/snapshots'\n        }\n    }\n}\n\ndependencies {\n    //compile project(':imagelib')\n    compile 'com.sus.library:imagelib:1.0.1'\n}\n```\n\n\n![这里写图片描述](https://github.com/soulrelay/ImageLoaderUtil/blob/master/imagelib/src/main/res/raw/catalog.png)\n\n## \u003cfont color=#C4573C size=5 face=\"黑体\"\u003e前言\u003c/font\u003e\n\u003e* 图片加载是Android开发中最最基础的功能，为了降低开发周期和难度，我们经常会选用一些图片加载的开源库\n\u003e* [选取第三方SDK需要谨慎](http://blog.csdn.net/s003603u/article/details/53257859)\n\u003e* [二次封装](http://blog.csdn.net/s003603u/article/details/53257965)\n\n\u003cfont color=#ff9866 size=4 face=\"黑体\"\u003e注意：所有改动更新会同步到\u003c/font\u003e[GitHub](https://github.com/soulrelay/ImageLoaderUtil)\n## \u003cfont color=#C4573C size=5 face=\"黑体\"\u003e主流图片加载库的对比\u003c/font\u003e\n\u003e* 共同点\n   * 使用简单：一句话实现图片的获取和显示\n   * 可配置性高：可配置各种解码、缓存、下载机制\n   * 自适应程度高：根据系统性能调整配置策略（如CPU核数决定最大并发数、内存决定内存缓存大小、网络状态变化调整最大并发数）\n   * 多级缓存\n   * 支持多种数据源\n   * 支持多种Displayer\n   * 兼容性好（可以配合okhttp等库进行使用）\n### \u003cfont color=#ff9866 size=4 face=\"黑体\"\u003eAndroid-Universal-Image-Loader\u003c/font\u003e\n\u003e* 简介\n      * 作者：nostra13\n      * 面世时间：2011\n      * star数（截止到发稿）：14509\n      * [https://github.com/nostra13/Android-Universal-Image-Loader](https://github.com/nostra13/Android-Universal-Image-Loader)\n\u003e*   优点\n       * 支持下载进度监听（ImageLoadingListener）\n       * 可在View滚动中暂停图片加载（PauseOnScrollListener）\n       * 默认实现多种内存缓存算法（最大最先删除，使用最少最先删除，最近最少使用，先进先删除，当然自己也可以配置缓存算法）\n\u003e* 缺点\n      * 从2015.11.27之后不再维护，项目中不建议使用\n \n### \u003cfont color=#ff9866 size=4 face=\"黑体\"\u003ePicasso\u003c/font\u003e\n\u003e* 简介\n      * 作者：JakeWharton（Square）\n      * 面世时间：2012\n      * star数（截止到发稿）：12076\n      * [https://github.com/square/picasso](https://github.com/square/picasso)\n\u003e*   优点\n       * 包较小（100k）\n       * 取消不在视野范围内图片资源的加载\n       * 使用最少的内存完成复杂的图片转换\n       * 自动添加二级缓存\n       * 任务调度优先级处理\n       * 并发线程数根据网络类型调整\n       * 图片的本地缓存交给同为Square出品的okhttp处理，控制图片的过期时间\n\u003e* 缺点\n      * 功能较为简单\n      * 自身无实现“本地缓存”\n\n### \u003cfont color=#ff9866 size=4 face=\"黑体\"\u003eGlide\u003c/font\u003e\n\u003e* 简介\n      * 作者：Sam sjudd (Google)\n      * 面世时间：2013\n      * star数（截止到发稿）：12067\n      * [https://github.com/bumptech/glide](https://github.com/bumptech/glide)\n\u003e*   优点\n       * 多种图片格式的缓存，适用于更多的内容表现形式（如Gif、WebP、缩略图、Video）\n       * 生命周期集成（根据Activity或者Fragment的生命周期管理图片加载请求）\n       * 高效处理Bitmap（bitmap的复用和主动回收，减少系统回收压力）\n       * 高效的缓存策略，灵活（Picasso只会缓存原始尺寸的图片，Glide缓存的是多种规格），加载速度快且内存开销小（默认Bitmap格式的不同，使得内存开销是Picasso的一半）\n\u003e* 缺点\n      * 方法较多较复杂，因为相当于在Picasso上的改进，包较大（500k），影响不是很大\n\n### \u003cfont color=#ff9866 size=4 face=\"黑体\"\u003eFresco\u003c/font\u003e\n\u003e* 简介\n      * 作者：Facebook\n      * 面世时间：2015\n      * star数（截止到发稿）：11235\n      * [https://github.com/facebook/fresco](https://github.com/facebook/fresco)\n\u003e*   优点\n       * 最大的优势在于5.0以下(最低2.3)的bitmap加载。在5.0以下系统，Fresco将图片放到一个特别的内存区域(Ashmem区)\n       * 大大减少OOM（在更底层的Native层对OOM进行处理，图片将不再占用App的内存）\n       * 适用于需要高性能加载大量图片的场景\n\u003e* 缺点\n      * 包较大（2~3M）\n      * 用法复杂\n      * 底层涉及c++领域，阅读源码深入学习难度大\n\n## \u003cfont color=#C4573C size=5 face=\"黑体\"\u003e按需选择图片加载库\u003c/font\u003e\n\u003e* 图片加载需要支持Gif，之前项目中使用的Android-Universal-Image-Loader不支持Gif且Android-Universal-Image-Loader已经停止维护，遂决定替换图片加载库\n\u003e* 分析完优缺点最终选择Glide的其它理由：\n  * Glide是在Picasso的基础上进行改进的（支持Gif，内存开销小），虽然500k左右的包大小相对于Picasso较大，但是这个数量级的影响可以接受\n  * 初衷是想一直维持图片的原始ImageView，而 Fresco需要在布局文件中将图片控件声明为库中自定义的SimpleDraweeView，如果切库还需要更改组件，代价会很高\n  * Google推荐（亲儿子），在Google很多开源项目中广泛使用\n\n\u003e*  但不可避免的是，Glide在使用的过程中依然存在着许多坑需要我们去填！\n\n## \u003cfont color=#C4573C size=5 face=\"黑体\"\u003e如何更好地封装图片加载库\u003c/font\u003e\n### \u003cfont color=#ff9866 size=4 face=\"黑体\"\u003e为什么要封装？\u003c/font\u003e\n先从现在面对的情形来看，项目中使用图片加载的地方都是使用的类似下面的语句\n```\nImageLoader.getInstance().displayImage(imageUrl, imageView，options);\n```\n然而现在ImageLoader已经停止维护且已经无法满足项目需求，我们需要替换，这时你会发现如果换库的话，所有涉及到的地方都要修改（Android-Universal-Image-Loader已经和图片加载的业务逻辑严重地耦合在一起了），工作量可见一斑，这就是不封装在切库时面临的窘境！\n那怎么解决那？\n计算机史上有个万能的解决方案就是，如果原有层面解决不了问题，那么就请再加一层！\n\n```\n/**\n * Created by soulrelay on 2016/10/11 13:42.\n * Class Note:\n * use this class to load image,single instance\n */\npublic class ImageLoaderUtil {\n\n    //图片默认加载类型 以后有可能有多种类型\n    public static final int PIC_DEFAULT_TYPE = 0;\n\n    //图片默认加载策略 以后有可能有多种图片加载策略\n    public static final int LOAD_STRATEGY_DEFAULT = 0;\n\n    private static ImageLoaderUtil mInstance;\n    \n    private BaseImageLoaderStrategy mStrategy;\n\n    public ImageLoaderUtil() {\n        mStrategy = new GlideImageLoaderStrategy();\n    }\n\n    //单例模式，节省资源\n    public static ImageLoaderUtil getInstance() {\n        if (mInstance == null) {\n            synchronized (ImageLoaderUtil.class) {\n                if (mInstance == null) {\n                    mInstance = new ImageLoaderUtil();\n                    return mInstance;\n                }\n            }\n        }\n        return mInstance;\n    }\n\n    /**\n     * 统一使用App context\n     * 可能带来的问题：http://stackoverflow.com/questions/31964737/glide-image-loading-with-application-context\n     *\n     * @param url\n     * @param placeholder\n     * @param imageView\n     */\n    public void loadImage(String url, int placeholder, ImageView imageView) {\n        mStrategy.loadImage(imageView.getContext(), url, placeholder, imageView);\n    }\n\n    public void loadGifImage(String url, int placeholder, ImageView imageView) {\n        mStrategy.loadGifImage(url, placeholder, imageView);\n    }\n\n    public void loadImage(String url, ImageView imageView) {\n        mStrategy.loadImage(url, imageView);\n    }\n\n  /**\n     * 展示图片加载进度\n     */\n    public void loadImageWithProgress(String url, ImageView imageView, ProgressLoadListener listener) {\n        mStrategy.loadImageWithProgress(url,imageView,listener);\n    }\n\n    public void loadGifWithProgress(String url, ImageView imageView, ProgressLoadListener listener) {\n        mStrategy.loadGifWithProgress(url,imageView,listener);\n    }\n\n    /**\n     * 策略模式的注入操作\n     *\n     * @param strategy\n     */\n    public void setLoadImgStrategy(BaseImageLoaderStrategy strategy) {\n        mStrategy = strategy;\n    }\n\n    /**\n     * 清除图片磁盘缓存\n     */\n    public void clearImageDiskCache(final Context context) {\n        mStrategy.clearImageDiskCache(context);\n    }\n\n    /**\n     * 清除图片内存缓存\n     */\n    public void clearImageMemoryCache(Context context) {\n        mStrategy.clearImageMemoryCache(context);\n    }\n\n    /**\n     * 根据不同的内存状态，来响应不同的内存释放策略\n     *\n     * @param context\n     * @param level\n     */\n    public void trimMemory(Context context, int level) {\n        mStrategy.trimMemory(context, level);\n    }\n\n    /**\n     * 清除图片所有缓存\n     */\n    public void clearImageAllCache(Context context) {\n        clearImageDiskCache(context.getApplicationContext());\n        clearImageMemoryCache(context.getApplicationContext());\n    }\n\n    /**\n     * 获取缓存大小\n     *\n     * @return CacheSize\n     */\n    public String getCacheSize(Context context) {\n        return mStrategy.getCacheSize(context);\n    }\n\n\n}\n```\n所有需要图片显示的地方使用如下方法进行调用：\n\n\u003e* 入口唯一，所有图片加载都在ImageLoaderUtil这一个地方统一管理，使用了[单例模式](http://blog.csdn.net/s003603u/article/details/51982140)(据说单元素的枚举类型已经成为实现Singleton的最佳方法，你可以试试 )，\n\u003e* 高效地封装减少了切库(只需要切换图片加载策略)带来的代价，默认采用GlideImageLoaderStrategy\n\n总结：外部表现一致，内部灵活处理原则。\n\n```\n/**\n * 图片加载库的封装演示案例\n * Created by soulrelay on 2016/12/11 19:18\n */\npublic class MainActivity extends AppCompatActivity {\n\n    @BindView(R.id.iv_normal)\n    ImageView ivNormal;\n    @BindView(R.id.iv_gif)\n    ImageView ivGif;\n    @BindView(R.id.iv_gif1)\n    ImageView ivGif1;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_main);\n        ButterKnife.bind(this);\n        initView();\n    }\n\n    private void initView() {\n        ImageLoaderUtil.getInstance().loadImage(\"http://image.sports.baofeng.com/25a3dbb0c99c5e48e52e60941ed230be\", R.drawable.bg_default_video_common_small, ivNormal);\n        ImageLoaderUtil.getInstance().loadImage(\"http://image.sports.baofeng.com/19ce5d6ac3b4fff255196f200b1d3079\", R.drawable.bg_default_video_common_small, ivGif);\n        ImageLoaderUtil.getInstance().loadGifImage(\"http://image.sports.baofeng.com/19ce5d6ac3b4fff255196f200b1d3079\", R.drawable.bg_default_video_common_small, ivGif1);\n\n    }\n\n}\n```\n\n效果图如下所示：\n![这里写图片描述](https://github.com/soulrelay/ImageLoaderUtil/blob/master/app/src/main/res/raw/pic_20170509.gif)\n### \u003cfont color=#ff9866 size=4 face=\"黑体\"\u003e使用策略模式封装图片加载策略\u003c/font\u003e\n如果你对策略模式不是很熟，请先参考[策略模式和状态模式](http://blog.csdn.net/s003603u/article/details/52033391)\n首先我们需要抽象出一个图片加载的基础接口BaseImageLoaderStrategy \n基本功能主要包括\n\u003e* 正常加载图片\n\u003e* 针对于GIF图片的特殊加载\n\u003e* 加载图片的进度回调\n\u003e* 清除缓存\n\u003e* 获取缓存大小等\n\u003e* 其它特殊需求自己封装，最好不要破坏策略模式的整体结构\n\n```\n/**\n * Created by soulrelay on 2016/10/11.\n * Class Note:\n * abstract class/interface defined to load image\n * (Strategy Pattern used here)\n */\npublic interface BaseImageLoaderStrategy {\n    //无占位图\n    void loadImage(String url, ImageView imageView);\n\n    void loadImage(String url, int placeholder, ImageView imageView);\n\n    void loadImage(Context context, String url, int placeholder, ImageView imageView);\n\n    void loadGifImage(String url, int placeholder, ImageView imageView);\n\n    void loadImageWithProgress(String url, ImageView imageView, ProgressLoadListener listener);\n\n    void loadGifWithProgress(String url, ImageView imageView, ProgressLoadListener listener);\n\n    //清除硬盘缓存\n    void clearImageDiskCache(final Context context);\n    //清除内存缓存\n    void clearImageMemoryCache(Context context);\n    //根据不同的内存状态，来响应不同的内存释放策略\n    void trimMemory(Context context, int level);\n    //获取缓存大小\n    String getCacheSize(Context context);\n\n}\n```\n需要说明的一点是：\n\u003e* 当封装的方法参数比较少时可以按照上述方式进行抽象，如果需要传递的参数较多，可以考虑使用建造者模式[建造者模式](http://blog.csdn.net/s003603u/article/details/51967809)\n\u003e* 例如封装一个ImageLoaderConfiguration，包含如下参数等等，将一个复杂对象的构建与它的表示分离，使得同样的构建过程可以创建不同的表示\n  * type 图片加载的类型（大图、小图、中图）\n  * url 需要解析的url\n  * placeHolder 当没有成功加载的时候显示的图片\n  * imgView ImageView的实例\n  * loadStrategy 加载策略\n\u003e* 当然这里我没有使用建造模式，考虑到目前使用的对象还不算复杂（传参比较简单），而且如果使用建造者模式有可能每次都要new一个新的对象实例，虽然开销可以接受\n\u003e* 使用ImageLoaderUtil的过程中，注意内存泄露的问题（静态单例的生命周期与App一样，当一个单例的对象长久不用时，不会被垃圾收集机制回收）\n\n然后基于每个图片库的各自方式来进行相应策略的封装，需要使用哪种策略，只需要通过ImageLoaderUtil的setLoadImgStrategy(BaseImageLoaderStrategy strategy)方法将相应的策略注入，相关类图关系如下所示：\n\n![这里写图片描述](http://img.blog.csdn.net/20161204205952522)\n\n不同的图片加载库实现不同的图片加载策略\n这里只是给出Glide的图片加载策略类GlideImageLoaderStrategy作为参考\n\u003e* Glide依赖v4包，且需要配置android.permission.INTERNET和android.permission.WRITE_EXTERNAL_STORAGE（忘记配置权限，图片加载不出来，还看不出什么异常）\n\u003e* 其中部分方法使用到了RequestListener的回调（这里是因为项目中的一些特殊需求而添加，如统计图片首次加载时长来测试一下图片cdn服务器的速度等）\n\u003e* 在使用Glide的过程中遇到了一些问题，部分已经在注释中说明\n\u003e* 之所以针对gif单独封装，是因为在使用的过程中发现，当在列表中加载大量gif会有OOM的问题，所以通过asGif进行特殊标明，即使这样也会出现类似问题，同时暂时通过skipMemoryCache(true)跳过内存缓存，之后有更好的办法会继续补充，各位看官如有良策，希望可以不吝赐教\n\u003e* Glide本身不提供图片的progress回调，所以关于进度回调的解决方案参照的是\n[ProgressGlide](https://github.com/shangmingchao/ProgressGlide)，并做了些许改动集成到项目中\n\u003e* 期间发现了一个很好的问题[Android的App中线程池的使用，具体使用多少个线程池？](https://www.zhihu.com/question/37804956)，其中一个答主的关于图片加载库线程池策略的分析很好，值得体会，简单摘录如下：\n  * UIL的线程池处理非常简单粗暴，没有根据CPU数量来选择，也没有根据网络状况的变化进行调整;\n  *  Picasso的线程池会根据网络状况的变化进行调整，在Wifi下线程数为4,而4G下线程数为3, 3G下为2， 2G下为1，默认状况为3；\n *  Glide加载缓存未命中的线程池会根据根据CPU的数量和Java虚拟机中可用的处理器数量来选择合适的线程数，但是最多不超过4;而加载缓存命中的图片的线程池默认大小为1.\n\n```\n/**\n * Created by soulrelay on 2016/10/11 13:48.\n * Class Note:\n * using {@link Glide} to load image\n */\npublic class GlideImageLoaderStrategy implements BaseImageLoaderStrategy {\n\n    @Override\n    public void loadImage(String url, int placeholder, ImageView imageView) {\n        loadNormal(imageView.getContext(), url, placeholder, imageView);\n    }\n\n    @Override\n    public void loadImage(Context context, String url, int placeholder, ImageView imageView) {\n        loadNormal(context, url, placeholder, imageView);\n    }\n\n    /**\n     * 无holder的gif加载\n     *\n     * @param url\n     * @param imageView\n     */\n    @Override\n    public void loadImage(String url, ImageView imageView) {\n        Glide.with(imageView.getContext()).load(url).dontAnimate()\n                .placeholder(imageView.getDrawable())\n                .diskCacheStrategy(DiskCacheStrategy.SOURCE)\n                .into(imageView);\n    }\n\n    @Override\n    public void loadGifImage(String url, int placeholder, ImageView imageView) {\n        loadGif(imageView.getContext(), url, placeholder, imageView);\n    }\n\n    @Override\n    public void loadImageWithProgress(String url, final ImageView imageView, final ProgressLoadListener listener) {\n        Glide.with(imageView.getContext()).using(new ProgressModelLoader(new ProgressUIListener() {\n            @Override\n            public void update(final int bytesRead, final int contentLength) {\n                imageView.post(new Runnable() {\n                    @Override\n                    public void run() {\n                        listener.update(bytesRead, contentLength);\n                    }\n                });\n            }\n        })).load(url).asBitmap().dontAnimate().\n                listener(new RequestListener\u003cObject, Bitmap\u003e() {\n                    @Override\n                    public boolean onException(Exception e, Object model, Target\u003cBitmap\u003e target, boolean isFirstResource) {\n                        listener.onException();\n                        return false;\n                    }\n\n                    @Override\n                    public boolean onResourceReady(Bitmap resource, Object model, Target\u003cBitmap\u003e target, boolean isFromMemoryCache, boolean isFirstResource) {\n                        listener.onResourceReady();\n                        return false;\n                    }\n                }).into(imageView);\n    }\n\n    @Override\n    public void loadGifWithProgress(String url, final ImageView imageView, final ProgressLoadListener listener) {\n        Glide.with(imageView.getContext()).using(new ProgressModelLoader(new ProgressUIListener() {\n            @Override\n            public void update(final int bytesRead, final int contentLength) {\n                imageView.post(new Runnable() {\n                    @Override\n                    public void run() {\n                        listener.update(bytesRead, contentLength);\n                    }\n                });\n            }\n        })).load(url).asGif().skipMemoryCache(true).dontAnimate().\n                listener(new RequestListener\u003cString, GifDrawable\u003e() {\n                    @Override\n                    public boolean onException(Exception e, String model, Target\u003cGifDrawable\u003e target, boolean isFirstResource) {\n                        listener.onException();\n                        return false;\n                    }\n\n                    @Override\n                    public boolean onResourceReady(GifDrawable resource, String model, Target\u003cGifDrawable\u003e target, boolean isFromMemoryCache, boolean isFirstResource) {\n                        listener.onResourceReady();\n                        return false;\n                    }\n                }).into(imageView);\n    }\n\n    @Override\n    public void clearImageDiskCache(final Context context) {\n        try {\n            if (Looper.myLooper() == Looper.getMainLooper()) {\n                new Thread(new Runnable() {\n                    @Override\n                    public void run() {\n                        Glide.get(context.getApplicationContext()).clearDiskCache();\n                    }\n                }).start();\n            } else {\n                Glide.get(context.getApplicationContext()).clearDiskCache();\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n    }\n\n    @Override\n    public void clearImageMemoryCache(Context context) {\n        try {\n            if (Looper.myLooper() == Looper.getMainLooper()) { //只能在主线程执行\n                Glide.get(context.getApplicationContext()).clearMemory();\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n    }\n\n    @Override\n    public void trimMemory(Context context, int level) {\n        Glide.get(context).trimMemory(level);\n    }\n\n    @Override\n    public String getCacheSize(Context context) {\n        try {\n            return CommonUtils.getFormatSize(CommonUtils.getFolderSize(Glide.getPhotoCacheDir(context.getApplicationContext())));\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n        return \"\";\n    }\n\n    /**\n     * load image with Glide\n     */\n    private void loadNormal(final Context ctx, final String url, int placeholder, ImageView imageView) {\n        /**\n         *  为其添加缓存策略,其中缓存策略可以为:Source及None,None及为不缓存,Source缓存原型.如果为ALL和Result就不行.然后几个issue的连接:\n         https://github.com/bumptech/glide/issues/513\n         https://github.com/bumptech/glide/issues/281\n         https://github.com/bumptech/glide/issues/600\n         modified by xuqiang\n         */\n\n        //去掉动画 解决与CircleImageView冲突的问题 这个只是其中的一个解决方案\n        //使用SOURCE 图片load结束再显示而不是先显示缩略图再显示最终的图片（导致图片大小不一致变化）\n        final long startTime = System.currentTimeMillis();\n        Glide.with(ctx).load(url).dontAnimate()\n                .placeholder(placeholder)\n                .diskCacheStrategy(DiskCacheStrategy.SOURCE).listener(new RequestListener\u003cString, GlideDrawable\u003e() {\n            @Override\n            public boolean onException(Exception e, String model, Target\u003cGlideDrawable\u003e target, boolean isFirstResource) {\n                return false;\n            }\n\n            @Override\n            public boolean onResourceReady(GlideDrawable resource, String model, Target\u003cGlideDrawable\u003e target, boolean isFromMemoryCache, boolean isFirstResource) {\n                return false;\n            }\n        })\n                .into(imageView);\n    }\n\n    /**\n     * load image with Glide\n     */\n    private void loadGif(final Context ctx, String url, int placeholder, ImageView imageView) {\n        final long startTime = System.currentTimeMillis();\n        Glide.with(ctx).load(url).asGif().dontAnimate()\n                .placeholder(placeholder).skipMemoryCache(true)\n                .diskCacheStrategy(DiskCacheStrategy.SOURCE).listener(new RequestListener\u003cString, GifDrawable\u003e() {\n            @Override\n            public boolean onException(Exception e, String model, Target\u003cGifDrawable\u003e target, boolean isFirstResource) {\n                return false;\n            }\n\n            @Override\n            public boolean onResourceReady(GifDrawable resource, String model, Target\u003cGifDrawable\u003e target, boolean isFromMemoryCache, boolean isFirstResource) {\n                return false;\n            }\n        })\n                .into(imageView);\n    }\n\n}\n\n```\n## \u003cfont color=#C4573C size=5 face=\"黑体\"\u003e源码地址\u003c/font\u003e\n[ImageLoaderUtil](https://github.com/soulrelay/ImageLoaderUtil)\n\n## \u003cfont color=#C4573C size=5 face=\"黑体\"\u003e部分参考链接\u003c/font\u003e\n[http://www.jianshu.com/p/97994c9693f9](http://www.jianshu.com/p/97994c9693f9)\n[https://www.zhihu.com/question/37804956](https://www.zhihu.com/question/37804956)\n[http://www.jianshu.com/p/e26130a93289](http://www.jianshu.com/p/e26130a93289)\n[http://www.cnblogs.com/android-blogs/p/5737611.html](http://www.cnblogs.com/android-blogs/p/5737611.html)\n## \u003cfont color=#C4573C size=5 face=\"黑体\"\u003e更新\u003c/font\u003e\n\n### \u003cfont color=#ff9866 size=4 face=\"黑体\"\u003e2016-12-09 ll You must not call setTag() on a view Glide is targeting\u003c/font\u003e\n\n\n\n项目中在使用Glide图片加载框架时遇到该错误\n报错原因大致是因为Glide加载的iamgeView调用了setTag()方法导致的错误，因为Glide已经默认为ImageView设置的Tag\n\n相关解决方案已经在Glide 3.6.0[（issue #370）](https://github.com/bumptech/glide/issues/370)被引进，实测可行\n在AndroidManifest.xml中加入\n\n```\n\u003capplication\n        android:name=\".App\"\u003e\n```\n然后在App中添加如下代码：\n\n```\npublic class App extends Application {\n    @Override public void onCreate() {\n        super.onCreate();\n        ViewTarget.setTagId(R.id.glide_tag);\n    }\n}\n```\n在src/main/values/ids.xml添加如下代码：\n\n```\n\u003cresources\u003e\n    \u003citem type=\"id\" name=\"glide_tag\" /\u003e\n\u003c/resources\u003e\n```\n\n\n### \u003cfont color=#ff9866 size=4 face=\"黑体\"\u003e2016-12-13 ll  添加loadGifWithPrepareCall方法\u003c/font\u003e\n2016.12.13\n\n只想知道图片是否准备完毕（包括来自网络或者sdcard），区别于loadImageWithProgress和loadGifWithProgress的进度回调\n\nTips：使用Glide加载图片注意ImageView的Scaletype的设置\n```\npublic interface BaseImageLoaderStrategy {\n    void loadGifWithPrepareCall(String url, ImageView imageView, SourceReadyListener listener);\n}\n```\n\n```\npublic class GlideImageLoaderStrategy implements BaseImageLoaderStrategy {\n\n       @Override\n    public void loadGifWithPrepareCall(String url, ImageView imageView, final SourceReadyListener listener) {\n        Glide.with(imageView.getContext()).load(url).asGif().dontAnimate()\n                .skipMemoryCache(true)\n                .diskCacheStrategy(DiskCacheStrategy.SOURCE).\n                listener(new RequestListener\u003cString, GifDrawable\u003e() {\n                    @Override\n                    public boolean onException(Exception e, String model, Target\u003cGifDrawable\u003e target, boolean isFirstResource) {\n                        return false;\n                    }\n\n                    @Override\n                    public boolean onResourceReady(GifDrawable resource, String model, Target\u003cGifDrawable\u003e target, boolean isFromMemoryCache, boolean isFirstResource) {\n                        listener.onResourceReady(resource.getIntrinsicWidth(),resource.getIntrinsicHeight());\n                        return false;\n                    }\n                }).into(imageView);\n    }\n}\n\n```\n\n```\npublic class ImageLoaderUtil {\n public void loadGifWithPrepareCall(String url, ImageView imageView, SourceReadyListener listener) {\n        mStrategy.loadGifWithPrepareCall(url,imageView,listener);\n    }\n}\n```\n### \u003cfont color=#ff9866 size=4 face=\"黑体\"\u003e2016-12-26 ll  更新loadGifWithProgress方法  \u003c/font\u003e\n#### \u003cfont color=#ff9866 size=4 face=\"黑体\"\u003e2017-1-10 ll  统一加载图片进度回调方法为loadImageWithProgress，弃用并删除loadGifWithProgress方法 \u003c/font\u003e\n具体细节查看GitHub最新代码\n### \u003cfont color=#ff9866 size=3 face=\"黑体\"\u003e2016-12-26 ll  自定义GlideModule 并将 Glide与okhttp3集成 \u003c/font\u003e\n 1.自定义一个GlideModule \n```\n/**\n * DES：自定义一个GlideModule\n * \u003cp\u003e\n * GlideModule 是一个抽象方法，全局改变 Glide 行为的一个方式，\n * 通过全局GlideModule 配置Glide，用GlideBuilder设置选项，用Glide注册ModelLoader等。\n * \u003cp\u003e\n */\npublic class MyGlideModule implements GlideModule {\n    @Override\n    public void applyOptions(Context context, GlideBuilder builder) {\n        // Apply options to the builder here.\n        int maxMemory = (int) Runtime.getRuntime().maxMemory();//获取系统分配给应用的总内存大小\n        int memoryCacheSize = maxMemory / 8;//设置图片内存缓存占用八分之一\n        //设置内存缓存大小\n        builder.setMemoryCache(new LruResourceCache(memoryCacheSize));\n        builder.setBitmapPool(new LruBitmapPool(memoryCacheSize));\n    }\n\n    @Override\n    public void registerComponents(Context context, Glide glide) {\n        // register ModelLoaders here.\n    }\n}\n```\n  2.AndroidManifest.xml注册\n```\n\u003cmanifest ...\u003e\n    \u003c!-- ... permissions --\u003e\n    \u003capplication ...\u003e\n             \u003c!-- 自定义GlideModule --\u003e\n        \u003cmeta-data\n            android:name=\"com.baofeng.soulrelay.utils.imageloader.MyGlideModule\"\n            android:value=\"GlideModule\" /\u003e\n        \u003c!-- 自定义GlideModule --\u003e\n        \u003c!-- ... activities and other components --\u003e\n    \u003c/application\u003e\n\u003c/manifest\u003e\n```\n3、 Glide与OkHttp3集成\n\n```\n   compile 'com.squareup.okhttp3:okhttp:3.4.2'\n   compile 'com.github.bumptech.glide:okhttp3-integration:1.4.0@aar'\n```\n4、添加混淆处理\n\n```\n#--------------------Glide-----------------------#\n-keep public class * implements com.bumptech.glide.module.GlideModule\n-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {\n  **[] $VALUES;\n  public *;\n}\n\n-keepnames class com.baofeng.soulrelay.utils.imageloader.MyGlideModule\n# or more generally:\n-keep public class * implements com.bumptech.glide.module.GlideModule\n-keep class com.bumptech.glide.integration.okhttp3.OkHttpGlideModule\n\n#--------------------Glide-----------------------#\n```\n\n### \u003cfont color=#ff9866 size=4 face=\"黑体\"\u003e2017-1-6 ll  GIF帧显示不完全 2017-1-10补充说明PS\u003c/font\u003e\n相关问题在[issues1649](https://github.com/bumptech/glide/issues/1649)中被提到和解决（目前glide:3.7.0的确存在这个问题）\n具体解决方法是：\n glide:3.8.0-SNAPSHOT修复了关于GIF展示的一些bug，实测可用\n Gradle配置修改如下：\n \u003e* Add the snapshot repo to your list of repositories:\n```\nrepositories {\n  jcenter()\n  maven {\n    name 'glide-snapshot'\n    url 'http://oss.sonatype.org/content/repositories/snapshots'\n  }\n}\n```\n\n\n\u003e* And then change your dependencies to the v3 snapshot version:\n\n```\ndependencies {\n  compile 'com.github.bumptech.glide:glide:3.8.0-SNAPSHOT'\n  compile 'com.github.bumptech.glide:okhttp-integration:1.5.0-SNAPSHOT'\n}\n```\n#### \u003cfont color=#ff9866 size=3 face=\"黑体\"\u003e2017-1-10补充说明PS\u003c/font\u003e\nPS：提供一个gif图 帧提取工具[GIFFrame.exe](http://download.csdn.net/detail/s003603u/9733492)\n据我分析，那些没有显示完整的GIF图片，里面的部分帧图片本身就不是完整的，但是之前的Glide并没有做很好的处理，所以显示效果有缺陷，当然最新的3.8.0-SNAPSHOT解决了这个问题，但是在显示的时候仍有瑕疵（有一些重叠，当然我觉得这也跟gif图的做工有关）\n### \u003cfont color=#ff9866 size=4 face=\"黑体\"\u003e2017-1-6 ll  You cannot start a load for a destroyed activity\u003c/font\u003e\n完整异常信息：\n```\nFATAL EXCEPTION: main\nProcess: com.sports.baofeng, PID: 9170\njava.lang.IllegalArgumentException: You cannot start a load for a destroyed activity\nat com.bumptech.glide.d.k.b(SourceFile:134)\nat com.bumptech.glide.d.k.a(SourceFile:102)\nat com.bumptech.glide.d.k.a(SourceFile:87)\nat com.bumptech.glide.i.c(SourceFile:629)\nat com.storm.durian.common.utils.imageloader.b.a(SourceFile:1194)\nat com.storm.durian.common.utils.imageloader.c.a(SourceFile:52)\nat com.sports.baofeng.specialtopic.SpecialTopicDetailFixActivity.a(SourceFile:311)\nat com.sports.baofeng.specialtopic.SpecialTopicDetailFixActivity.a(SourceFile:1347)\nat com.sports.baofeng.specialtopic.d.a(SourceFile:1052)\nat com.sports.baofeng.specialtopic.c$1.a(SourceFile:1064)\nat com.storm.durian.common.b.b$1.onPostExecute(SourceFile:57)\nat android.os.AsyncTask.finish(AsyncTask.java:651)\nat android.os.AsyncTask.access$500(AsyncTask.java:180)\nat android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:668)\nat android.os.Handler.dispatchMessage(Handler.java:102)\nat android.os.Looper.loop(Looper.java:158)\nat android.app.ActivityThread.main(ActivityThread.java:7225)\nat java.lang.reflect.Method.invoke(Native Method)\nat com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230)\nat com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120)\n\n```\n以上异常出现的几率为random\n初步分析原因是：进入页面然后又迅速退出，导致AsyncTask中的onPostExecute在调用Glide加载图片时出现如上异常，当然也跟对AsyncTask的管理有关\n同样的问题参考[issues138](https://github.com/bumptech/glide/issues/138)\n简单的摘要：\n\n\u003e 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\n\n按照上面这个理解的话，如果是在AsyncTask中的onPostExecute执行时\n调用Glide加载图片，context最好使用ApplicationContext\n\n对[ImageLoaderUtil](https://github.com/soulrelay/ImageLoaderUtil)做如下更新，添加方法\n\u003e* BaseImageLoaderStrategy\n```\n //这里的context指定为ApplicationContext\n    void loadImageWithAppCxt(String url, ImageView imageView);\n```\n\n\u003e* GlideImageLoaderStrategy\n```\n  @Override\n    public void loadImageWithAppCxt(String url, ImageView imageView) {\n        Glide.with(imageView.getContext().getApplicationContext()).load(url).dontAnimate()\n                .placeholder(imageView.getDrawable())\n                .diskCacheStrategy(DiskCacheStrategy.SOURCE)\n                .into(imageView);\n    }\n```\n\u003e* ImageLoaderUtil\n```\n public void loadImageWithAppCxt(String url, ImageView imageView) {\n        mStrategy.loadImageWithAppCxt(url,imageView);\n    }\n```\n\n### \u003cfont color=#ff9866 size=4 face=\"黑体\"\u003e2017-1-10 ll  简单说说图片适配的问题\u003c/font\u003e\n过多的概念不赘述，可以先参考[Android屏幕适配全攻略(最权威的官方适配指导)](http://blog.csdn.net/zhaokaiqiang1992/article/details/45419023)\n这里主要描述一种现象，明白的话自然觉得很简单！\n\n```\n    \u003cImageView\n                android:id=\"@+id/iv_gif\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:scaleType=\"centerInside\"\n                 /\u003e\n```\n假设现在要加载一张200px * 200px的GIF图片（图片基于1280 * 720），这张图片的宽高设置为wrap_content，如果在1920 * 1080分辨率的手机上显示，相对于1280 * 720（假设屏幕尺寸相同），在视觉效果上会显得小，这其实是Android系统基于手机像素密度的一种自适配，单一变化条件下，1920 * 1080分辨率的手机的像素密度是1280 * 720的1.5倍\n假设如果系统的自适配让你觉得在高分辨率手机上显得图片过小（像素密度高，200个像素显示起来就比较挤），可以通过自己的计算来改变这种现象\nImageLoaderUtil提供如下加载成功回调的方法（并且会把图片的宽高告诉你）：这里有个参数设置，看需求来计算，粗略点可以只使用宽度比例来算，如下面的例子显示，参数为AppParams.screenWidth / 720，当然也可以获取屏幕密度，1920 * 1080的屏幕密度为3，1280 * 720的为2，所以参数可以设置为AppParams.density/2（在两种分辨率上看着视觉上一样）\n\n```\n    ImageLoaderUtil.getInstance().loadGifWithPrepareCall(url, mImageView, new SourceReadyListener() {\n                @Override\n                public void onResourceReady(int width, int height) {\n                    ViewGroup.LayoutParams params = mImageView.getLayoutParams();\n                      params.height = height * AppParams.screenWidth / 720;\n                    params.width = width * AppParams.screenWidth / 720;\n                    mImageView.setLayoutParams(params);\n                    progressBar.setVisibility(View.GONE);\n                }\n            });\n```\n\n### \u003cfont color=#ff9866 size=4 face=\"黑体\"\u003e2017-1-10 ll  添加saveImage方法，实现图片的本地自定义保存功能\u003c/font\u003e\n已同步到[GitHub ImageLoaderUtil](https://github.com/soulrelay/ImageLoaderUtil)\n\u003e* ImageLoaderUtil相关接口：\n\n```\n  /**\n     * @param context\n     * @param url 图片url\n     * @param savePath 保存路径\n     * @param saveFileName 保存文件名\n     * @param listener 文件保存成功与否的监听器\n     */\n public void saveImage(Context context, String url, String savePath, String saveFileName, ImageSaveListener listener) {\n        mStrategy.saveImage(context, url, savePath, saveFileName, listener);\n    }\n```\n\n\u003e*  在工作线程中调用示例如下：\n\n```\nImageLoaderUtil.getInstance().saveImage(getActivity(), url,\n                        Environment.getExternalStorageDirectory().getAbsolutePath() + \"/bfsports\",\n                        \"bfsports\" + System.currentTimeMillis(), new ImageSaveListener() {\n                            @Override\n                            public void onSaveSuccess() {\n                                handler.obtainMessage(MSG_PIC_SAVE_SUCC).sendToTarget();\n                            }\n\n                            @Override\n                            public void onSaveFail() {\n                                handler.obtainMessage(MSG_PIC_SAVE_FAIL).sendToTarget();\n                            }\n                        });\n```\n\n### \u003cfont color=#ff9866 size=4 face=\"黑体\"\u003e2017-03-08 ll   Glide圆形图片加载封装\n\u003e* 之前加载圆形图片，一般都是采用自定义的CircleImageview。后来开始使用Glide加载图片，期间遇到Glide和CircleImageview使用冲突的问题（如：有的图片第一次加载的时候只显示占位图，第二次才显示正常的图片，以及CircleImageview带来的崩溃问题），当时采用了一个牺牲动画效果的解决方案\n\u003e* 既然已经全面使用Glide，那么就尽量基于Glide来完成加载圆形图片的方案\n\u003e* 具体方案请参考[Glide圆形图片加载封装](http://blog.csdn.net/s003603u/article/details/60880308)\n### \u003cfont color=#ff9866 size=4 face=\"黑体\"\u003e2017-05-09 ll   圆形图片加载优化，更新简单使用案例","funding_links":[],"categories":["图片"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsoulrelay%2FImageLoaderUtil","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsoulrelay%2FImageLoaderUtil","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsoulrelay%2FImageLoaderUtil/lists"}