LruCache--远程图片获取与本地缓存
Class Overview
A cache that holds strong references to a limited number of values. Each time a value is accessed, it is moved to the head of a queue. When a value is added to a full cache, the value at the end of that queue is evicted and may become eligible for garbage collection.
If your cached values hold resources that need to be explicitly released, override entryRemoved(boolean, K, V, V)
.
If a cache miss should be computed on demand for the corresponding keys, override create(K)
. This simplifies the calling code, allowing it to assume a value will always be returned, even when there's a cache miss.
By default, the cache size is measured in the number of entries. Override sizeOf(K, V)
to size the cache in different units. For example, this cache is limited to 4MiB of bitmaps:
int cacheSize = 4 * 1024 * 1024; // 4MiB
LruCache bitmapCache = new LruCache(cacheSize) {
protected int sizeOf(String key, Bitmap value) {
return value.getByteCount(); }}
This class is thread-safe. Perform multiple cache operations atomically by synchronizing on the cache:
synchronized (cache) {
if (cache.get(key) == null) {
cache.put(key, value); }}
This class does not allow null to be used as a key or value. A return value of null from get(K)
, put(K, V)
or remove(K)
is unambiguous: the key was not in the cache.
This class appeared in Android 3.1 (Honeycomb MR1); it's available as part of Android's Support Package for earlier releases.
Summary
Public Constructors | |||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
LruCache(int maxSize) |
Public Methods | |||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
synchronized final int | createCount()
Returns the number of times
create(Object) returned a value. |
||||||||||
final void | evictAll()
Clear the cache, calling
entryRemoved(boolean, K, V, V) on each removed entry. |
||||||||||
synchronized final int | evictionCount()
Returns the number of values that have been evicted.
|
||||||||||
final V | get(K key)
Returns the value for
key if it exists in the cache or can be created by #create . |
||||||||||
synchronized final int | hitCount()
Returns the number of times
get(K) returned a value that was already present in the cache. |
||||||||||
synchronized final int | maxSize()
For caches that do not override
sizeOf(K, V) , this returns the maximum number of entries in the cache. |
||||||||||
synchronized final int | missCount()
Returns the number of times
get(K) returned null or required a new value to be created. |
||||||||||
final V | put(K key, V value)
Caches
value for key . |
||||||||||
synchronized final int | putCount()
Returns the number of times
put(K, V) was called. |
||||||||||
final V | remove(K key)
Removes the entry for
key if it exists. |
||||||||||
synchronized final int | size()
For caches that do not override
sizeOf(K, V) , this returns the number of entries in the cache. |
||||||||||
synchronized final Map<K, V> | snapshot()
Returns a copy of the current contents of the cache, ordered from least recently accessed to most recently accessed.
|
||||||||||
synchronized final String | toString()
Returns a string containing a concise, human-readable description of this object.
|
||||||||||
void | trimToSize(int maxSize)
Remove the eldest entries until the total of remaining entries is at or below the requested size.
|
Protected Methods | |||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
V | create(K key)
Called after a cache miss to compute a value for the corresponding key.
|
||||||||||
void | entryRemoved(boolean evicted, K key, V oldValue, V newValue)
Called for entries that have been evicted or removed.
|
||||||||||
int | sizeOf(K key, V value)
Returns the size of the entry for
key and value in user-defined units. |
http://developer.android.com/reference/android/util/LruCache.html
Demo分步讲解:
1.布局文件
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" > <GridView
android:id="@+id/gridView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:stretchMode="columnWidth"
android:columnWidth="120dip"
android:verticalSpacing="10dip"
android:horizontalSpacing="10dip"
android:cacheColorHint="@android:color/transparent"
android:numColumns="auto_fit" >
</GridView> </RelativeLayout>
2. 保存图片到SD卡或者手机上(工具类)
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException; import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.BitmapFactory;
import android.os.Environment; public class FileUtils {
/**
* sd卡的根目录
*/
private static String mSdRootPath = Environment
.getExternalStorageDirectory().getPath();
/**
* 手机的缓存根目录
*/
private static String mDataRootPath = null;
/**
* 保存Image的目录名
*/
private final static String FOLDER_NAME = "/AndroidImage"; public FileUtils(Context context) {
mDataRootPath = context.getCacheDir().getPath();
} /**
* 获取储存Image的目录
* @return
*/
private String getStorageDirectory() {
return Environment.getExternalStorageState().equals(
Environment.MEDIA_MOUNTED) ? mSdRootPath + FOLDER_NAME
: mDataRootPath + FOLDER_NAME;
} /**
* 保存Image的方法,有sd卡存储到sd卡,没有就存储到手机目录
*
* @param fileName
* @param bitmap
* @throws IOException
*/
public void savaBitmap(String fileName, Bitmap bitmap) throws IOException {
if (bitmap == null) {
return;
}
String path = getStorageDirectory();
File folderFile = new File(path);
if (!folderFile.exists()) {
folderFile.mkdir();
}
File file = new File(path + File.separator + fileName);
file.createNewFile();
FileOutputStream fos = new FileOutputStream(file);
bitmap.compress(CompressFormat.JPEG, 100, fos);
fos.flush();
fos.close();
} /**
* 从手机或者sd卡获取Bitmap
*
* @param fileName
* @return
*/
public Bitmap getBitmap(String fileName) {
return BitmapFactory.decodeFile(getStorageDirectory() + File.separator
+ fileName);
} /**
* 判断文件是否存在
*
* @param fileName
* @return
*/
public boolean isFileExists(String fileName) {
return new File(getStorageDirectory() + File.separator + fileName)
.exists();
} /**
* 获取文件的大小
*
* @param fileName
* @return
*/
public long getFileSize(String fileName) {
return new File(getStorageDirectory() + File.separator + fileName)
.length();
} /**
* 删除SD卡或者手机的缓存图片和目录
*/
public void deleteFile() {
File dirFile = new File(getStorageDirectory());
if (!dirFile.exists()) {
return;
}
if (dirFile.isDirectory()) {
String[] children = dirFile.list();
for (int i = 0; i < children.length; i++) {
new File(dirFile, children[i]).delete();
}
} dirFile.delete();
}
}
compress方法详解如下:
public boolean compress (Bitmap.CompressFormat format, int quality, OutputStream stream)
Write a compressed version of the bitmap to the specified outputstream. If this returns true, the bitmap can be reconstructed by passing a corresponding inputstream to BitmapFactory.decodeStream(). Note: not all Formats support all bitmap configs directly, so it is possible that the returned bitmap from BitmapFactory could be in a different bitdepth, and/or may have lost per-pixel alpha (e.g. JPEG only supports opaque pixels).
写入位图的压缩版本到指定的OutputStream。如果返回true,则位图可以通过传递一个相应的InputStream BitmapFactory.decodeStream(重建)。注意:并非所有的格式直接支持所有的位图的configs,所以它可能是从BitmapFactory返回的位图可能是在不同的位深度,和/或可能已经失去了每个像素的alpha(如JPEG只支持不透明像素)。
Parameters
format | The format of the compressed image |
---|---|
quality | Hint to the compressor, 0-100. 0 meaning compress for small size, 100 meaning compress for max quality. Some formats, like PNG which is lossless, will ignore the quality setting |
stream | The outputstream to write the compressed data. |
Returns true if successfully compressed to the specified stream.
3. 主Activity代码片段
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.GridView;
import android.widget.Toast; public class MainActivity extends Activity {
private GridView mGridView;
private String [] imageThumbUrls = Images.imageThumbUrls;
private ImageAdapter mImageAdapter;
private FileUtils fileUtils; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
fileUtils = new FileUtils(this);
mGridView = (GridView) findViewById(R.id.gridView);
mImageAdapter = new ImageAdapter(this, mGridView, imageThumbUrls);
mGridView.setAdapter(mImageAdapter);
} @Override
protected void onDestroy() {
mImageAdapter.cancelTask();
super.onDestroy();
} @Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
menu.add("删除手机中图片缓存");
return super.onCreateOptionsMenu(menu);
} @Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case 0:
fileUtils.deleteFile();
Toast.makeText(getApplication(), "清空缓存成功", Toast.LENGTH_SHORT).show();
break;
}
return super.onOptionsItemSelected(item);
} }
4. 图片适配器ImageAdapter
import android.content.Context;
import android.graphics.Bitmap;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.BaseAdapter;
import android.widget.GridView;
import android.widget.ImageView; import com.example.asyncimageloader.ImageDownLoader.onImageLoaderListener; public class ImageAdapter extends BaseAdapter implements OnScrollListener {
/**
* 上下文对象的引用
*/
private Context context; /**
* Image Url的数组
*/
private String[] imageThumbUrls; /**
* GridView对象的应用
*/
private GridView mGridView; /**
* Image 下载器
*/
private ImageDownLoader mImageDownLoader; /**
* 记录是否刚打开程序,用于解决进入程序不滚动屏幕,不会下载图片的问题。
* 参考http://blog.csdn.net/guolin_blog/article/details/9526203#comments
*/
private boolean isFirstEnter = true; /**
* 一屏中第一个item的位置
*/
private int mFirstVisibleItem; /**
* 一屏中所有item的个数
*/
private int mVisibleItemCount; public ImageAdapter(Context context, GridView mGridView,
String[] imageThumbUrls) {
this.context = context;
this.mGridView = mGridView;
this.imageThumbUrls = imageThumbUrls;
mImageDownLoader = new ImageDownLoader(context);
mGridView.setOnScrollListener(this);
} @Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
// 仅当GridView静止时才去下载图片,GridView滑动时取消所有正在下载的任务
if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE) {
showImage(mFirstVisibleItem, mVisibleItemCount);
} else {
cancelTask();
}
} /**
* GridView滚动的时候调用的方法,刚开始显示GridView也会调用此方法
*/
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
mFirstVisibleItem = firstVisibleItem;
mVisibleItemCount = visibleItemCount;
// 因此在这里为首次进入程序开启下载任务。
if (isFirstEnter && visibleItemCount > 0) {
showImage(mFirstVisibleItem, mVisibleItemCount);
isFirstEnter = false;
}
} @Override
public int getCount() {
return imageThumbUrls.length;
} @Override
public Object getItem(int position) {
return imageThumbUrls[position];
} @Override
public long getItemId(int position) {
return position;
} @Override
public View getView(int position, View convertView, ViewGroup parent) {
ImageView mImageView;
final String mImageUrl = imageThumbUrls[position];
if (convertView == null) {
mImageView = new ImageView(context);
} else {
mImageView = (ImageView) convertView;
} mImageView.setLayoutParams(new GridView.LayoutParams(150, 150));
mImageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE); // 给ImageView设置Tag,这里已经是司空见惯了
mImageView.setTag(mImageUrl); /******************************* 去掉下面这几行试试是什么效果 ****************************/
Bitmap bitmap = mImageDownLoader.showCacheBitmap(mImageUrl.replaceAll(
"[^\\w]", ""));
if (bitmap != null) {
mImageView.setImageBitmap(bitmap);
} else {
mImageView.setImageDrawable(context.getResources().getDrawable(
R.drawable.ic_empty));
}
/**********************************************************************************/ return mImageView;
} /**
* 显示当前屏幕的图片,先会去查找LruCache,LruCache没有就去sd卡或者手机目录查找,在没有就开启线程去下载
*
* @param firstVisibleItem
* @param visibleItemCount
*/
private void showImage(int firstVisibleItem, int visibleItemCount) {
Bitmap bitmap = null;
for (int i = firstVisibleItem; i < firstVisibleItem + visibleItemCount; i++) {
String mImageUrl = imageThumbUrls[i];
final ImageView mImageView = (ImageView) mGridView
.findViewWithTag(mImageUrl);
bitmap = mImageDownLoader.downloadImage(mImageUrl,
new onImageLoaderListener() { @Override
public void onImageLoader(Bitmap bitmap, String url) {
if (mImageView != null && bitmap != null) {
mImageView.setImageBitmap(bitmap);
} }
}); if (bitmap != null) {
mImageView.setImageBitmap(bitmap);
} else {
mImageView.setImageDrawable(context.getResources().getDrawable(
R.drawable.ic_empty));
}
}
} /**
* 取消下载任务
*/
public void cancelTask() {
mImageDownLoader.cancelTask();
} }
5. (ImageDownLoader)缓存Image的类,当存储Image的大小大于LruCache设定的值,系统自动释放内存
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os.Message;
import android.support.v4.util.LruCache; public class ImageDownLoader {
/**
* 缓存Image的类,当存储Image的大小大于LruCache设定的值,系统自动释放内存
*/
private LruCache<String, Bitmap> mMemoryCache;
/**
* 操作文件相关类对象的引用
*/
private FileUtils fileUtils;
/**
* 下载Image的线程池
*/
private ExecutorService mImageThreadPool = null; public ImageDownLoader(Context context) {
// 获取系统分配给每个应用程序的最大内存,每个应用系统分配32M
int maxMemory = (int) Runtime.getRuntime().maxMemory();
int mCacheSize = maxMemory / 8;
// 给LruCache分配1/8 4M
mMemoryCache = new LruCache<String, Bitmap>(mCacheSize) { // 必须重写此方法,来测量Bitmap的大小
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getRowBytes() * value.getHeight();
} }; fileUtils = new FileUtils(context);
} /**
* 获取线程池的方法,因为涉及到并发的问题,我们加上同步锁
*
* @return
*/
public ExecutorService getThreadPool() {
if (mImageThreadPool == null) {
synchronized (ExecutorService.class) {
if (mImageThreadPool == null) {
// 为了下载图片更加的流畅,我们用了2个线程来下载图片
mImageThreadPool = Executors.newFixedThreadPool(2);
}
}
} return mImageThreadPool; } /**
* 添加Bitmap到内存缓存
*
* @param key
* @param bitmap
*/
public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
if (getBitmapFromMemCache(key) == null && bitmap != null) {
mMemoryCache.put(key, bitmap);
}
} /**
* 从内存缓存中获取一个Bitmap
*
* @param key
* @return
*/
public Bitmap getBitmapFromMemCache(String key) {
return mMemoryCache.get(key);
} /**
* 先从内存缓存中获取Bitmap,如果没有就从SD卡或者手机缓存中获取,SD卡或者手机缓存 没有就去下载
*
* @param url
* @param listener
* @return
*/
public Bitmap downloadImage(final String url,
final onImageLoaderListener listener) {
// 替换Url中非字母和非数字的字符,这里比较重要,因为我们用Url作为文件名,比如我们的Url
// 是Http://xiaanming/abc.jpg;用这个作为图片名称,系统会认为xiaanming为一个目录,
// 我们没有创建此目录保存文件就会保存
final String subUrl = url.replaceAll("[^\\w]", "");
Bitmap bitmap = showCacheBitmap(subUrl);
if (bitmap != null) {
return bitmap;
} else { final Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
listener.onImageLoader((Bitmap) msg.obj, url);
}
}; getThreadPool().execute(new Runnable() { @Override
public void run() {
Bitmap bitmap = getBitmapFormUrl(url);
Message msg = handler.obtainMessage();
msg.obj = bitmap;
handler.sendMessage(msg); try {
// 保存在SD卡或者手机目录
fileUtils.savaBitmap(subUrl, bitmap);
} catch (IOException e) {
e.printStackTrace();
} // 将Bitmap 加入内存缓存
addBitmapToMemoryCache(subUrl, bitmap);
}
});
} return null;
} /**
* 获取Bitmap, 内存中没有就去手机或者sd卡中获取,这一步在getView中会调用,比较关键的一步
*
* @param url
* @return
*/
public Bitmap showCacheBitmap(String url) {
if (getBitmapFromMemCache(url) != null) {
return getBitmapFromMemCache(url);
} else if (fileUtils.isFileExists(url)
&& fileUtils.getFileSize(url) != 0) {
// 从SD卡获取手机里面获取Bitmap
Bitmap bitmap = fileUtils.getBitmap(url); // 将Bitmap 加入内存缓存
addBitmapToMemoryCache(url, bitmap);
return bitmap;
} return null;
} /**
* 从Url中获取Bitmap
*
* @param url
* @return
*/
private Bitmap getBitmapFormUrl(String url) {
Bitmap bitmap = null;
HttpURLConnection con = null;
try {
URL mImageUrl = new URL(url);
con = (HttpURLConnection) mImageUrl.openConnection();
con.setConnectTimeout(10 * 1000);
con.setReadTimeout(10 * 1000);
con.setDoInput(true);
con.setDoOutput(true);
bitmap = BitmapFactory.decodeStream(con.getInputStream());
} catch (Exception e) {
e.printStackTrace();
} finally {
if (con != null) {
con.disconnect();
}
}
return bitmap;
} /**
* 取消正在下载的任务
*/
public synchronized void cancelTask() {
if (mImageThreadPool != null) {
mImageThreadPool.shutdownNow();
mImageThreadPool = null;
}
} /**
* 异步下载图片的回调接口
*
* @author len
*
*/
public interface onImageLoaderListener {
void onImageLoader(Bitmap bitmap, String url);
} }
6.配置文件
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.asyncimageloader"
android:versionCode="1"
android:versionName="1.0" > <uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="16" /> <application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:configChanges="orientation"
android:name="com.example.asyncimageloader.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> </manifest>
DEMO下载地址:http://download.csdn.net/detail/androidsj/7171167
LruCache--远程图片获取与本地缓存的更多相关文章
- Android远程图片获取和本地缓存
对于客户端——服务器端应用,从远程获取图片算是经常要用的一个功能,而图片资源往往会消耗比较大的流量,对 应用来说,如果处理不好这个问题,那会让用户很崩溃,不知不觉手机流量就用完了,等用户发现是你的应用 ...
- AsyncTask--远程图片获取与本地缓存
对于客户端——服务器端应用,从远程获取图片算是经常要用的一个功能,而图片资源往往会消耗比较大的流量,对应用来说,如果处理不好这个问题,那会让用户很崩溃,不知不觉手机流量就用完了,等用户发现是你的应用消 ...
- php 获取远程图片保存到本地
php 获取远程图片保存到本地 使用两个函数 1.获取远程文件 2.把图片保存到本地 /** * 获取远程图片并把它保存到本地 * $url 是远程图片的完整URL地址,不能为空. */ functi ...
- C# 远程图片下载到本地
下载方法 using System; using System.Net; using System.IO; using System.Text; namespace Common { /// < ...
- PHP 将某个http地址的远程图片下载到本地的某个目录
代码: function getImage($url,$save_dir='',$filename='',$type=0){ if(trim($url)==''){ return array('fil ...
- ASP.NET下载远程图片保存到本地的方法、保存抓取远程图片
以下介绍两种方法:1.利用WebRequest,WebResponse 类 WebRequest wreq=WebRequest.Create("http://www.xueit.com/e ...
- 用ASP.NET实现下载远程图片保存到本地的方法 保存抓取远程图片的方法
以下介绍两种方法:1.利用WebRequest,WebResponse 类WebRequest wreq=WebRequest.Create("http://files.jb51.net/f ...
- php获取远程图片并把它保存到本地
/* *功能:php多种方式完美实现下载远程图片保存到本地 *参数:文件url,保存文件名称,使用的下载方式 *当保存文件名称为空时则使用远程文件原来的名称 */ function getImage( ...
- php将远程图片下载保存到本地
/* *功能:php完美实现下载远程图片保存到本地 *参数:文件url,保存文件目录,保存文件名称,使用的下载方式 *当保存文件名称为空时则使用远程文件原来的名称 */ function getIma ...
随机推荐
- 实记JLink刷固件方法
最近J-Link调试程序,不知何故,USB报无法连接,试了别的电脑也是这样.文件存于“百度网盘/05.组装维修/4.单片机/1.JLink-ARM仿真器”下.所有的操作基于WIN7 32位系统. 1. ...
- Android Design Support Library 的 代码实验——几行代码,让你的 APP 变得花俏
原文:Codelab for Android Design Support Library used in I/O Rewind Bangkok session--Make your app fanc ...
- CodeForces484A——Bits(贪心算法)
Bits Let's denote as the number of bits set ('1' bits) in the binary representation of the non-negat ...
- c++ 在客户端的GCC使用
c++ 在客户端的GCC使用 翻译自: GCC是GNU工具箱的一个功能,GNU还包括如下功能: GNU compiler collection (GCC) GNU make GNU Debugger ...
- 练习用php做表格
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- JQUERY与JS的区别
JQUERY与JS的区别 <style type="text/css"> #aa { width:200px; height:200px; } </style&g ...
- 【USACO】【Section1.1】Greedy Gift Givers
小白题,也没啥好说的.关键我的算法感觉特别菜的一点是每次要遍历数组从人名找对应的编号,这个效率就很低了.看了ANALYZE里面也是这样的.不过它比我好的一点是我多余设置了initial_money变量 ...
- JSP 中的几种注释
1.多行注释 <!-- 注释1 注释2 注释3 --> 或者 <!-- 注释1 …… 注释n //--> 2.多行注释,不同的是:注释内容不会发送到客户端,会被JSP引擎所忽略 ...
- 《OD大数据实战》Flume环境搭建
一.CentOS 6.4安装Nginx http://shiyanjun.cn/archives/72.html 二.安装Flume 1. 下载flume-ng-1.5.0-cdh5.3.6.tar. ...
- 无锁编程(四) - CAS与ABA问题
CAS 一般采用原子级的read-modify-write原语来实现Lock-Free算法,其中LL和SC是Lock-Free理论研究领域的理想原语,但实现这些原语需要CPU指令的支持,非常遗憾的是目 ...