Android图片库

更新时间:2016/02/25 访问次数:12521

1 Android图片库简介

Android图片智能下载库(MediaPhenix)是基于手机淘宝开放的整套移动端图片下载、加载、存储的开发库。主要包括以下两部分:

  • 图片策略库: 图片策略库是针对百川多媒体CDN图片URL做出智能匹配处理的一套工具,可以加载指定尺寸、质量的图片,也可自动根据网络场景加载不同质量的图片。

  • Phenix: Phenix提供网络图像加载、多级缓存策略、本地文件图像加载、支持WebP全格式、减少重复请求、Bitmap空间复用等功能。

2 SDK和Demo下载

点击下载

解压后的目录结构如下所示:

|—— MediaPhenix: 该文件夹即为Android图片库 SDK。
  |—— libs
  |—— AndroidManifest.xml
|—— PhenixDemo: 该文件夹为图片库SDK的调用示例。
  |—— res
  |—— src
  |—— AndroidManifest.xml
|—— README.md: 介绍文档

3 Demo使用

3.1 导入SDK和Demo

将MediaPhenix和PhenixDemo导入到IDE中,使PhenixDemo依赖MediaPhenix。

3.2 运行Demo

编译并运行,Demo界面如下图所示,包含三个示例。

这三个示例在加载图片的统计结果如下所示。

  • 1) 直接下载图片。不使用图片库,直接通过HttpURLConnection下载图片。每次加载图片时,都会进行网络请求,导致效率低下。下图展示在wifi环境下,直接加载图片耗时179ms。

  • 2) 基于图片库下载图片。图片库首次完成图片下载后,会对图片进行缓存。当再次访问图片时,可直接从缓存中直接加载并显示图片。下图展示利用图片库加载已缓存过的图片,需要的时间为3ms。

  • 3) 基于图片库加载自定义图片。针对百川多媒体空间的图片,开发者可以通过策略库加载指定尺寸和质量的图片。下图展示加载指定大小(460 × 460)的图片时,服务端返回的图片大小为(460 × 345),内存占用减小到619kb。

4 图片库接口说明

4.1 初始化

开发者在Application初始化时,需要完成对图片库Phenix的初始化,类似下面这样。

FLog.setMinLevel(Log.DEBUG);
// 使用全局上下文初始化Phenix单例
Phenix.instance().with(applicationContext);

4.2 使用

1) 简单接入

如果你手上有一个ImageView,有一个Url,那么你就可以像下面这样直接调用:

Phenix.instance().with(getContext()).load(url)
 .placeholder(android.R.drawable.ic_menu_rotate)
 .error(android.R.drawable.ic_delete)
 .into(imageView);

Phenix这时获取ImageView的宽高(如果能够成功获取的话,失败则默认使用屏幕宽高)来做为图片解码为Bitmap重采样的最大宽高,以计算准确的倍率inSample值,达到节省内存开销的目的。

如果你明确知道调用into的时候,还不能获取到正确的ImageView的宽高,并且你想使用你自己的默认最大宽高值,则像下面这样调用(myDefaultMaxWidth, myDefaultMaxHeight都必须为int正数):

Phenix.instance().with(getContext()).load(url)
 .placeholder(android.R.drawable.ic_menu_rotate)
 .error(android.R.drawable.ic_delete)
 .into(imageView, myDefaultMaxWidth, myDefaultMaxHeight);

还有一种情况,你觉得使用ImageView的宽高/默认宽高解出来的Bitmap仍然过大,内存上仍然吃不销,你可以在调用into时自己设置一个比率,像下面这样计算Bitmap最大宽高时使用的是原有宽高大小的1/2:

Phenix.instance().with(getContext()).load(url)
 .placeholder(android.R.drawable.ic_menu_rotate)
 .error(android.R.drawable.ic_delete)
 .into(imageView, 2.0f);

2) 列表中的图片加载

在ListView或者RecyclerView这类列表组件中加载图片时,如果存在convertView组件复用的情况,使用into接口时需要特别注意,需要对图片的下载任务作个简单的管理,否则可能出现图片跳动而与最终资源不符的问题。即在复用ImageView前,要对该View绑定的上一个加载任务进行取消,如下所示:

