关闭 x
IT技术网
    技 采 号
    ITJS.cn - 技术改变世界
    • 实用工具
    • 菜鸟教程
    IT采购网 中国存储网 科技号 CIO智库

    IT技术网

    IT采购网
    • 首页
    • 行业资讯
    • 系统运维
      • 操作系统
        • Windows
        • Linux
        • Mac OS
      • 数据库
        • MySQL
        • Oracle
        • SQL Server
      • 网站建设
    • 人工智能
    • 半导体芯片
    • 笔记本电脑
    • 智能手机
    • 智能汽车
    • 编程语言
    IT技术网 - ITJS.CN
    首页 » 安卓开发 »Android ImageLoader框架之图片加载与加载策略

    Android ImageLoader框架之图片加载与加载策略

    2015-04-05 00:00:00 出处:Mr.Simple的专栏
    分享

    在Android ImageLoader框架之初始配置与请求调度中,我们已经讲述了ImageLoader的请求配置与调度相关的设计与实现。今天我们就来深入了解图片的具体加载过程以及加载的策略(包括按顺序加载和逆序加载) ,在这其中我会分享我的一些设计决策,也欢迎大家给我提建议。

    图片的加载

    Loader与LoaderManager的实现

    在上一篇文章Android ImageLoader框架之初始配置与请求调度中,我们聊到了Loader与LoaderManager。 ImageLoader不断地从队列中获取请求,然后解析到图片uri的schema,从schema的格式就可以知道它是存储在哪里的图片。例如网络图片对象的schema是http或者https,sd卡存储的图片对应的schema为file,schemae与Loader有一个对应关系。根据schema我们从LoaderManager中获取对应的Loader来加载图片。这个设计保证了SimpleImageLoader可加载图片类型的可扩展性,这就是为什么会增加loader这个包的原因。用户只需要根据uri的格式来构造图片uri,并且实现自己的Loader类,然后将Loader对象注入到LoaderManager即可。RequestDispatcher中的run函数如下 :

     @Override
        public void run() {
            try {
                while (!this.isInterrupted()) {
                    final BitmapRequest request = mRequestQueue.take();
                    if (request.isCancel) {
                        continue;
                    }
    
                    final String schema = parseSchema(request.imageUri);
                    // 根据schema获取loader
                    Loader imageLoader = LoaderManager.getInstance().getLoader(schema);
                    imageLoader.loadImage(request);
                }
            } catch (InterruptedException e) {
                Log.i("", "### 请求分发器退出");
            }
        }

    Loader只定义了一个接口,只用一个加载图片的方法。

    public interface Loader {
        public void loadImage(BitmapRequest result);
    }

    抽象是为了可扩展,定义这个接口,我们就可以注入自己的图片加载实现类。例如从资源、assets中加载。不管从网络还是本地加载图片,我们加载图片的过程有如下几个步骤:

    判断缓存中是否含有该图片; 假如有则将图片直接投递到UI线程,并且更新UI; 假如没有缓存,则从对应的地方获取到图片,并且将图片缓存起来,然后再将结果投递给UI线程,更新UI;

    我们可以发现,不管从哪里加载图片,这些逻辑都是通用的,因此我抽象了一个AbsLoader类。它将这几个过程抽象起来,只将变化的部分交给子类处理,就相当于AbsLoader封装了一个逻辑框架( 可以思考用了什么设计模式),大致代码如下 :

    /**
     * @author mrsimple
     */
    public abstract class AbsLoader implements Loader {
    
        /**
         * 图片缓存
         */
        private static BitmapCache mCache = SimpleImageLoader.getInstance().getConfig().bitmapCache;
    
        @Override
        public final void loadImage(BitmapRequest request) {
            // 1、从缓存中获取
            Bitmap resultBitmap = mCache.get(request);
            Log.e("", "### 是否有缓存 : " + resultBitmap + ", uri = " + request.imageUri);
            if (resultBitmap == null) {
                showLoading(request);
                // 2、没有缓存,调用onLoaderImage加载图片
                resultBitmap = onLoadImage(request);
                // 3、缓存图片
                cacheBitmap(request, resultBitmap);
            } else {
                request.justCacheInMem = true;
            }
            // 4、将结果投递到UI线程
            deliveryToUIThread(request, resultBitmap);
        }
    
        /** 加载图片的hook方法,留给子类处理
         * @param request
         * @return
         */
        protected abstract Bitmap onLoadImage(BitmapRequest request);
        // 代码省略
        }

    代码逻辑如上所述实现了一个模板函数,变化的部分就是onLoadImage,子类在这里实现真正的加载图片的方法。比如从网络上加载图片。

    /**
     * @author mrsimple
     */
    public class UrlLoader extends AbsLoader {
    
        @Override
        public Bitmap onLoadImage(BitmapRequest request) {
            final String imageUrl = request.imageUri;
            FileOutputStream fos = null;
            InputStream is = null;
            try {
                URL url = new URL(imageUrl);
                final HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                is = new BufferedInputStream(conn.getInputStream());
                is.mark(is.available());
    
                final InputStream inputStream = is;
                BitmapDecoder bitmapDecoder = new BitmapDecoder() {
    
                    @Override
                    public Bitmap decodeBitmapWithOption(Options options) {
                        Bitmap bitmap = BitmapFactory.decodeStream(inputStream, null, options);
                        //
                        if (options.inJustDecodeBounds) {
                            try {
                                inputStream.reset();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        } else {
                            // 关闭流
                            conn.disconnect();
                        }
                        return bitmap;
                    }
                };
    
                return bitmapDecoder.decodeBitmap(request.getImageViewWidth(),
                        request.getImageViewHeight());
            } catch (Exception e) {
    
            } finally {
                IOUtil.closeQuietly(is);
                IOUtil.closeQuietly(fos);
            }
    
            return null;
        }
    
    }

    在初始化ImageLoader时我们会默认将几个Loader注入到LoaderManager中,然后在加载图片时ImageLoader会根据图片的schema来获取对应Loader来完成加载功能。

        /**
         * 
         */
        private LoaderManager() {
            register(HTTP, new UrlLoader());
            register(HTTPS, new UrlLoader());
            register(FILE, new LocalLoader());
        }

    加载策略

    加载策略就是你的图片加载请求提交以后ImageLoader按照一个什么规则来加载你的请求。默认就是SerialPolicy策略(FIFO),谁在队列前面就是谁优先被执行。但是事情往往没有那么简单,我们在Listview滚动时,我们希望最后添加到请求队列的图片优先得了加载,因此此时它们就在手机屏幕上,所以我们又添加了一个ReversePolicy策略。咦,对于这种存在各种可能性的部分,我们最不能具体化,还是要抽象!于是我定义了LoadPolicy接口,它的作用是compare两个请求,以此来规定排序原则。

    public interface LoadPolicy {
        public int compare(BitmapRequest request1, BitmapRequest request2);
    }

    因为我们的请求队列使用的是优先级队列PriorityBlockingQueue,因此我们的BitmapRequest都实现了 Comparable 接口,我们在BitmapRequest的函数中将compareTo委托给LoadPolicy对象的compare。

        @Override
        public int compareTo(BitmapRequest another) {
            return mLoadPolicy.compare(this, another);
        }

    我们看看默认的加载策略,即按顺序加载,先添加到队列的请求先被执行。

    /**
     * 顺序加载策略
     * 
     * @author mrsimple
     */
    public class SerialPolicy implements LoadPolicy {
    
        @Override
        public int compare(BitmapRequest request1, BitmapRequest request2) {
            // 那么按照添加到队列的序列号顺序来执行
            return request1.serialNum - request2.serialNum;
        }
    
    }

    逆序加载则为 :

    /**
     * 逆序加载策略,即从最后加入队列的请求进行加载
     * 
     * @author mrsimple
     */
    public class ReversePolicy implements LoadPolicy {
    
        @Override
        public int compare(BitmapRequest request1, BitmapRequest request2) {
            // 注意Bitmap请求要先执行最晚加入队列的请求,ImageLoader的策略
            return request2.serialNum - request1.serialNum;
        }
    }

    呵,想想这不是策略模式么!原来模式无处不在,当你习惯之后你就会发现模式在无形之中已经运用到你的代码了。如上所示,策略都是简单的实现,这个策略只需要在配置ImageLoader时指定就行了,用户也可以根据自己的需求来实现策略类,并且注入给ImageLoader。这样就保证了灵活性、可扩展性。

    总结

    通过Loader和LoaderManager保证了可加载图片来源的扩展性,即图片可以存储在网络上、sd卡中、res文件夹中等等,实现一个从特定位置加载图片的Loader,然后给这个Loader注册一个schema,在加载图片的时候根据图片的路径获取schema,再通过schema获取Loader,通过Loader加载图片。

    而图片的加载策略又通过LoadPolicy这个抽象来定制,用户可以自行实现加载策略。这样就保证了灵活性,当然还有后期的图片缓存也是需要同样的灵活性。和我在公共技术点之面向对象六大原则所说,面向对象的几大原则最终化为几个简单的关键字: : 抽象、单一职责、最小化。领悟到了这些思想,我想你的代码质量应该会有一个质的提升。

    ImageLoader库,图片缓存肯定必不可少。关于图片的缓存设计,还是那句老话,待我下回讲解~

    Github地址

    SimpleImageLoader。

    上一篇返回首页 下一篇

    声明: 此文观点不代表本站立场;转载务必保留本文链接;版权疑问请联系我们。

    别人在看

    hiberfil.sys文件可以删除吗?了解该文件并手把手教你删除C盘的hiberfil.sys文件

    Window 10和 Windows 11哪个好?答案是:看你自己的需求

    盗版软件成公司里的“隐形炸弹”?老板们的“法务噩梦” 有救了!

    帝国CMS7.5编辑器上传图片取消宽高的三种方法

    帝国cms如何自动生成缩略图的实现方法

    Windows 12即将到来,将彻底改变人机交互

    帝国CMS 7.5忘记登陆账号密码怎么办?可以phpmyadmin中重置管理员密码

    帝国CMS 7.5 后台编辑器换行,修改回车键br换行为p标签

    Windows 11 版本与 Windows 10比较,新功能一览

    Windows 11激活产品密钥收集及专业版激活方法

    IT头条

    智能手机市场风云:iPhone领跑销量榜,华为缺席引争议

    15:43

    大数据算法和“老师傅”经验叠加 智慧化收储粮食尽显“科技范”

    15:17

    严重缩水!NVIDIA将推中国特供RTX 5090 DD:只剩24GB显存

    00:17

    无线路由大厂 TP-Link突然大裁员:补偿N+3

    02:39

    Meta 千万美金招募AI高级人才

    00:22

    技术热点

    微软已修复windows 7/windows 8.1媒体中心严重漏洞 用户可下载安

    卸载MySQL数据库,用rpm如何实现

    windows 7中使用网上银行或支付宝支付时总是打不开支付页面

    一致性哈希算法原理设计

    MySQL数字类型中的三种常用种类

    如何解决SQL Server中传入select语句in范围参数

      友情链接:
    • IT采购网
    • 科技号
    • 中国存储网
    • 存储网
    • 半导体联盟
    • 医疗软件网
    • 软件中国
    • ITbrand
    • 采购中国
    • CIO智库
    • 考研题库
    • 法务网
    • AI工具网
    • 电子芯片网
    • 安全库
    • 隐私保护
    • 版权申明
    • 联系我们
    IT技术网 版权所有 © 2020-2025,京ICP备14047533号-20,Power by OK设计网

    在上方输入关键词后,回车键 开始搜索。Esc键 取消该搜索窗口。