Android LRU算法 图片缓存性能改善 <9>
2016-01-13 18:49
736 查看
由于开发过程中经常要使用图片,特别是从互联网上面获取图片,如果要让设备获取互联网上面图片,就意味着连接网络,获取图片,但是从服务器获取图片是需要时间的,当图片非常多,或者图片有非常大的时候,这样获取是很危险的,可能常常遇见OOM的问题,导致APP不稳定,性能非常的差,而且频繁的联网获取,浪费流量和电量,有无数个不爽,所以android就引入LRU算法来改善图片加载的问题.
大致的逻辑应该是这样的:
android系统提供了LRU机制,系统源代码LRUCache.java中,本人大致对其的理解,前提其实需要知道几个概念,源代码中
private final LinkedHashMap<K, V> map;
调用运行,打印结果:
这个概念大致清楚了,看一下LRUCache.java程序的另外一个地方:
这个变量非常重要,我的理解大概有两点,第一点是缓存空间是有限的,不是无限的,所以是有上限的;第二点时缓存应该是缓存相对较新的内容,移除非常旧的资源,当有新的资源获取时,应该在有限的容器中添加最新的,剔除最旧的资源,这句话和奇怪,下面程序辅助解释:
注意下面一段:
if (size <= maxSize) {
break;
}
Map.Entry<K, V> toEvict = map.eldest();
if (toEvict == null) {
break;
}
key = toEvict.getKey();
value = toEvict.getValue();
map.remove(key);
如果现在size已经达到maxSize了,那么下面就会从LinkedHashMap中移除最旧的资源,上面map只认识key啦!
下面可以做一个android 测试进行验证上面的:
<1> : 新建Android 工程:
<2> : 具体程序如下:
package org.durian.durianlrubitmap;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v4.util.LruCache;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import org.durian.durianlrubitmap.util.DurianUtil;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashSet;
public class DurianMainActivity extends ActionBarActivity implements View.OnClickListener{
private final static String TAG="DurianMainActivity";
private final static int IMAGE_COUNT=3;
private final static String IMAGE_SRC[]={
"http://pic.qiantucdn.com/58pic/13/96/30/41S58PICpI9_1024.png",
"http://pic.qiantucdn.com/58pic/14/79/59/15s58PICMdP_1024.jpg",
"http://pic.qiantucdn.com/58pic/14/64/73/03q58PICp9d_1024.jpg",
};
//网络数据源,格式:json
//json 中罗列了图片的url列表信息
private final static String URL_LIST= "http://pugme.herokuapp.com/bomb?count=" + IMAGE_COUNT;
private LruCache<String,Bitmap> mMemoryCache;
private ImageView imageView;
private Button mButton;
private Button mDownButton;
private Button mLinkButton;
private ListView listView;
private Bitmap bitmap;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.durian_main);
mLinkButton=(Button)findViewById(R.id.link);
mLinkButton.setOnClickListener(this);
listView=(ListView)findViewById(android.R.id.list);
imageView=(ImageView)findViewById(R.id.image);
int maxMemory=(int)(Runtime.getRuntime().maxMemory()/1024);
int cacheSize=maxMemory/8;
mMemoryCache=new LruCache<String,Bitmap>(cacheSize){
@Override
protected int sizeOf(String key, Bitmap bitmap) {
Log.i(TAG,"bitmap cache size : "+bitmap.getByteCount()/1024);
return bitmap.getByteCount()/1024;
}
};
mButton=(Button)findViewById(R.id.button);
mButton.setOnClickListener(this);
mDownButton=(Button)findViewById(R.id.loadjson);
mDownButton.setOnClickListener(this);
}
public void addBitmapToMemoryCache(String key,Bitmap bitmap){
if(getBitmapFromMemCache(key)==null){
Log.i(TAG,"add bitmap key : "+key);
mMemoryCache.put(key,bitmap);
}
}
public Bitmap getBitmapFromMemCache(String key){
Log.i(TAG,"get bitmap key : "+key);
return mMemoryCache.get(key);
}
@Override
public void onClick(View v) {
int id=v.getId();
switch (id){
case R.id.button:
Bitmap map=getBitmapFromMemCache(urls);
if(map==null){
ImageLoadTask work=new ImageLoadTask();
work.execute();
}else{
imageView.setImageBitmap(map);
}
break;
case R.id.loadjson:
new ImageLoadListTask().execute();
break;
case R.id.link:
DurianUtil.DurianLinkedHashMap();
break;
default:
break;
}
}
private class ImageLoadListTask extends AsyncTask<String,Integer,ArrayList<String>>{
@Override
protected ArrayList<String> doInBackground(String... params) {
try {
URL url=new URL(URL_LIST);
try {
HttpURLConnection conn=(HttpURLConnection)url.openConnection();
conn.setRequestProperty("ACCEPT","application/json");
InputStream is=conn.getInputStream();
StringBuilder buf=new StringBuilder();
BufferedReader reader=new BufferedReader(new InputStreamReader(is),1024);
String buffer;
for (buffer=reader.readLine();buffer!=null;buffer=reader.readLine()){
buf.append(buffer);
}
is.close();
String resp=buf.toString();
JSONObject jsonObject= null;
JSONArray jsonArray=null;
HashSet<String> pugUrls=null;
try {
jsonObject = new JSONObject(resp);
jsonArray=jsonObject.getJSONArray("pugs");
pugUrls = new HashSet<String>(jsonArray.length());
for (int i = 0, z = jsonArray.length(); i < z; i++) {
//这个网络数据源不是太好,图片TMD太大了,有一些居然10多M,汗死.
//pugUrls.add(jsonArray.getString(i));
//下面提供选择几个不错的数据源用于加载
pugUrls.add(IMAGE_SRC[i]);
Log.i(TAG,"json : "+jsonArray.getString(i));
}
} catch (JSONException e) {
e.printStackTrace();
}
return new ArrayList<>(pugUrls);
} catch (IOException e) {
e.printStackTrace();
}
} catch (MalformedURLException e) {
e.printStackTrace();
}
return null;
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
}
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected void onPostExecute(ArrayList<String> ret) {
super.onPostExecute(ret);
DurianBaseAdapter adapter=new DurianBaseAdapter(DurianMainActivity.this,ret);
listView.setAdapter(adapter);
}
}
private final static String urls="http://pic.qiantucdn.com/58pic/13/96/30/41S58PICpI9_1024.png";//http://pic.qiantucdn.com/58pic/14/79/59/15s58PICMdP_1024.jpg";
private class ImageLoadTask extends AsyncTask<String,Integer,String>{
@Override
protected String doInBackground(String... params) {
try {
URL url=new URL(urls);
try {
HttpURLConnection conn=(HttpURLConnection)url.openConnection();
conn.setConnectTimeout(5000);
conn.setRequestMethod("GET");
InputStream is=conn.getInputStream();
bitmap= BitmapFactory.decodeStream(is);
addBitmapToMemoryCache(urls,bitmap);
mHandler.sendEmptyMessage(0);
} catch (IOException e) {
e.printStackTrace();
}
} catch (MalformedURLException e) {
e.printStackTrace();
}
return null;
}
@Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
}
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
}
}
private Handler mHandler=new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
imageView.setImageBitmap(bitmap);
}
};
}
package org.durian.durianlrubitmap;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Rect;
import android.media.Image;
import android.os.AsyncTask;
import android.support.v4.util.LruCache;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
/**
* Project name : DurianLRUBitmap
* Created by zhibao.liu on 2016/1/13.
* Time : 12:15
* Email warden_sprite@foxmail.com
* Action : durian
*/
public class DurianBaseAdapter extends BaseAdapter {
private final static String TAG="DurianBaseAdapter";
private ArrayList<String> mUrlArray;
private Context mContext;
private LayoutInflater mInflater;
private ViewHolder mViewHolder;
private LruCache<String,Bitmap> mMemoryCache;
public DurianBaseAdapter(Context context, ArrayList<String> url_list){
mContext=context;
mUrlArray=url_list;
mInflater=LayoutInflater.from(mContext);
mViewHolder=new ViewHolder();
int maxMemory=(int)(Runtime.getRuntime().maxMemory()/1024);
int cacheSize=maxMemory/8;
mMemoryCache=new LruCache<String,Bitmap>(cacheSize){
@Override
protected int sizeOf(String key, Bitmap bitmap) {
Log.i(TAG,"bitmap cache size : "+bitmap.getByteCount()/1024);
return bitmap.getByteCount()/1024;
}
};
}
@Override
public int getCount() {
return 3;
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if(convertView==null){
convertView=mInflater.inflate(R.layout.list_item,null);
mViewHolder.textView=(TextView)convertView.findViewById(R.id.text);
mViewHolder.imageView=(ImageView)convertView.findViewById(R.id.image);
mViewHolder.textView.setText(mUrlArray.get(position).toString());
//download image
AyncLoadImage mAyncLoadImage=new AyncLoadImage(mViewHolder.imageView,position);
mAyncLoadImage.execute();
}
return convertView;
}
class ViewHolder{
TextView textView;
ImageView imageView;
}
public void addBitmapToMemoryCache(String key,Bitmap bitmap){
if(getBitmapFromMemCache(key)==null){
Log.i(TAG,"add bitmap key : "+key);
mMemoryCache.put(key,bitmap);
}
}
public Bitmap getBitmapFromMemCache(String key){
Log.i(TAG,"get bitmap key : "+key);
return mMemoryCache.get(key);
}
private class AyncLoadImage extends AsyncTask<String,Integer,Bitmap>{
WeakReference<ImageView> imageViewWeakRef;
int postion;
@Override
protected Bitmap doInBackground(String... params) {
Bitmap map;
//检查是否已经有缓存了
//如果有缓存了,就是用缓存
map=getBitmapFromMemCache(mUrlArray.get(postion).toString());
if(map!=null){
return map;
}
//如果没有缓存,就到网络上面去获取
try {
URL url=new URL(mUrlArray.get(postion).toString());
try {
HttpURLConnection conn=(HttpURLConnection)url.openConnection();
conn.setRequestMethod("GET");
InputStream is=conn.getInputStream();
map= BitmapFactory.decodeStream(is);
return map;
} catch (IOException e) {
e.printStackTrace();
}
} catch (MalformedURLException e) {
e.printStackTrace();
}
return null;
}
public AyncLoadImage(ImageView imageview,int postion) {
super();
imageViewWeakRef=new WeakReference<ImageView>(imageview);
this.postion=postion;
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
}
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
ImageView imageView=imageViewWeakRef.get();
ByteArrayOutputStream out=new ByteArrayOutputStream();
if(bitmap!=null) {
bitmap.compress(Bitmap.CompressFormat.JPEG, 50, out);
addBitmapToMemoryCache(mUrlArray.get(postion).toString(),bitmap);
}
imageView.setImageBitmap(bitmap);
}
}
}
<3> : 运行程序后,界面上面有三个按钮,"LINK","BUTTON","JSON"按钮:
LINK按钮是LinkedHashMap的一个测试样例;
BUTTON是初步熟悉LRUCche的基本使用;
JSON是从网络上面获取一组数据,加载到ListView中,在加载的时候,有限判断缓存是否已经有了,如果有,直接加载缓存中的,如果没有再通过网络获取资源.
注意: Mainfiest文件需要添加联网权限:
其实还是比较好理解.
程序另外几个地方有几个概念,我觉得下面说的还不错,借过来,如下:
WeakReference与SoftReference都可以用来保存对象的实例引用,这两个类与垃圾回收有关。
WeakReference是弱引用,其中保存的对象实例可以被GC回收掉。这个类通常用于在某处保存对象引用,而又不干扰该对象被GC回收,通常用于Debug、内存监视工具等程序中。因为这类程序一般要求即要观察到对象,又不能影响该对象正常的GC过程。
最近在JDK的Proxy类的实现代码中也发现了Weakrefrence的应用,Proxy会把动态生成的Class实例暂存于一个由Weakrefrence构成的Map中作为Cache。
SoftReference是强引用,它保存的对象实例,除非JVM即将OutOfMemory,否则不会被GC回收。这个特性使得它特别适合设计对象Cache。对于Cache,我们希望被缓存的对象最好始终常驻内存,但是如果JVM内存吃紧,为了不发生OutOfMemoryError导致系统崩溃,必要的时候也允许JVM回收Cache的内存,待后续合适的时机再把数据重新Load到Cache中。这样可以系统设计得更具弹性
大致的逻辑应该是这样的:
android系统提供了LRU机制,系统源代码LRUCache.java中,本人大致对其的理解,前提其实需要知道几个概念,源代码中
private final LinkedHashMap<K, V> map;
</pre></p><p></p><p></p><p>LinkedHashMap这个是HashMap的一个子类,不过它相比HashMap有一些区别,LinkedHashMap存储数据是以链表的形式保存的,也就是说是有一定顺序的,可以做一段测试程序:</p><pre class="html" name="code">package org.durian.durianlrubitmap.util; import android.util.Log; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; /** * Project name : DurianLRUBitmap * Created by zhibao.liu on 2016/1/13. * Time : 18:33 * Email warden_sprite@foxmail.com * Action : durian */ public class DurianUtil { private final static String TAG="DurianUtil"; public static void DurianLinkedHashMap(){ Map<Integer,String> map=new LinkedHashMap<Integer, String>(); map.put(5,"hello,zhibao.liu"); map.put(8,"hello,world !"); map.put(3,"hello,java programe !"); for (Iterator it=map.keySet().iterator();it.hasNext();){ Object key=it.next(); Log.i(TAG,"link key : "+key.toString()); } Log.i(TAG,"******************************************"); Map<Integer,String> hashmap=new HashMap<Integer, String>(); hashmap.put(5,"hello,zhibao.liu"); hashmap.put(8,"hello,world !"); hashmap.put(3,"hello,java programe !"); for (Iterator it=hashmap.keySet().iterator();it.hasNext();){ Object key=it.next(); Log.i(TAG,"hash key : "+key.toString()); } } }
调用运行,打印结果:
这个概念大致清楚了,看一下LRUCache.java程序的另外一个地方:
public LruCache(int maxSize) { if (maxSize <= 0) { throw new IllegalArgumentException("maxSize <= 0"); } this.maxSize = maxSize; this.map = new LinkedHashMap<K, V>(0, 0.75f, true); }
private int maxSize;
这个变量非常重要,我的理解大概有两点,第一点是缓存空间是有限的,不是无限的,所以是有上限的;第二点时缓存应该是缓存相对较新的内容,移除非常旧的资源,当有新的资源获取时,应该在有限的容器中添加最新的,剔除最旧的资源,这句话和奇怪,下面程序辅助解释:
/** * Remove the eldest entries until the total of remaining entries is at or * below the requested size. * * @param maxSize the maximum size of the cache before returning. May be -1 * to evict even 0-sized elements. */ public void trimToSize(int maxSize) { while (true) { K key; V value; synchronized (this) { if (size < 0 || (map.isEmpty() && size != 0)) { throw new IllegalStateException(getClass().getName() + ".sizeOf() is reporting inconsistent results!"); } if (size <= maxSize) { break; } Map.Entry<K, V> toEvict = map.eldest(); if (toEvict == null) { break; } key = toEvict.getKey(); value = toEvict.getValue(); map.remove(key); size -= safeSizeOf(key, value); evictionCount++; } entryRemoved(true, key, value, null); } }
注意下面一段:
if (size <= maxSize) {
break;
}
Map.Entry<K, V> toEvict = map.eldest();
if (toEvict == null) {
break;
}
key = toEvict.getKey();
value = toEvict.getValue();
map.remove(key);
如果现在size已经达到maxSize了,那么下面就会从LinkedHashMap中移除最旧的资源,上面map只认识key啦!
下面可以做一个android 测试进行验证上面的:
<1> : 新建Android 工程:
<2> : 具体程序如下:
DurianMainActivity.java
package org.durian.durianlrubitmap;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v4.util.LruCache;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import org.durian.durianlrubitmap.util.DurianUtil;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashSet;
public class DurianMainActivity extends ActionBarActivity implements View.OnClickListener{
private final static String TAG="DurianMainActivity";
private final static int IMAGE_COUNT=3;
private final static String IMAGE_SRC[]={
"http://pic.qiantucdn.com/58pic/13/96/30/41S58PICpI9_1024.png",
"http://pic.qiantucdn.com/58pic/14/79/59/15s58PICMdP_1024.jpg",
"http://pic.qiantucdn.com/58pic/14/64/73/03q58PICp9d_1024.jpg",
};
//网络数据源,格式:json
//json 中罗列了图片的url列表信息
private final static String URL_LIST= "http://pugme.herokuapp.com/bomb?count=" + IMAGE_COUNT;
private LruCache<String,Bitmap> mMemoryCache;
private ImageView imageView;
private Button mButton;
private Button mDownButton;
private Button mLinkButton;
private ListView listView;
private Bitmap bitmap;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.durian_main);
mLinkButton=(Button)findViewById(R.id.link);
mLinkButton.setOnClickListener(this);
listView=(ListView)findViewById(android.R.id.list);
imageView=(ImageView)findViewById(R.id.image);
int maxMemory=(int)(Runtime.getRuntime().maxMemory()/1024);
int cacheSize=maxMemory/8;
mMemoryCache=new LruCache<String,Bitmap>(cacheSize){
@Override
protected int sizeOf(String key, Bitmap bitmap) {
Log.i(TAG,"bitmap cache size : "+bitmap.getByteCount()/1024);
return bitmap.getByteCount()/1024;
}
};
mButton=(Button)findViewById(R.id.button);
mButton.setOnClickListener(this);
mDownButton=(Button)findViewById(R.id.loadjson);
mDownButton.setOnClickListener(this);
}
public void addBitmapToMemoryCache(String key,Bitmap bitmap){
if(getBitmapFromMemCache(key)==null){
Log.i(TAG,"add bitmap key : "+key);
mMemoryCache.put(key,bitmap);
}
}
public Bitmap getBitmapFromMemCache(String key){
Log.i(TAG,"get bitmap key : "+key);
return mMemoryCache.get(key);
}
@Override
public void onClick(View v) {
int id=v.getId();
switch (id){
case R.id.button:
Bitmap map=getBitmapFromMemCache(urls);
if(map==null){
ImageLoadTask work=new ImageLoadTask();
work.execute();
}else{
imageView.setImageBitmap(map);
}
break;
case R.id.loadjson:
new ImageLoadListTask().execute();
break;
case R.id.link:
DurianUtil.DurianLinkedHashMap();
break;
default:
break;
}
}
private class ImageLoadListTask extends AsyncTask<String,Integer,ArrayList<String>>{
@Override
protected ArrayList<String> doInBackground(String... params) {
try {
URL url=new URL(URL_LIST);
try {
HttpURLConnection conn=(HttpURLConnection)url.openConnection();
conn.setRequestProperty("ACCEPT","application/json");
InputStream is=conn.getInputStream();
StringBuilder buf=new StringBuilder();
BufferedReader reader=new BufferedReader(new InputStreamReader(is),1024);
String buffer;
for (buffer=reader.readLine();buffer!=null;buffer=reader.readLine()){
buf.append(buffer);
}
is.close();
String resp=buf.toString();
JSONObject jsonObject= null;
JSONArray jsonArray=null;
HashSet<String> pugUrls=null;
try {
jsonObject = new JSONObject(resp);
jsonArray=jsonObject.getJSONArray("pugs");
pugUrls = new HashSet<String>(jsonArray.length());
for (int i = 0, z = jsonArray.length(); i < z; i++) {
//这个网络数据源不是太好,图片TMD太大了,有一些居然10多M,汗死.
//pugUrls.add(jsonArray.getString(i));
//下面提供选择几个不错的数据源用于加载
pugUrls.add(IMAGE_SRC[i]);
Log.i(TAG,"json : "+jsonArray.getString(i));
}
} catch (JSONException e) {
e.printStackTrace();
}
return new ArrayList<>(pugUrls);
} catch (IOException e) {
e.printStackTrace();
}
} catch (MalformedURLException e) {
e.printStackTrace();
}
return null;
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
}
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected void onPostExecute(ArrayList<String> ret) {
super.onPostExecute(ret);
DurianBaseAdapter adapter=new DurianBaseAdapter(DurianMainActivity.this,ret);
listView.setAdapter(adapter);
}
}
private final static String urls="http://pic.qiantucdn.com/58pic/13/96/30/41S58PICpI9_1024.png";//http://pic.qiantucdn.com/58pic/14/79/59/15s58PICMdP_1024.jpg";
private class ImageLoadTask extends AsyncTask<String,Integer,String>{
@Override
protected String doInBackground(String... params) {
try {
URL url=new URL(urls);
try {
HttpURLConnection conn=(HttpURLConnection)url.openConnection();
conn.setConnectTimeout(5000);
conn.setRequestMethod("GET");
InputStream is=conn.getInputStream();
bitmap= BitmapFactory.decodeStream(is);
addBitmapToMemoryCache(urls,bitmap);
mHandler.sendEmptyMessage(0);
} catch (IOException e) {
e.printStackTrace();
}
} catch (MalformedURLException e) {
e.printStackTrace();
}
return null;
}
@Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
}
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
}
}
private Handler mHandler=new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
imageView.setImageBitmap(bitmap);
}
};
}
DurianBaseAdapter.java
package org.durian.durianlrubitmap;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Rect;
import android.media.Image;
import android.os.AsyncTask;
import android.support.v4.util.LruCache;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
/**
* Project name : DurianLRUBitmap
* Created by zhibao.liu on 2016/1/13.
* Time : 12:15
* Email warden_sprite@foxmail.com
* Action : durian
*/
public class DurianBaseAdapter extends BaseAdapter {
private final static String TAG="DurianBaseAdapter";
private ArrayList<String> mUrlArray;
private Context mContext;
private LayoutInflater mInflater;
private ViewHolder mViewHolder;
private LruCache<String,Bitmap> mMemoryCache;
public DurianBaseAdapter(Context context, ArrayList<String> url_list){
mContext=context;
mUrlArray=url_list;
mInflater=LayoutInflater.from(mContext);
mViewHolder=new ViewHolder();
int maxMemory=(int)(Runtime.getRuntime().maxMemory()/1024);
int cacheSize=maxMemory/8;
mMemoryCache=new LruCache<String,Bitmap>(cacheSize){
@Override
protected int sizeOf(String key, Bitmap bitmap) {
Log.i(TAG,"bitmap cache size : "+bitmap.getByteCount()/1024);
return bitmap.getByteCount()/1024;
}
};
}
@Override
public int getCount() {
return 3;
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if(convertView==null){
convertView=mInflater.inflate(R.layout.list_item,null);
mViewHolder.textView=(TextView)convertView.findViewById(R.id.text);
mViewHolder.imageView=(ImageView)convertView.findViewById(R.id.image);
mViewHolder.textView.setText(mUrlArray.get(position).toString());
//download image
AyncLoadImage mAyncLoadImage=new AyncLoadImage(mViewHolder.imageView,position);
mAyncLoadImage.execute();
}
return convertView;
}
class ViewHolder{
TextView textView;
ImageView imageView;
}
public void addBitmapToMemoryCache(String key,Bitmap bitmap){
if(getBitmapFromMemCache(key)==null){
Log.i(TAG,"add bitmap key : "+key);
mMemoryCache.put(key,bitmap);
}
}
public Bitmap getBitmapFromMemCache(String key){
Log.i(TAG,"get bitmap key : "+key);
return mMemoryCache.get(key);
}
private class AyncLoadImage extends AsyncTask<String,Integer,Bitmap>{
WeakReference<ImageView> imageViewWeakRef;
int postion;
@Override
protected Bitmap doInBackground(String... params) {
Bitmap map;
//检查是否已经有缓存了
//如果有缓存了,就是用缓存
map=getBitmapFromMemCache(mUrlArray.get(postion).toString());
if(map!=null){
return map;
}
//如果没有缓存,就到网络上面去获取
try {
URL url=new URL(mUrlArray.get(postion).toString());
try {
HttpURLConnection conn=(HttpURLConnection)url.openConnection();
conn.setRequestMethod("GET");
InputStream is=conn.getInputStream();
map= BitmapFactory.decodeStream(is);
return map;
} catch (IOException e) {
e.printStackTrace();
}
} catch (MalformedURLException e) {
e.printStackTrace();
}
return null;
}
public AyncLoadImage(ImageView imageview,int postion) {
super();
imageViewWeakRef=new WeakReference<ImageView>(imageview);
this.postion=postion;
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
}
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
ImageView imageView=imageViewWeakRef.get();
ByteArrayOutputStream out=new ByteArrayOutputStream();
if(bitmap!=null) {
bitmap.compress(Bitmap.CompressFormat.JPEG, 50, out);
addBitmapToMemoryCache(mUrlArray.get(postion).toString(),bitmap);
}
imageView.setImageBitmap(bitmap);
}
}
}
DurianUtil.java
package org.durian.durianlrubitmap.util; import android.util.Log; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; /** * Project name : DurianLRUBitmap * Created by zhibao.liu on 2016/1/13. * Time : 18:33 * Email warden_sprite@foxmail.com * Action : durian */ public class DurianUtil { private final static String TAG="DurianUtil"; public static void DurianLinkedHashMap(){ Map<Integer,String> map=new LinkedHashMap<Integer, String>(); map.put(5,"hello,zhibao.liu"); map.put(8,"hello,world !"); map.put(3,"hello,java programe !"); for (Iterator it=map.keySet().iterator();it.hasNext();){ Object key=it.next(); Log.i(TAG,"link key : "+key.toString()); } Log.i(TAG,"******************************************"); Map<Integer,String> hashmap=new HashMap<Integer, String>(); hashmap.put(5,"hello,zhibao.liu"); hashmap.put(8,"hello,world !"); hashmap.put(3,"hello,java programe !"); for (Iterator it=hashmap.keySet().iterator();it.hasNext();){ Object key=it.next(); Log.i(TAG,"hash key : "+key.toString()); } } }
<3> : 运行程序后,界面上面有三个按钮,"LINK","BUTTON","JSON"按钮:
LINK按钮是LinkedHashMap的一个测试样例;
BUTTON是初步熟悉LRUCche的基本使用;
JSON是从网络上面获取一组数据,加载到ListView中,在加载的时候,有限判断缓存是否已经有了,如果有,直接加载缓存中的,如果没有再通过网络获取资源.
注意: Mainfiest文件需要添加联网权限:
<uses-permission android:name="android.permission.INTERNET" />
其实还是比较好理解.
程序另外几个地方有几个概念,我觉得下面说的还不错,借过来,如下:
WeakReference与SoftReference都可以用来保存对象的实例引用,这两个类与垃圾回收有关。
WeakReference是弱引用,其中保存的对象实例可以被GC回收掉。这个类通常用于在某处保存对象引用,而又不干扰该对象被GC回收,通常用于Debug、内存监视工具等程序中。因为这类程序一般要求即要观察到对象,又不能影响该对象正常的GC过程。
最近在JDK的Proxy类的实现代码中也发现了Weakrefrence的应用,Proxy会把动态生成的Class实例暂存于一个由Weakrefrence构成的Map中作为Cache。
SoftReference是强引用,它保存的对象实例,除非JVM即将OutOfMemory,否则不会被GC回收。这个特性使得它特别适合设计对象Cache。对于Cache,我们希望被缓存的对象最好始终常驻内存,但是如果JVM内存吃紧,为了不发生OutOfMemoryError导致系统崩溃,必要的时候也允许JVM回收Cache的内存,待后续合适的时机再把数据重新Load到Cache中。这样可以系统设计得更具弹性
相关文章推荐
- android布局 Android fill_parent wrap_content 和match_parent的区别
- Android源码分析-全面理解Context
- undefined reference to 'typeinfo for android::Thread'
- android中的AIDL进程间通信
- Android退出所有Activity最优雅的方式
- Android 开源项目NineOldAnimations详解
- Android缓存的一个Demo
- Android 仿转转商品详情页面图片消失方式
- Android View系统解析(下)
- Android View系统解析(上)
- AndroidStudio操作笔记--如何加载外部aar包
- Android应用签名打包(一)
- Android 创建其他程序对应的Context
- Android音乐播放器乱码
- Android之Activity的栈管理
- RxJava开发精要7 - Schedulers-解决Android主线程问题
- androidStudio 发布自己的jar
- android Asynctask 和Handler的优缺点
- Android Studio IDE Out of Memory
- Android RecyclerView中实现自定义GridView的方式(实现不同item)