文档章节

KJFrameForAndroid框架学习----高效设置网络图片

kymjs张涛
 kymjs张涛
发布于 2014/07/25 21:05
字数 1968
阅读 3320
收藏 16

 KJFrameForAndroid框架项目地址:http://git.oschina.net/kymjs/KJFrameForAndroid

    我们都知道,计算机读取数据时:内存的读取速度是最快的,然后是文件的读取速度,最后是网络资源的读取。

    如果每次加载同一张图片都要从网络获取,那代价实在太大了。所以同一张图片只要从网络获取一次就够了,然后在本地缓存起来,之后加载同一张图片时就 从缓存中加载就可以了。
从内存缓存读取图片是最快的,但是因为Android对每个应用所能使用的内存容量都有限制,所以最好再加上文件缓存。文件缓存空间也不是无限大的,容量越大读取效率越低,这个很好理解,从沙漠中找出丢失的一根针和从盘子中找到一根针,哪个容易一想即知。因此我们常设置一个限定大小比如10M。

所以,加载图片的流程应该是:
    1、先从内存缓存中获取,取到则返回,取不到则进行下一步;
    2、从文件缓存中获取,取到则返回并更新到内存缓存,取不到则进行下一步;
    3、从网络下载图片,并更新到内存缓存和文件缓存。

如果您只想了解文件缓存与内存缓存公用,请查看下一篇博文。


    在过去,我们经常会使用一种非常流行的内存缓存技术的实现,即软引用或弱引用 (SoftReference or WeakReference)。但是根据Google的描述:现在已经不再推荐使用这种方式了,因为从 Android 2.3 (API Level 9)开始,垃圾回收器会更倾向于回收持有软引用或弱引用的对象,这让软引用和弱引用变得不再可靠。另外,Android 3.0 (API Level 11)中,图片的数据会存储在本地的内存当中,因而无法用一种可预见的方式将其释放,这就有潜在的风险造成应用程序的内存溢出并崩溃。

    因此,我们更多的是去使用lru算法(Least Recently Used 近期最少使用算法)最初这种算法是用在操作系统调度上的。他的原理是通过个线性表存储数据,并记录数据每次调用次数,越常用到的排名就越靠前,越少用到的排名就越靠后,如果是一个新加入的数据,就会把它放在第一位,然后移除掉排名最后一位的数据。这里是KJFrameForAndroid框架中关于内存lru算法的实现方式 LruMemoryCache

既然是加载网络图片,那么当然需要加载的控件和网络图片地址作为参数,示例如下所示

private void loadImage(ImageView imageView, String imageUrl) {
    // 首先访问内存缓存,判断图片是否已经存在
    Bitmap bitmap = mMemoryCache.get(StringUtils.md5(imageUrl));
    if (bitmap != null) {
            imageView.setImageBitmap(bitmap);
        }else{ 
        //否则就去网络下载
        BitmapWorkerTask task = new BitmapWorkerTask(imageView);
            task.execute(imageUrl);
    }
}

    至于实际下载的方法,我就不详细讲解了,相信大家都能想到,就是一个网络请求,然后下载图片,再转成bitmap,最后设置为控件图片。然而这里有一个需要注意的重要地方,就是当我们把图片下载成功后要记得在mMemoryCache中缓存起来。
这里是KJFraemForAndroid应用开发框架中的一段网络图片加载的代码:

    /**
     * 加载图片(核心方法)
     * 
     * @param imageView
     *            要显示图片的控件(ImageView设置src,普通View设置bg)
     * @param imageUrl
     *            图片的URL
     */
    private void loadImage(View imageView, String imageUrl) {
        if (config.callBack != null)
            config.callBack.imgLoading(imageView);
        Bitmap bitmap = mMemoryCache.get(StringUtils.md5(imageUrl));
        if (bitmap != null) {
            if (imageView instanceof ImageView) {
                ((ImageView) imageView).setImageBitmap(bitmap);
            } else {
                imageView.setBackgroundDrawable(new BitmapDrawable(bitmap));
            }
            if (config.callBack != null)
                config.callBack.imgLoadSuccess(imageView);
            if (config.isDEBUG)
                KJLoger.debugLog(getClass().getName(),
                        "download success, from memory cache\n" + imageUrl);
        } else {
            if (imageView instanceof ImageView) {
                ((ImageView) imageView).setImageBitmap(config.loadingBitmap);
            } else {
                imageView.setBackgroundDrawable(new BitmapDrawable(
                        config.loadingBitmap));
            }
            BitmapWorkerTask task = new BitmapWorkerTask(imageView);
            taskCollection.add(task);
            task.execute(imageUrl);
        }
    }

    /********************* 异步获取Bitmap并设置image的任务类 *********************/
    private class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap> {
        private View imageView;

        public BitmapWorkerTask(View imageview) {
            this.imageView = imageview;
        }

        @Override
        protected Bitmap doInBackground(String... params) {
            Bitmap bitmap = null;
            byte[] res = downloader.loadImage(params[0]);
            if (res != null) {
                bitmap = BitmapCreate.bitmapFromByteArray(res, 0, res.length,
                        config.width, config.height);
            }
            if (bitmap != null && config.openMemoryCache) {
                // 图片载入完成后缓存到LrcCache中
                putBitmapToMemory(params[0], bitmap);
                if (config.isDEBUG)
                    KJLoger.debugLog(getClass().getName(),
                            "put to memory cache\n" + params[0]);
            }
            return bitmap;
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            super.onPostExecute(bitmap);
            if (imageView instanceof ImageView) {
                if (bitmap != null) {
                    ((ImageView) imageView).setImageBitmap(bitmap);
                }
            } else {
                imageView.setBackgroundDrawable(new BitmapDrawable(bitmap));
            }
            if (config.callBack != null)
                config.callBack.imgLoadSuccess(imageView);
            taskCollection.remove(this);
        }
    }


