android之图片异步加载并缓存到本地
在android项目中访问网络图片是非常普遍性的事情,如果我们每次请求都要访问网络来获取图片,会非常耗费流量,而且图片占用内存空间也比较大,图片过多且不释放的话很容易造成内存溢出。针对上面遇到的两个问题,首先耗费流量我们可以将图片第一次加载上面缓存到本地,以后如果本地有就直接从本地加载。图片过多造成内存溢出,这个是最不容易解决的,要想一些好的缓存策略,比如大图片使用LRU缓存策略或懒加载缓存策略。今天首先介绍一下本地缓存图片。
首先看一下异步加载缓存本地代码:
- public class AsyncBitmapLoader
- {
- /**
- * 内存图片软引用缓冲
- */
- private HashMap<String, SoftReference<Bitmap>> imageCache = null;
- public AsyncBitmapLoader()
- {
- imageCache = new HashMap<String, SoftReference<Bitmap>>();
- }
- public Bitmap loadBitmap(final ImageView imageView, final String imageURL, final ImageCallBack imageCallBack)
- {
- //在内存缓存中,则返回Bitmap对象
- if(imageCache.containsKey(imageURL))
- {
- SoftReference<Bitmap> reference = imageCache.get(imageURL);
- Bitmap bitmap = reference.get();
- if(bitmap != null)
- {
- return bitmap;
- }
- }
- else
- {
- /**
- * 加上一个对本地缓存的查找
- */
- String bitmapName = imageURL.substring(imageURL.lastIndexOf("/") + 1);
- File cacheDir = new File("/mnt/sdcard/test/");
- File[] cacheFiles = cacheDir.listFiles();
- int i = 0;
- if(null!=cacheFiles){
- for(; i<cacheFiles.length; i++)
- {
- if(bitmapName.equals(cacheFiles[i].getName()))
- {
- break;
- }
- }
- if(i < cacheFiles.length)
- {
- return BitmapFactory.decodeFile("/mnt/sdcard/test/" + bitmapName);
- }
- }
- }
- final Handler handler = new Handler()
- {
- /* (non-Javadoc)
- * @see android.os.Handler#handleMessage(android.os.Message)
- */
- @Override
- public void handleMessage(Message msg)
- {
- // TODO Auto-generated method stub
- imageCallBack.imageLoad(imageView, (Bitmap)msg.obj);
- }
- };
- //如果不在内存缓存中,也不在本地(被jvm回收掉),则开启线程下载图片
- new Thread()
- {
- /* (non-Javadoc)
- * @see java.lang.Thread#run()
- */
- @Override
- public void run()
- {
- // TODO Auto-generated method stub
- InputStream bitmapIs = HttpUtils.getStreamFromURL(imageURL);
- Bitmap bitmap = BitmapFactory.decodeStream(bitmapIs);
- imageCache.put(imageURL, new SoftReference<Bitmap>(bitmap));
- Message msg = handler.obtainMessage(0, bitmap);
- handler.sendMessage(msg);
- File dir = new File("/mnt/sdcard/test/");
- if(!dir.exists())
- {
- dir.mkdirs();
- }
- File bitmapFile = new File("/mnt/sdcard/test/" +
- imageURL.substring(imageURL.lastIndexOf("/") + 1));
- if(!bitmapFile.exists())
- {
- try
- {
- bitmapFile.createNewFile();
- }
- catch (IOException e)
- {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- FileOutputStream fos;
- try
- {
- fos = new FileOutputStream(bitmapFile);
- bitmap.compress(Bitmap.CompressFormat.PNG,
- 100, fos);
- fos.close();
- }
- catch (FileNotFoundException e)
- {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- catch (IOException e)
- {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }.start();
- return null;
- }
- public interface ImageCallBack
- {
- public void imageLoad(ImageView imageView, Bitmap bitmap);
- }
- }
这是一个封装好的异步加载图片类,缓存了两份,一份是使用软引用缓存到内存中,一份是缓存到本地sd卡,如果内存中没有,则从本地查找,如果本地没有则从网络获取图片。
- public class HttpUtils {
- public static InputStream getStreamFromURL(String imageURL) {
- InputStream in=null;
- try {
- URL url=new URL(imageURL);
- HttpURLConnection connection=(HttpURLConnection) url.openConnection();
- in=connection.getInputStream();
- } catch (Exception e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- return in;
- }
- }
这是一个访问网络获取类。不细说了。
下面看一下如何使用封装好的异步加载图片的类:
- public class ImageCacheActivity extends Activity {
- /** Called when the activity is first created. */
- private ListView listview;
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- listview=(ListView) findViewById(R.id.listView_list);
- MyAdapter adapter=new MyAdapter();
- listview.setAdapter(adapter);
- }
- private class MyAdapter extends BaseAdapter{
- private AsyncBitmapLoader asyncBitmapLoader;
- public MyAdapter(){
- asyncBitmapLoader=new AsyncBitmapLoader();
- }
- @Override
- public int getCount() {
- // TODO Auto-generated method stub
- return 10;
- }
- @Override
- public Object getItem(int position) {
- // TODO Auto-generated method stub
- return null;
- }
- @Override
- public long getItemId(int position) {
- // TODO Auto-generated method stub
- return 0;
- }
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- // TODO Auto-generated method stub
- if(convertView==null){
- convertView=LayoutInflater.from(getApplicationContext()).inflate(R.layout.list_item, null);
- }
- ImageView image=(ImageView) convertView.findViewById(R.id.addexam_list_icon);
- String imageURL="http://s.ata.net.cn/4f98db46908987a21a000003/logo/2012/04/114_80aaf295c083d07a496743699aac3193.png";
- Bitmap bitmap=asyncBitmapLoader.loadBitmap(image, imageURL, new ImageCallBack() {
- @Override
- public void imageLoad(ImageView imageView, Bitmap bitmap) {
- // TODO Auto-generated method stub
- imageView.setImageBitmap(bitmap);
- }
- });
- if(bitmap == null)
- {
- image.setImageResource(R.drawable.ic_launcher);
- }
- else
- {
- image.setImageBitmap(bitmap);
- }
- return convertView;
- }
- }
- }
这样就完成了,网络获取不到bitmap则显示默认图片。
这是一种很实用的方法,大家自己试试吧!