Bitmaps
有效的处理较大的位图
图像有各种不同的形状和大小。在许多情况下,他们往往比-一个典型应用程序的用户界面(UI)所需要的资源更大。
读取一个位图的尺寸和类型:
为了从多种资源来创建-一个位图,BitmapFactory类提供 了几个解码的方法(decodeByteArray(),decodeFile(),decodeResource(),等等)。 根据你的圈像数据资源选擇最合适的解码方法。这些方法试图请求分配内存来构造位图,因此很容易导致OutOfMemory异常。每种类型的解码方法都有额外的特征可以让你通过BitMapFactory Options类指定解码选项。当解码时避免内存分配可以设置inJustDecodeBounds属性为true,位圈对象返回nl但是设置了outwidth, outHeight和outMimeType.这种技术允许你在创建位图(和分配内存之前去读取图像的尺寸和类型。
加载一个缩小版本到内存中:
现在的图像尺寸都是已知的,他们可以被用来决定是否应该加载完整的图片到内存或者是否用一个缩小的版本去代替加载。
以下是一些值得考虑的因素:
- 估计加载完整图像所需要的内存;
- 你承诺加载这个图片所需空间带给你的程序的其他内存需求;
- 准备加载图像的目标lmageView或UI组件尺寸;
- 当前设备的屏幕R寸和密度;
告诉解码器去重新采样这个图像,加载-一个更小的版本到内存中,在你的BitmapFactory .Option对象中设置inSampleSize为true。例如,将一个分辨率为20481536的图像用inSampleSize值为4去编码将产生一个大小为大约512384的位图。加载这个到内存中仅使用0.75MB,而不是完整的12MB大小的图像(假设使用ARGB.8888位图的配置(占4个字节))。这里有一一个方法在目标的宽度和高度的基础上来计算一个SampleSize的值。
//计算位图的采样比例
public int calculateInSampleSize(BitmapFactory.Options options,int reqWidth,int reqHeight){
//获取位图的原尺寸宽高
int w=options.outWidth;
int h=options.outHeight;
System.out.println("w="+w);
System.out.println("h="+h);
int inSampleSize=1;
if (w>reqWidth||h>reqHeight){
if (w>h){
inSampleSize=Math.round((float) h/(float)reqHeight);
}else {
inSampleSize=Math.round((float) w/(float)reqWidth);
}
}
System.out.println("inSampleSize="+inSampleSize);
return inSampleSize;
}
提示:使用2的幂数设置inSampleSize的值可以使解码器更快,更有效。然而,如果你想在内存或硬盘中缓存一个图片调整后的版本,通常解码到合适的图像R寸更适合来节省空间。
要使用这种方法,首先解码,将inJusiPecodeBounds设置为true, 将选项传递进去,然后再次解码,在使用新的inSampleSize值并将inJustDecodeBounds设置为false:
//位图重新采样
public Bitmap decodeSampledBitmapFromResource(Resources res,int resid,int reqWidth,int reqHeight){
BitmapFactory.Options options=new BitmapFactory.Options();
options.inJustDecodeBounds=true;
BitmapFactory.decodeResource(res,resid,options);
options.inSampleSize=calculateInSampleSize(options,reqWidth,reqHeight);
options.inJustDecodeBounds=false;
return BitmapFactory.decodeResource(res,resid,options);
}
显示位图
private ImageView imageView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView.findViewById(R.id.imageView);
}
public void showClick(View view){
Bitmap bitmap=decodeSampledBitmapFromResource(getResources(),R.mipmap.a,100,100);
imageView.setImageBitmap(bitmap);
}
缓存位图
(1)内存缓存:
内存缓冲提供了可以快速访问位图LruCache类用于缓存位图的任务,最近被引用的对象保存在-个强引用LinkedHashMap中,以及在缓存超过了其指定的大小之前释放最近很少使用的对象的内存。
注意:在过去,一个常用的内存缓存实现是一个SoftReference或WeakReference的位图缓存,然而现在不推荐使用。从android2.3 (API 级别9)开始,垃圾回收器更加注重于回收软/弱引用,这使得使用以上引用很大程度上无效。此外, 之前的android3.0 (API级别11) 位图的备份很有可能会导致应用程序内存溢出和崩溃。
注: LRU是Least Recently Used近期最少使用算法
private ImageView imageView;
private LruCache<String,Bitmap> lruCache;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView=findViewById(R.id.imageView);
//获取当前Activity内存大小
ActivityManager am= (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
int memoryClass=am.getMemoryClass();
final int cacheSize=memoryClass/8 * 1024 * 1024;//字节 1/8的内存作为缓存大小
//创建LruCache
lruCache=new LruCache<>(cacheSize);
}
//添加缓存的方法
public void addBitmapToCache(String key,Bitmap bitmap){
if (getBitmapFromCahce(key)==null){
lruCache.put(key,bitmap);
}
}
//从缓存中读取对象方法
public Bitmap getBitmapFromCahce(String key){
return lruCache.get(key);
}
修改显示视图方法
public void showClick(View view){
String key=String.valueOf(R.mipmap.a);
Bitmap bitmap=getBitmapFromCahce(key);
if (bitmap==null){
bitmap=decodeSampledBitmapFromResource(getResources(),R.mipmap.a,100,100);
addBitmapToCache(key,bitmap);
}else {
System.out.println("lruCache中有位图");
}
imageView.setImageBitmap(bitmap);
}
(2)磁盘缓存:
使用磁盘缓存来持续处理位图,并且有助于在图片在内存缓存中不再可用时缩短加载时间,当然, 从磁盘获取图片比从内存加载更慢并且应当在后台线程中处理,因为磁盘读取的时间是不可预知的注意:如果它们被更频繁地访问,那么一个ContentProvider可能是 一个更合适的地方来存储缓存中的图像,例如在一个图片库应用程序里。
第三方DiskLruCache解决方案(在 Android4.Ot上版本源码)
下载地址:
https://android.googlesource.com/platform/libcore/+/android-4.1.1_r1/luni/src/main/java/libcore/io/DiskLruCache.java
参考博客:https://www.jianshu.com/p/f9cfbea586c2
创建DiskLruCacheUtils工具类
package libcore.io;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.AsyncTask;
import android.os.Environment;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class LruCacheUtils {
private static LruCacheUtils diskLruCacheUtils;
private DiskLruCache diskLruCache;
private Context context;
private LruCacheUtils(){}
public static LruCacheUtils getInstance(){
if (diskLruCacheUtils==null){
diskLruCacheUtils=new LruCacheUtils();
}
return diskLruCacheUtils;
}
public void open(Context context,String disk_cache_subdir,int disk_cache_size){
try{
this.context=context;
//参数:缓存目录,获取app版本
diskLruCache=DiskLruCache.open(getCacheDir(disk_cache_subdir),getAppVersion(),1,disk_cache_size);
}catch (IOException e){
e.printStackTrace();
}
}
//取
public InputStream get(String url){
String key=hashKeyForDisk(url);
try {
DiskLruCache.Snapshot snapshot=diskLruCache.get(key);
if (snapshot!=null){
return snapshot.getInputStream(0);
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
//写
private void put(final String url){
new AsyncTask<String,Void,Void>(){
@Override
protected Void doInBackground(String... strings) {
String key=hashKeyForDisk(strings[0]);
DiskLruCache.Editor editor=null;
try {
URL url=new URL(strings[0]);
HttpURLConnection conn= (HttpURLConnection) url.openConnection();
editor=diskLruCache.edit(key);
BufferedOutputStream out=new BufferedOutputStream(editor.newOutputStream(0),8*1024);
BufferedInputStream in=new BufferedInputStream(conn.getInputStream(),8*1024);
byte[] bytes=new byte[1024];
int len=-1;
while ((len=in.read(bytes))!=-1){
out.write(bytes,0,len);
}
out.close();
in.close();
editor.commit();
conn.disconnect();
} catch (IOException e) {
try {
editor.abort();
} catch (IOException e1) {
e1.printStackTrace();
}
}
return null;
}
}.execute(url);
}
//计算
public String hashKeyForDisk(String key){
String cacheKey;
try {
final MessageDigest mDigest=MessageDigest.getInstance("MD5");
mDigest.update(key.getBytes());
cacheKey=bytesToHexString(mDigest.digest());
} catch (NoSuchAlgorithmException e) {
cacheKey=String.valueOf(key.hashCode());
}
return cacheKey;
}
private String bytesToHexString(byte[] bytes){
StringBuilder sb=new StringBuilder();
for (int i = 0; i <bytes.length ; i++) {
String hex=Integer.toHexString(0xff&bytes[i]);
if (hex.length()==1){
sb.append('0');
}
sb.append(hex);
}
return sb.toString();
}
private int getAppVersion(){
try {
return context.getPackageManager().getPackageInfo(context.getPackageName(),0).versionCode;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return 1;
}
//获取缓存目录
private File getCacheDir(String name){
String cachePath= Environment.getExternalStorageState()==Environment.MEDIA_MOUNTED || !Environment.isExternalStorageRemovable()?
context.getExternalCacheDir().getPath():context.getCacheDir().getPath();
return new File(cachePath+File.separator+name);
}
}
在Main的onCreate方法中获得
diskLruCacheUtils= LruCacheUtils.getInstance();
diskLruCacheUtils.open(this,DISK_CACHE_SUBDIR,DISK_CACHE_SIZE);
双缓存案例
package libcore.io;
import android.app.ActivityManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Environment;
import android.util.LruCache;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import javax.security.auth.callback.Callback;
public class LruCacheUtils {
private static LruCacheUtils lruCacheUtils;
private DiskLruCache diskLruCache;//Lru磁盘缓存
private LruCache<String, Bitmap> lruCache;//Lru内存缓存
private Context context;
private LruCacheUtils(){}
public static LruCacheUtils getInstance(){
if (lruCacheUtils==null){
lruCacheUtils=new LruCacheUtils();
}
return lruCacheUtils;
}
public void open(Context context,String disk_cache_subdir,int disk_cache_size){
try{
this.context=context;
//获取Activity内存大小
ActivityManager am= (ActivityManager) context.getSystemService(context.ACTIVITY_SERVICE);
int memoryClass=am.getMemoryClass();
lruCache=new LruCache<>(memoryClass/8*1024*1024);//字节,当前内存的八分之一作为缓存大小
//参数:缓存目录,获取app版本,缓存文件数量(通常为1),缓存大小
diskLruCache=DiskLruCache.open(getCacheDir(disk_cache_subdir),getAppVersion(),1,disk_cache_size);
}catch (IOException e){
e.printStackTrace();
}
}
//添加缓存的方法
public void addBitmapToCache(String url,Bitmap bitmap){
String key=hashKeyForDisk(url);
if (getBitmapFromCahce(key)==null){
System.out.println("key====="+key);
System.out.println("bitmap====="+bitmap);
lruCache.put(key,bitmap);
}
}
//从缓存中读取对象方法
public Bitmap getBitmapFromCahce(String url){
String key=hashKeyForDisk(url);
return lruCache.get(key);
}
//从磁盘中取
public InputStream getDiskCache(String url){
String key=hashKeyForDisk(url);
System.out.println("getDiskCache:"+key);
try {
DiskLruCache.Snapshot snapshot=diskLruCache.get(key);
System.out.println(snapshot);
if (snapshot!=null){
return snapshot.getInputStream(0);
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
//加到磁盘缓存
public void putCache(final String url, final Callback callback){
new AsyncTask<String,Void,Bitmap>(){
@Override
protected Bitmap doInBackground(String... strings) {
String key=hashKeyForDisk(strings[0]);
System.out.println("key====="+key);
DiskLruCache.Editor editor=null;
Bitmap bitmap=null;
try {
URL url=new URL(strings[0]);
HttpURLConnection conn= (HttpURLConnection) url.openConnection();
conn.setReadTimeout(1000*30);//读取超时
conn.setConnectTimeout(1000*30);//连接超时
ByteArrayOutputStream baos=null;
if (conn.getResponseCode()==HttpURLConnection.HTTP_OK){
BufferedInputStream bis=new BufferedInputStream(conn.getInputStream());
baos=new ByteArrayOutputStream();
byte[] bytes=new byte[1024];
int len=-1;
while ((len=bis.read(bytes))!=-1){
baos.write(bytes,0,len);
}
bis.close();
baos.close();
conn.disconnect();
}
if (baos!=null){
bitmap=decodeSampledBitmapFromResource(baos.toByteArray(),100,100);
addBitmapToCache(strings[0],bitmap);
editor=diskLruCache.edit(key);
System.out.println(url.getFile());
//压缩输出(参数:压缩模式,质量(100不压缩,30为压缩70%),输出流)
bitmap.compress(Bitmap.CompressFormat.JPEG,100,editor.newOutputStream(0));
editor.commit();//提交
}
} catch (IOException e) {
try {
editor.abort();
} catch (IOException e1) {
e1.printStackTrace();
}
}
return bitmap;
}
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
callback.response(bitmap);
}
}.execute();
}
//关闭磁盘缓存
public void close(){
if (diskLruCache!=null&&!diskLruCache.isClosed()){
try {
diskLruCache.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//刷新磁盘缓存
public void flush(){
if (diskLruCache!=null);{
try {
diskLruCache.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//回调接口
public interface Callback<T>{
public void response(T entity);
}
//计算位图的采样比例
public int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight){
//获取位图的原尺寸宽高
int w=options.outWidth;
int h=options.outHeight;
System.out.println("w="+w);
System.out.println("h="+h);
int inSampleSize=1;
if (w>reqWidth||h>reqHeight){
if (w>h){
inSampleSize=Math.round((float) h/(float)reqHeight);
}else {
inSampleSize=Math.round((float) w/(float)reqWidth);
}
}
System.out.println("inSampleSize="+inSampleSize);
return inSampleSize;
}
//位图重新采样
public Bitmap decodeSampledBitmapFromResource(byte[] bytes, int reqWidth, int reqHeight){
BitmapFactory.Options options=new BitmapFactory.Options();
options.inJustDecodeBounds=true;
BitmapFactory.decodeByteArray(bytes,0,bytes.length,options);
options.inSampleSize=calculateInSampleSize(options,reqWidth,reqHeight);
options.inJustDecodeBounds=false;
return BitmapFactory.decodeByteArray(bytes,0,bytes.length,options);
}
//计算摘要,根据字符串通过MD5的计算出16进制的摘要
public String hashKeyForDisk(String key){
String cacheKey;
try {
final MessageDigest mDigest=MessageDigest.getInstance("MD5");
mDigest.update(key.getBytes());
cacheKey=bytesToHexString(mDigest.digest());
} catch (NoSuchAlgorithmException e) {
cacheKey=String.valueOf(key.hashCode());
}
return cacheKey;
}
private String bytesToHexString(byte[] bytes){
StringBuilder sb=new StringBuilder();
for (int i = 0; i <bytes.length ; i++) {
String hex=Integer.toHexString(0xff&bytes[i]);
if (hex.length()==1){
sb.append('0');
}
sb.append(hex);
}
return sb.toString();
}
//获取版本号
private int getAppVersion(){
try {
return context.getPackageManager().getPackageInfo(context.getPackageName(),0).versionCode;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return 1;
}
//获取缓存目录
private File getCacheDir(String name){
String cachePath= Environment.getExternalStorageState()==Environment.MEDIA_MOUNTED || !Environment.isExternalStorageRemovable()?
context.getExternalCacheDir().getPath():context.getCacheDir().getPath();
return new File(cachePath+File.separator+name);
}
}
添加权限
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
调用方法
public void showClick2(View view){
String url="http://e.hiphotos.baidu.com/image/pic/item/574e9258d109b3dea897b509c2bf6c81800a4ca2.jpg";
loadBitmap(url,imageView2);
}
@Override
protected void onResume() {
super.onResume();
lruCacheUtils=LruCacheUtils.getInstance();
lruCacheUtils.open(this,DISK_CACHE_SUBDIR,DISK_CACHE_SIZE);
}
@Override
protected void onPause() {
super.onPause();
lruCacheUtils.flush();
}
@Override
protected void onStop() {
super.onStop();
lruCacheUtils.close();
}
//加载图片
public void loadBitmap(String url,final ImageView imageView){
//从内存缓存中取图片
Bitmap bitmap=lruCacheUtils.getBitmapFromCahce(url);
if (bitmap==null){
//在从磁盘缓存中取
InputStream in=lruCacheUtils.getDiskCache(url);
if (in==null){
lruCacheUtils.putCache(url, new LruCacheUtils.Callback<Bitmap>() {
@Override
public void response(Bitmap entity) {
System.out.println("http load");
imageView.setImageBitmap(entity);
}
});
}else {
System.out.println("disk cache");
bitmap=BitmapFactory.decodeStream(in);
lruCacheUtils.addBitmapToCache(url,bitmap);
imageView.setImageBitmap(bitmap);
}
}else {
System.out.println("memory cache");
imageView.setImageBitmap(bitmap);
}
}