深入理解图片加载在实际项目中的应用

    以上只是网络图片加载并缓存的基本操作,那么我们如果在实际项目中使用必须考虑到代码的完备性与可扩展性。

    ①比如我们想指定图片的大小,虽然我们可以通过设置view的固定宽高来强制图片的显示大小,但如果是一张几兆的图片,而我们只需要15*15分辨率大小的显示区域,这显然是浪费的;

    ②又比如,我们希望控件在网络正在下载图片时先显示一个默认的图片(比如一个灰色的头像)又或者是图片下载的时候显示一个环形的进度条,那么上面的代码是没有办法的;

    ③再比如,我们希望图片的下载方式有多种,对于不同网站来源有不同的下载方式。。。。


这些种种特殊的需求告诉我们,上面的代码完全没有办法做到。那么为了控件的完备性与可扩展性,我们就需要一个配置器、一个显示器、一个下载器。。等等根据特殊需要而添加的插件式开发。

    因此,我们可以看到在KJFrameForAndroid框架的org.kymjs.aframe.bitmap包下有着KJBitmapConfig、I_ImageLoder、I_Display等等用final修饰的类或者协议接口。

    比如KJBitmapConfig类,是一个用final修饰的配置器类,通过这个配置器,我们就可以动态的对每一张下载的图片设置宽高、以及内存大小等。而I_ImageLoder、I_Display则是两个协议接口,分别定义了下载器和显示器的方法,这里实际上是GoF设计模式中工厂方法模式的应用,只是这里的工厂实际上并不是用来创建对象,而是用来定义显示方法或下载方法的,不论是哪个实现了I_ImageLoder抽象工厂的实际工厂,都必须有一个加载图片的方法。那么在项目的实际应用中,就可以不管这个下载器的实际工厂是什么,只需要调用工厂的加载图片的方法就行了。

/**
 * 图片载入接口协议,可自定义实现此协议的下载器
 * 
 * @explain 采用工厂方法模式设计的下载器,本类也是一个抽象工厂类,用于生产byte[]产品
 * @author kymjs(kymjs123@gmail.com)
 * @version 1.0
 * @created 2014-7-11
 */
public interface I_ImageLoder {
    public byte[] loadImage(String imageUrl);

}

这里我们应该就可以知道上面的代码段中有这么一段代码原因了
byte[] res = downloader.loadImage(params[0]);
downloader实际上就是下载器的抽象工厂。

至于显示器的逻辑和下载器是一样的,这里我就不详细介绍了,大家可以自己查看KJFrameForAndroid的源代码或示例项目。

这里是I_ImageLoader下载器协议的一个实现类 Downloader.java,大家当然也可以根据自己的需要去实现自己的下载器,这完全没有任何作为扩展试开发,这对于图片设置代码本身没有任何影响。



© 著作权归作者所有

kymjs张涛

kymjs张涛

粉丝 511
博文 64
码字总数 76485
作品 4
普陀
Android工程师
私信 提问
加载中

评论(7)

kymjs张涛
kymjs张涛 博主

引用来自“leo_zhipeng”的评论

你好,我用这个框架,可是我想加载那种长度很长的图片时,图片的尺寸被定成半个屏幕,变的很模糊怎么办
有重载方法可以自己定义要显示的图片的宽高。图片的宽高传0可以加载原图,不过此时需要自己小心OOM问题。
leo_zhipeng
leo_zhipeng
你好,我用这个框架,可是我想加载那种长度很长的图片时,图片的尺寸被定成半个屏幕,变的很模糊怎么办
lwm1368
lwm1368

