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 ...
随机推荐
- JVM垃圾回收机制总结(4) :新一代的垃圾回收算法
垃圾回收的瓶颈 传统分代垃圾回收方式,已经在一定程度上把垃圾回收给应用带来的负担降到了最小,把应用的吞吐量推到了一个极限.但是他无法解决的一个问题,就是Full GC所带来的应用暂停.在一些对实时性要 ...
- 276. Paint Fence
题目: There is a fence with n posts, each post can be painted with one of the k colors. You have to pa ...
- hadoop 1.2.1 eclipse 插件编译
hadoop-1.2.1 eclipse插件编译 在ubuntu上进行hadoop相关的开发,需要在eclipse上安装hadoop开发插件.最新释放出的hadoop包含源码的包,以had ...
- Linux平台Cpu使用率的计算
proc文件系统 /proc文件系统是一个伪文件系统,它只存在内存当中,而不占用外存空间.它以文件系统的方式为内核与进程提供通信的接口.用户和应用程序可以通过/proc得到系统的信息,并可以改变内核的 ...
- 同一Session中的aspx页面的并发限制
项目中客户端采用WebBrowser展示aspx页面,用户有可能打开带多个带WebBrowser的winform窗体.此时,如果其中一个的WebBrowser的aspx页面响应较长的话,其他窗体中的W ...
- 《Linux内核设计与实现》读书笔记(十)- 内核同步方法【转】
转自:http://www.cnblogs.com/wang_yb/archive/2013/05/01/3052865.html 内核中提供了多种方法来防止竞争条件,理解了这些方法的使用场景有助于我 ...
- android 从assets和res中读取文件
1. 相关文件夹介绍 在Android项目文件夹里面,主要的资源文件是放在res文件夹里面的.assets文件夹是存放不进行编译加工的原生文件,即该文件夹里面的文件不会像xml,java文件被预编译, ...
- 正则表达式验证工具类RegexUtils.java
Java 表单注册常用正则表达式验证工具类,常用正则表达式大集合. 1. 电话号码 2. 邮编 3. QQ 4. E-mail 5. 手机号码 6. URL 7. 是否为数字 8. 是否为中文 9. ...
- hdu - 1180 诡异的楼梯 (bfs+优先队列)
http://acm.hdu.edu.cn/showproblem.php?pid=1180 注意点就是楼梯是在harry移动完之后才会改变方向,那么只要统计到达这个点时间奇偶性,就可以知道当前楼梯是 ...
- android异步任务详解 AsynTask
android提供了一套专门用于异步处理的类.即:AynsTask类.使用这个类可以为耗时程序开辟一个新线程进行处理,处理完时返回. 其实,AsynTask类就是对Thread类的一个封装,并且加入了 ...