// 取消原有的加载任务
if (view.getTag() instanceof PhenixTicket) {
    ((PhenixTicket) view.getTag()).cancel();
}
// 重新绑定新的加载任务
PhenixTicket ticket = Phenix.instance().with(view.getContext()).load(realUrl).into(view);
view.setTag(ticket);

3) 只下载Bitmap到内存

有些时候你可能只需要依据一个Url下载一个BitmapDrawable到内存,不需要绑定到ImageView显示而是保存到磁盘或者另作它用,这时你可以使用下面的代码:

Phenix.instance().with(getContext())
    .load(url)
    .succListener(new IPhenixListener<SuccPhenixEvent>() {
        @Override
        public boolean onHappen(SuccPhenixEvent event) {
            if (event.getDrawable() != null && !event.isIntermediate()) {
                 BitmapDrawable bitmap = event.getDrawable();
                 // to do what you want with the final drawable
            }
            return true;
        }
}).fetch();

这里有两点需要注意的:
* OnHappen可能回调多次,故需要判断非空与操作是否重复;
* 你可以调用SuccPhenixEvent的isImmediate()或者isIntermediate()方法,来判断当前回调值是否为**立即值**或是**中间值**;

4) 显式释放Drawable

当你接入图片库Phenix时,你从Phenix拿到可用的Drawable之后并未通知Phenix这个Drawable什么时候被解引用,什么时候可以回收利用了。对此,为了获得更好的性能,就需要你在Drawable解引用时显式地通知Phenix回收。具体的做法分两种,一种是Drawable与ImageView发生了绑定,在ImageView不可见或者销毁时,你可以通过getDrawable()拿到Drawable然后调用下面的代码;另外一种是Drawable未与任何ImageView发生绑定,在你明确已使用完此Drawalbe后,同样地调用下面的代码:

if (drawable instanceof RecyclingBitmapDrawable) {
    ((RecyclingBitmapDrawable) drawable).releaseFromExternal();
}

5) 本地图片加载

本地图片加载与网络图片加载的唯一区别就是URI传入到Phenix时的路径不一样,本地文件路径以**单个左斜杠开头**,如下代码所示:

Phenix.instance().with(getContext())
.load("/storage/emulated/0/DCIM/P50719-104759.jpg")
.into(imageView);

6) 指定缓存项占位

如果你在加载一张网络图像之前,明确地知道曾经一个URL图像已经加载过,可能存在内存缓存/磁盘缓存当中,而你想用这张图片先作为某个ImageView的临时占位符,那么你可以像下面这样,调用**secondary(String)**方法来设置一个曾加载过的URL:

Phenix.instance().with(getContext()).load(url)
 .secondary(placeHolderUrl)
 .placeholder(android.R.drawable.ic_menu_rotate)
 .error(android.R.drawable.ic_delete)
 .into(imageView);

4.3 自定义接口

1) 自定义网络加载器

开发者可以实现HttpLoader接口,来自定义网络加载器。

默认提供的实现为DefaultHttpLoader。该实现是直接使用系统的HttpURLConnection连接下载。当然你实现了自己的网络加载器UserHttpLoader,可以像下面这样在初始化时注入。另外你可以使用第三方比如OkHttp来实现自己的网络加载器。

Phenix.instance().httpLoaderBuilder().with(new UserHttpLoader(applicationContext));

2) 自定义磁盘缓存组件

开发者可以实现DiskCache接口,来自定义磁盘缓存组件。

默认提供的实现为NonCatalogDiskCache,这套方案是每个图像文件单独存储在ExternalCache,并按照hash值分文件夹存储,同时也避免了FAT32存储设备上单目录存放大量文件Bug,控制在一个目录下最大放置的文件不超过100个。如果你有自己的实现,可以像下面这样初始化:

Phenix.instance().diskCacheBuilder().with(new NonCatalogDiskCache());

3) 自定义内存缓存组件

开发者可以实现LruCache<String, ChainBitmapDrawable>接口,来自定义内存缓存组件。

默认提供的实现为DefaultDrawableMemCache组件,基于Oracle改进的冷热端循环淘汰的Lru算法HotEndLruCache。如果你有自己的实现,可以像下面这样初始化:

