我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Android Bitmap的加载优化与Cache相关介绍

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Android Bitmap的加载优化与Cache相关介绍

一 . 高效加载 Bitmap

BitMapFactory 提供了四类方法: decodeFile,decodeResource,decodeStream 和 decodeByteArray 分别用于从文件系统,资源,输入流以及字节数组中加载出一个 Bitmap 对象。

高效加载 Bitmap 很简单,即采用

BitMapFactory.options
来加载所需要尺寸图片。
BitMapFactory.options 
就可以按照一定的采样率来加载缩小后的图片,将缩小后的图片置于 ImageView 中显示。

通过采样率即可高效的加载图片,遵循如下方式获取采样率:

BitmapFactory.Options 
的 inJustDecodeBounds 参数设置为 true 并加载图片 从
BitmapFactory.Options 
中取出图片的原始宽高信息,即对应于 outWidth 和 outHeight 参数 根据采样率的规则并结合目标 View 的所需大小计算出采样率 inSampleSize 将
BitmapFactory.Options 
的 injustDecodeBounds 参数设置为 false,然后重新加载图片

过上述四个步骤,加载出的图片就是最终缩放后的图片,当然也有可能没有缩放。

代码实现如下:


public Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) {
 // First decode with inJustDecodeBounds=true to check dimensions
 final BitmapFactory.Options options = new BitmapFactory.Options();
 options.inJustDecodeBounds = true;
 BitmapFactory.decodeResource(res, resId, options);
 // Calculate inSampleSize
 options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
 // Decode bitmap with inSampleSize set
 options.inJustDecodeBounds = false;
 return BitmapFactory.decodeResource(res, resId, options);
}
public int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
 if (reqWidth == 0 || reqHeight == 0) {
  return 1;
 }
 // Raw height and width of image
 final int height = options.outHeight;
 final int width = options.outWidth;
 Log.d(TAG, "origin, w= " + width + " h=" + height);
 int inSampleSize = 1;
 if (height > reqHeight || width > reqWidth) {
  final int halfHeight = height / 2;
  final int halfWidth = width / 2;
  // Calculate the largest inSampleSize value that is a power of 2 and
  // keeps both height and width larger than the requested height and width.
  while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) {
   inSampleSize *= 2;
  }
 }
 Log.d(TAG, "sampleSize:" + inSampleSize);
 return inSampleSize;
}

实际使用就可以像下面这样了,如加载 100*100 的图片大小,就可以像下面这样高效的加载图片了:


mImageView.setImageBitmap(
decodeSampledBitmapFromResource(getResource(),R.id.myimage,100,100));

二 . Android 中的缓存策略

目前常用的算法是 LRU,即近期最少使用算法,当缓存存满时,会优先淘汰近期最少使用的缓存对象

2.1 LruCache

LruCache 是一个泛型类,其内部实现机制是 LinkedHashMap 以强引用的方式存储外部的缓存对象,提供了

get() 
 put() 
来完成缓存对象的存取。当缓存满了,移除较早的缓存对象,再添加新的。LruCache 是线程安全的。

强引用:直接的对象引用 软引用:当一个对象只有软引用时,系统内存不足时,会被 gc 回收 弱引用:当一个对象只有弱引用时,随时会被回收

2.2 DiskLriCache

DiskLruCache 用于实现存储设备缓存,即磁盘缓存。

2.2.1 DiskLruCache 的创建

由于它不属于 Android SDK的一部分,所以不能通过构造方法来创建,提供了

open()
方法用于自身的创建


public static DiskLruCache open(File directory,int appversion,int valueCount,long maxSize);

典型的 DiskLruCache 的创建过程


private static final Disk_CACHE_SIZE = 1024*1024*50;//50M
File diskCaCheDir = getDiskCacheDir(mContext,"bitmap");
if(!diskCacheDir.exists()){
 diskCacheDir.mkdirs();
}
mDiskLruCache = DiskLruCache.open(diskCaCheDir,1,1,Disk_CACHE_SIZE);

第三个参数表示单个节点所对应的数据,一般设置为1即可。

2.2.2 DiskLruCache 的缓存添加 缓存的添加操作是通过 Editor 完成的, Editor 表示一个缓存对象的编辑对象。DiskLruCache 不允许同时编辑一个缓存对象。

2.2.3 DiskLruCache 的缓存查找

缓存查找过程也需要将 url 转换为 key,通过 DiskLruCache 的

get() 
得到一个 Snapshot 对象,然后通过该对象即可得到缓存的文件输入流,得到文件输入流即可得到 Bitmap 对象了。为了避免加载过程中 OOM,一般不会直接加载原始图片。在前面介绍通过
BitmapFactory.Options 
来加载一张缩放后的图片,但是那种方法对 FileInputStream 的缩放存在问题,原因是 FileInputStream 是一种有序的文件流,而两次 decodeStream 调用影响了文件流的位置属性,导致了第二次 decodeStream 时得到的是 null。为了解决这个问题,可以通过文件流得到其对应的文件描述符,然后通过
BitmapFactory.decodeFileDescriptor 
方法来加载一张缩放过后的图片。


 Bitmap bitmap = null;
 String key = hashKeyFormUrl(url);
 DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key);
  if (snapShot != null) {
   FileInputStream fileInputStream = (FileInputStream)snapShot.getInputStream(DISK_CACHE_INDEX);
   // 获取文件描述符
   FileDescriptor fileDescriptor = fileInputStream.getFD();
   // 通过 BitmapFactory.decodeFileDescriptor 来加载一张缩放后的图片
   bitmap = mImageResizer.decodeSampledBitmapFromFileDescriptor(fileDescriptor,
     reqWidth, reqHeight);
   if (bitmap != null) {
    addBitmapToMemoryCache(key, bitmap);
   }
  }
  return bitmap;
 }

