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 ...
随机推荐
- SPRING IN ACTION 第4版笔记-第十一章Persisting data with object-relational mapping-005Spring-Data-JPA例子的代码
一.结构 二.Repository层 1. package spittr.db; import java.util.List; import org.springframework.data.jpa. ...
- QApplication::alert 如果窗口不是活动窗口,则会向窗口显示一个警告(非常好用,效果就和TeamViewer一样)
void QApplication::alert(QWidget * widget, int msec = 0)如果窗口不是活动窗口,则会向窗口显示一个警告.警报会显示msec 毫秒.如果毫秒为零,闪 ...
- IME 编程相关
以下内容摘自http://msdn.microsoft.com/zh-cn/goglobal/bb688135.aspx 在 Win32 中处理输入语言的方法 Microsoft Developer ...
- python xml.etree ElementTree解析 编辑 xml
python有很多种xml解析方式,不过感觉etree的ElementTree 用起来最方便. #coding=utf-8 from xml.etree import ElementTree impo ...
- 《c程序设计语言》读书笔记--子函数原型和声明的形参
#include <stdio.h> #define Num 20 int power(int base,int n) { int p = 1; int i; for(i = 0;i &l ...
- .htaccess的应用
今天下班后,同事问到这个东西的用法.我以前也没接触过,特地搜索了一下,现在保存方便以后使用. 1.首先判断服务器类型,服务器是否支持. 2.写法是否有错误. 3.需要的功能是否在.htaccess是否 ...
- NPOI导出Excel表功能实现(多个工作簿)(备用)
Excel生成操作类: 代码 using System; using System.Collections.Generic; using System.Text; using System.IO; u ...
- Javascript零散知识点总结
一.Javascript获取系统当前时间 <script type="text/javascript"> var time = new Date(); var date ...
- Asp.Net生命周期系列四
上回我们说的当一个Http请求来到HttpModule这里的时候,Asp.Net内部并未对这个Http请求做出任何的处理,我们可以对这个Http请求添加一些我们需要的信息,以方便我们控制这个Http请 ...
- HDU 1564 (博弈) Play a game
这道题的答案猜也很好猜出来,但是想太难想了..题解一贴,然后闪人.. 请戳这