如何高效地加载Bitmap呢?其实核心思想就是采用BitmapFactory.Options来加载所需尺寸的图片。这里假设通过ImageView来显示图片,很多时候ImageView并没有图片的原始尺寸那么大,这个时候把整个图片加载进来再设给ImageView,这显然是没必要的,因为ImageView并没有办法显示原始的图片。通过BitmapFactory.Options就可以按一定的采样率来加载缩小后的图片,将缩小后的图片在ImageView中显示,这样就会降低内存占用从而在一定程度上避免OOM。
通过BitmapFactory.Options来缩放图片,主要是用到了它的inSampleSize参数,即采样率,当inSampleSize为1时,采样后的图片大小为图片的原始大小,当inSampleSize大于1时,比如为2,那么采样后的图片其宽/高均为原图大小的1/2,而像素数为原图的1/4,其占有的内存大小也为原图的1/4。当inSampleSize小于1时,起作用相当于1,另外inSampleSize的取值应该总是为2的指数,比如1,2,4,8,16...如果外界传递给系统的inSampleSize不为2的指数,那么系统会向下取整并选择一个最接近2的指数来代替。
示例代码如下:
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.ceshi);
imageView=findViewById(R.id.image);
imageView1=findViewById(R.id.image1);
imageView.setImageBitmap(getCompressBitmap(getResources(),R.mipmap.touxiang,88,88));
Log.d("CeShi", "getCompressBitmap(getResources(),R.mipmap.touxiang,88,88).getRowBytes():" + getCompressBitmap(getResources(), R.mipmap.touxiang, 88, 88).getRowBytes());
Log.d("CeShi", "((BitmapDrawable)imageView1.getDrawable()).getBitmap().getRowBytes():" + ((BitmapDrawable) imageView1.getDrawable()).getBitmap().getRowBytes());
/*输出结果为:(当然图像也变得不如原来那么清晰了)
05-25 20:56:11.181 19944-19944/com.example.liang.arlvyou D/CeShi: getCompressBitmap(getResources(),R.mipmap.touxiang,88,88).getRowBytes():812
05-25 20:56:11.181 19944-19944/com.example.liang.arlvyou D/CeShi: ((BitmapDrawable)imageView1.getDrawable()).getBitmap().getRowBytes():6480*/
}
public static Bitmap getCompressBitmap(Resources resources,int resId,int requestWidth,int requestHeight){
BitmapFactory.Options options=new BitmapFactory.Options();
options.inJustDecodeBounds=true;
BitmapFactory.decodeResource(resources,resId,options);
options.inSampleSize=getSampleSize(options,requestWidth,requestHeight);
options.inJustDecodeBounds=false;
return BitmapFactory.decodeResource(resources,resId,options);
}
private static int getSampleSize(BitmapFactory.Options options,int requestWidth,int requestHeight) {
int sampleSize=1;
if(options.outWidth>requestWidth||options.outHeight>requestHeight){
int w=options.outWidth/2;
int h=options.outHeight/2;
while ((w/sampleSize)>=requestWidth&&(h/sampleSize)>=requestHeight){
sampleSize*=2;
}
}
return sampleSize;
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:layoutAnimation="@anim/viewgroup"
android:id="@+id/lin">
<ImageView
android:layout_width="88dp"
android:layout_height="88dp"
android:id="@+id/image"/>
<ImageView
android:id="@+id/image1"
android:src="@mipmap/touxiang"
android:layout_width="88dp"
android:layout_height="88dp"/>
</LinearLayout>
LruCache的介绍
在使用LruCache时建议采用support-v4兼容包中提供的LruCache,而不要直接使用Android3.1提供的LruCache。
LruCache是一个泛型类,它内部采用一个LinkedHashMap以强引用的方式存储外界的缓存对象,提供了get和put方法来完成缓存的获取和添加操作,当缓存满时,LruCache会移除较早使用的缓存对象,然后再添加新的缓存对象。
示例代码如下,下面是加载了本地图片,主要是加载网络图片用
imageView=findViewById(R.id.image);
button=findViewById(R.id.button);
final LruCache<String,Bitmap>lruCache=new LruCache<String,Bitmap>((int) (Runtime.getRuntime().maxMemory()/8)){
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getRowBytes();
}
};
lruCache.put("tupian", BitmapFactory.decodeResource(getResources(),R.mipmap.touxiang));
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
imageView.setImageBitmap(lruCache.get("tupian"));
}
});
DiskLruCache的介绍(地址:https://github.com/JakeWharton/DiskLruCache)
DiskLruCache用于实现存储设备缓存,即磁盘缓存,它通过将缓存对象写入文件系统从而实现缓存的效果。
public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize);
open方法有四个参数的介绍:
第一个参数表示磁盘缓存在文件系统中的存储路径。如果应用卸载后就希望删除缓存文件,那么就选择SD卡上的缓存目录,如果希望保留缓存数据那就应该选择SD卡上的其他特定目录。
第二个参数表示应用的版本号,一般设为1即可。
第三个参数表示单个节点所对应的数据的个数,一般设为1即可。
第四个参数表示缓存的总大小,比如50MB,写法为1024102450,当缓存大小超出这个设定值后,DiskLruCache会清除一些缓存从而保证总大小不大于这个设定值。
DiskLruCache的缓存添加的操作是通过Editor完成的,Editor表示一个缓存对象的编辑对象。对于一个key来说,如果当前不存在Editor对象,那么edit()就会返回一个新的Editor对象,如果存在Editor对象,就返回对应的Editor对象。如果DiskLruCache的open方法中设置了一个节点只能有一个数据,那么流的参数index就只能为0。最后必须通过Editor的commit()才能提交写入操作。
示例代码如下:
button = findViewById(R.id.button);
final File file = new File("/storage/emulated/0/Bytes");
if (!file.exists()) {
file.mkdir();
}
//本地缓存字符串
new Thread() {
@Override
public void run() {
final DiskLruCache diskLruCache;
try {
diskLruCache = DiskLruCache.open(file, 1, 1, 50 * 1024 * 1024);
DiskLruCache.Editor editor = diskLruCache.edit("diyige");
editor.set(0,"hello,world");
editor.commit();//记得commit,才能生效
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
Log.d("CeShi", diskLruCache.get("diyige").getString(0));//输出结果为:05-26 21:09:54.733 19153-19153/com.example.liang.arlvyou D/CeShi: hello,world
} catch (IOException e) {
e.printStackTrace();
}
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
//本地缓存图片的方式如下所示,下面是缓存了字符串,图片也一样
/* new Thread(){
@Override
public void run() {
try {
final DiskLruCache diskLruCache=DiskLruCache.open(file,1,1,50*1024*1024);
DiskLruCache.Editor editor=diskLruCache.edit("diyige");
final OutputStream outputStream=editor.newOutputStream(0);
outputStream.write("woshiliu".getBytes());
outputStream.flush();
editor.commit();
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
InputStream inputStream=diskLruCache.get("diyige").getInputStream(0);
ByteArrayOutputStream byteArrayOutputStream=new ByteArrayOutputStream();
int i;
while((i=inputStream.read())!=-1){
byteArrayOutputStream.write(i);
}
s=new String(byteArrayOutputStream.toByteArray());
Log.d("CeShi", s);//输出结果为:05-26 21:02:18.913 10685-10685/com.example.liang.arlvyou D/CeShi: woshiliu
} catch (IOException e) {
e.printStackTrace();
}
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();*/
网友评论