三 . ImageLoader 的实现

具备的功能,即图片的同步加载,异步加载,图片的压缩,内存缓存,磁盘缓存以及网络拉取。

3.1 图片压缩功能

如前面所述。

3.2 内存缓存和磁盘缓存的实现

选择 LruCache 和 DiskLruCache 来分别完成内存缓存和磁盘缓存的工作

3.3 同步加载和异步加载的接口设计

关于同步加载:从 loadBitmap 的实现可以看出,其工作过程遵循如下几个步骤:先试着从内存缓存中读取图片,接着从磁盘缓存中读取图片,最后试着从网络拉取图片。另外该方法不能在主线程中调用,否则就会抛出异常。因为加载图片是一个耗时的操作。

关于异步加载:从 bindBitmap 中可以看出,binfBitmap 会先试着从内存缓存中读取结果,如果成功就直接返回,否则会从线程池中去调用

loadBitmap()
,当加载成功后,再讲图片,图片地址以及需要绑定的 ImageView 封装成一个 loaderResult 对象,通过 mMainHandler 向主线程发送一个消息,这样就可以在主线程中给 ImageView 设置图片了。图片的异步加载是一个很有用的功能,很多时候调用者不想在单独的线程中以同步的方式来加载图片,并将图片设置给需要的 ImageVIew, 从而ImageLoader 内部需要自己需要在内部线程中加载图片,并且将图片设置给所需要的 ImageView。

ImageLoader源码可以点击这里:下载 查看ImageLoader的实现

四 . ImageLoader 的使用

核心是 ImageAdapter , 其中的

getView() 
的核心方法如下:


@Override
public View getView(int position, View convertView, ViewGroup parent) {
   ViewHolder holder = null;
   if (convertView == null) {
    convertView = mInflater.inflate(R.layout.image_list_item,parent, false);
    holder = new ViewHolder();
    holder.imageView = (ImageView) convertView.findViewById(R.id.image);
    convertView.setTag(holder);
   } else {
    holder = (ViewHolder) convertView.getTag();
   }
   ImageView imageView = holder.imageView;
   final String tag = (String)imageView.getTag();
   final String uri = getItem(position);
   if (!uri.equals(tag)) {
    imageView.setImageDrawable(mDefaultBitmapDrawable);
   }
   if (mIsGridViewIdle && mCanGetBitmapFromNetWork) {
    imageView.setTag(uri);
    // 这句话将图片的复杂加载过程交给 ImageLoader 了
    mImageLoader.bindBitmap(uri, imageView, mImageWidth, mImageWidth);
   }
   return convertView;
  }

对于上述代码 ImageAdapter 来说, ImageLoader 的加载图片的复杂过程,更不需要知道。

优化列表卡顿现象:

不要在 getView() 中做加载图片的操作,那样肯定会耗时,像这个例子中一样,交给 ImageLoaer 来实现。 控制异步加载频率, 如果用户刻意的频繁的上下滑动,可能在一瞬间加载几百个异步任务,这样会给线程池造成拥堵。解决的办法是考虑在用户滑动列表时,停止加载图片。等到列表停下来时,在进行异步加载任务。 开启硬件加速:给Activity添加配置android:hardwareAccelerated=”true”

总结

以上就是这篇文章的全部内容了,希望本文的内容对给我Android开发者们能带来一定的帮助,如果有疑问大家可以留言交流。

您可能感兴趣的文章:Android VideoCache视频缓存的方法详解详解Android的内存优化--LruCache解析Android中View转换为Bitmap及getDrawingCache=null的解决方法Android 加载大图、多图和LruCache缓存详细介绍android中图片的三级缓存cache策略(内存/文件/网络)实现Android 获取cache缓存的目录路径的方法


免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Android Bitmap的加载优化与Cache相关介绍

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Android Bitmap的加载优化与Cache相关介绍

一 . 高效加载 BitmapBitMapFactory 提供了四类方法: decodeFile,decodeResource,decodeStream 和 decodeByteArray 分别用于从文件系统,资源,输入流以及字节数组中加载
2022-06-06

Android项目中如何优化Bitmap的加载

Android项目中如何优化Bitmap的加载?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。一 . 高效加载 BitmapBitMapFactory 提供了四类方法: deco
2023-05-31

基于Android 监听ContentProvider 中数据变化的相关介绍

如果ContentProvider的访问者需要知道ContentProvider中的数据的变化情况,可以在ContentProvider发生数据变化时调用getContentResolver().notifyChange(uri,null)
2022-06-06

Android优化查询加载大数量的本地相册图片

一、概述讲解优化查询相册图片之前,我们先来看下PM提出的需求,PM的需求很简单,就是要做一个类似微信的本地相册图片查询控件,主要包含两个两部分:进入图片选择页面就要显示出手机中所有的照片,包括系统相册图片和其他目录下的所有图片,并按照时间倒
2022-06-06

Android中读取中文字符的文件与文件读取相关介绍

一、如何显示assets/license.txt(中文)的内容? (1)方法1:InputStream.available()得到字节数,然后一次读取完。 代码如下: private String readUserAgreementFro
2022-06-06

Android垂直切换的圆角Banner与垂直指示器相关介绍与应用详解

这篇文章主要介绍了Android垂直切换的圆角Banner与垂直指示器相关介绍与应用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
2022-11-13

PHP与MySQL索引的数据加载和数据关联的优化策略及其对性能的影响

1.数据加载优化策略在PHP中,通过MySQL进行数据查询是常见的操作。为了提高数据加载速度,可以使用索引或优化查询语句。索引是对数据库表中一个或多个列的值进行排序的数据结构,它能够显著提高数据的读取性能。(1)合理使用索引在MySQL中,
2023-10-21

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录