Phenix.instance().memCacheBuilder().with(new DefaultDrawableMemCache());

4) 自定义本地文件加载器

开发者可以实现FileLoader接口,来自定义内存缓存组件。

默认提供的实现为DefaultFileLoader,里面目前只实现了对绝对路径的本地文件的加载。其他两类(assets以及res图像)暂不支持。如果你有自己的实现,像下面这样初始化:

Phenix.instance().fielLoaderBuilder().with(new DefaultFileLoader());

5) 自定义缓冲字节复用池

开发者可以实现BytesPool接口,来自定义缓冲字节复用池。

默认提供的实现为LinkedBytesPool,并且总容量为1MB。如果你有自己的实现,像下面这样初始化:

Phenix.instance().bytesPoolBuilder().with(new LinkedBytesPool());

5 策略库接口说明

针对存储在百川多媒体空间中的图片,开发者可以通过策略库指定图片的尺寸和质量等参数,百川多媒体服务根据设置的参数返回匹配的图片。

5.1 初始化

策略库和图片库Phenix一样,在应用创建时也需要初始化。

/**
 * 初始化策略库环境
 * getConfigString,用以获取最新的配置项,或者没有配置中心接入,可直接返回默认值
 * isSupportWebP,当前环境是否支持WebP,使用来自Phenix的方法,用以得到当前图片库能否支持WebP格式
 * isNetworkSlow,当前是否为弱网,这里只简单判断了下是否为WiFi,准确的做法是借助网络库测速判断
 * **/
ImageInitBusinss.newInstance(getApplication(), new IImageStrategySupport() {
    @Override
    public String getConfigString(String group, String key, String defaultVal) {
        return defaultVal;
    }
    @Override
    public boolean isSupportWebP() {
        return BitmapDecodeHelper.isSupportWebp();
    }
    @Override
    public boolean isNetworkSlow() {
        return !isWifi();
    }
}).notifyConfigsChange();

5.2 使用

完成初始化后,你就可以通过ImageStrategyDecider来依据View的宽高自动适配CDN URL了。然后把适配好的URL直接交给Phenix下载即可。这里需要注意的是,如果ImageView在布局xml已经指定了固定宽高,可以从LayoutParams直接取得宽高。如果布局中没有指定或者你的View大小是动态变化的,那么**一定在ImageView布局完成(onLayout)后**通过getWidth()/getHeight()来获得准确的宽高,以保证适配的尺寸正确。

String decidedUrl = ImageStrategyDecider.decideUrl(
          ConstantData.TAE_OSS_CDN_URL,
          imageView.getLayoutParams().width,
          imageView.getLayoutParams().height,
          null
);
Phenix.instance().load(decidedUrl).into(imageView);

5.3 自定义策略

你可以通过ImageStrategyConfig自定义各种策略。里面提供各种开关,以满足你特殊的需求,详细的控制开关见下面注释。

/**
 * Builder下可以使用的开关如下:
 *
 * 强制指定最终使用的质量参数
 * setFinalImageQuality(TaobaoImageUrlStrategy.ImageQuality.q50)
 *
 * 不使用质量控制,这时通过setFinalImageQuality指定的将被忽略
 * enableQuality(false)
 *
 * 不使用WebP格式
 * enableWebP(false)
 *
 * 不使用图片锐化
 * enableSharpen(false)
 *
 * 关闭尺寸分级阶梯复用
 * enableLevelModel(false)
 *
 * 指定图片裁减方式
 * setCutType(TaobaoImageUrlStrategy.CutType.xz)
 *
 * 指定图片定宽还是定高,另外一边按比例缩放
 * setSizeLimitType(ImageStrategyConfig.SizeLimitType.HEIGHT_LIMIT)
 * **/
ImageStrategyConfig config = ImageStrategyConfig.newBuilderWithName("test").enableQuality(false).build();

String decidedUrl = ImageStrategyDecider.decideUrl(
          ConstantData.TAE_OSS_CDN_URL,
          imageView.getLayoutParams().width,
          imageView.getLayoutParams().height,
          config 
);
Phenix.instance().load(decidedUrl).into(imageView);

FAQ

关于此文档暂时还没有FAQ
返回
顶部