引用来自“张涛OSC”的评论

引用来自“lwm1368”的评论

涛哥~为何我安装后报了一个错误呢?:
NewListViewDemo.java第35行
提示mSwipeRefreshLayout.setColorSchemeResources()这个方法找不到,我把这里屏蔽后就没问题了。。。

那个方法是v4包的方法
说来也奇怪,我找了两个v4的包,一个607KB,一个633KB. 这V4包还有多个版本的吗?
kymjs张涛
kymjs张涛 博主

引用来自“lwm1368”的评论

涛哥~为何我安装后报了一个错误呢?:
NewListViewDemo.java第35行
提示mSwipeRefreshLayout.setColorSchemeResources()这个方法找不到,我把这里屏蔽后就没问题了。。。

那个方法是v4包的方法
lwm1368
lwm1368
涛哥~为何我安装后报了一个错误呢?:
NewListViewDemo.java第35行
提示mSwipeRefreshLayout.setColorSchemeResources()这个方法找不到,我把这里屏蔽后就没问题了。。。
f
fy1990
博主,KJFrameForAndroid框架有没有一份专门的文档啊?
程序员R
程序员R
KJFrameForAndroid 1.4 beta 版本发布

KJFrameForAndroid 是一个android的orm 和 ioc 框架。同时封装了android中的Bitmap与Http操作的框架,使其更加简单易用; KJFrameForAndroid的设计思想是通过封装Android原生SDK中复杂的复杂...

kymjs张涛
2014/09/03
1K
6
KJFrameForAndroid 开发框架 2.14 版本发布

KJFrameForAndroid的设计思想是通过封装Android原生SDK中复杂的复杂操作而达到简化Android应用级开发,最终实现快速而又安全高效的开发APP。目标是用最少的代码,完成最多的操作,用最高的效...

kymjs张涛
2015/03/24
1K
3
KJFrameForAndroid框架,V1.1 正式版发布

首先恭喜KJFrameForAndroid框架登上github热门推荐项目榜 KJFrameForAndroid 又叫KJLibrary,是一个帮助快速开发的框架。使用KJFrameForAndroid,你可以只用一行代码就完成http请求、网络图片...

kymjs张涛
2014/09/19
4.8K
19
KJFrameForAndroid 正式版发布

KJFrameForAndroid 又叫KJLibrary,是一个帮助快速开发的框架。使用KJFrameForAndroid,你可以只用一行代码就完成http请求、网络图片加载、数据库数据保存或读取。 KJFrameForAndroid的设计思...

kymjs张涛
2014/09/15
2.7K
9
KJFrameForAndroid 框架 1.3 版本发布

KJFrameForAndroid 又叫KJLibrary,是一个帮助快速开发的框架。 使用KJFrameForAndroid,你可以只用一行代码就完成http请求、网络图片加载、数据库数据保存或读取,同时可以轻松实现运行未被...

kymjs张涛
2014/10/24
2.3K
4

没有更多内容

加载失败,请刷新页面

加载更多

64.监控平台介绍 安装zabbix 忘记admin密码

19.1 Linux监控平台介绍 19.2 zabbix监控介绍 19.3/19.4/19.6 安装zabbix 19.5 忘记Admin密码如何做 19.1 Linux监控平台介绍: 常见开源监控软件 ~1.cacti、nagios、zabbix、smokeping、ope...

oschina130111
今天
13
0
当餐饮遇上大数据,嗯真香!

之前去开了一场会,主题是「餐饮领袖新零售峰会」。认真听完了餐饮前辈和新秀们的分享,觉得获益匪浅,把脑子里的核心纪要整理了一下,今天和大家做一个简单的分享,欢迎感兴趣的小伙伴一起交...

数澜科技
今天
7
0
DNS-over-HTTPS 的下一代是 DNS ON BLOCKCHAIN

本文作者:PETER LAI ,是 Diode 的区块链工程师。在进入软件开发领域之前,他主要是在做工商管理相关工作。Peter Lai 也是一位活跃的开源贡献者。目前,他正在与 Diode 团队一起开发基于区块...

红薯
今天
10
0
CC攻击带来的危害我们该如何防御?

随着网络的发展带给我们很多的便利,但是同时也带给我们一些网站安全问题,网络攻击就是常见的网站安全问题。其中作为站长最常见的就是CC攻击,CC攻击是网络攻击方式的一种,是一种比较常见的...

云漫网络Ruan
今天
12
0
实验分析性专业硕士提纲撰写要点

为什么您需要研究论文的提纲? 首先当您进行研究时,您需要聚集许多信息和想法,研究论文提纲可以较好地组织你的想法, 了解您研究资料的流畅度和程度。确保你写作时不会错过任何重要资料以此...

论文辅导员
今天
8
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部