【Android】网络图片加载优化(一)利用弱引用缓存异步加载

原创 2013年11月08日 23:27:38

应用背景

ListView 或GridView是一种可以显示一系列项目并能进行滚动显示的 View,每一行的Item可能包含复杂的结构。

其可能会从网络上获取一些图片信息,就现在的网络速度要想保持ListView运行的很好滚动流畅是做不到的。

所以这里就需要把这些信息利用多线程实现异步加载,同时,应用弱引用缓存技术方便再次加载时预览。

工作原理

使用了SoftReference来缓存图片,允许 GC在需要的时候可以对缓存中的图片进行清理。

它是这样工作:

(1) 调用 loadDrawable(ImageUrl, imageCallback),传入一个匿名实现的 ImageCallback接口;

(2)如果图片在缓存中不存在的话,图片将从单一的线程中下载并在下载结束时通过 ImageCallback回调;

(3)如果图片确实存在于缓存中,就会马上返回,不会回调 ImageCallback。

弱应用缓存类的封装

package com.wei.util;

import java.lang.ref.SoftReference;
import java.net.URL;
import java.util.HashMap;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os.Message;


public class AsyncImageLoader{
	Bitmap bitmap = null;
	HashMap<String, SoftReference<Bitmap>> imageCache;
	BitmapFactory.Options opts;
	// 1.根据URL返回Drawable的函数
	// 2.工具类的核心函数,包含handler+thread

	public AsyncImageLoader() {
		imageCache = new HashMap<String, SoftReference<Bitmap>>();
		opts = new BitmapFactory.Options();   
		opts.inSampleSize = 0;    //这个的值压缩的倍数(2的整数倍),数值越小,压缩率越小,图片越清晰    
	}

	public Bitmap asyncLoadImage(final String strUrl, final int i, final LoadFinishCallBack loadFinishCallBack) {
		Bitmap bitmap = null;
		//首先判断Map中是否有这种图片的缓存,若有,直接返回该图片的引用
		if(imageCache.containsKey(strUrl)) {
			SoftReference<Bitmap> softReference = imageCache.get(strUrl);
			bitmap = softReference.get();
			if(bitmap != null) {
				return bitmap;
			}
		}
		
		final Handler handler = new Handler() {
			@Override
			public void handleMessage(Message msg) {
				// 回调接口
				loadFinishCallBack.loadFinish(strUrl, i, (Bitmap) msg.obj);
			}
		};

		new Thread() {
			@Override
			public void run() {
				Bitmap bitmap = null;
				try {
					bitmap = BitmapFactory.decodeStream(new URL(strUrl).openConnection().getInputStream(), null, opts);	
				} catch (Exception e) {
					e.printStackTrace();
				}
				imageCache.put(strUrl, new SoftReference<Bitmap>(bitmap));//第一次拿某张图片,把该图片的引用放入缓存中
				Message msg = new Message();
				msg.obj = bitmap;
				//System.out.println("bitmap 宽高"+bitmap.getWidth()+"-"+bitmap.getHeight());
				handler.sendMessage(msg);
			}
		}.start();

		return bitmap;
	}

	public interface LoadFinishCallBack {
		public void loadFinish(String strUrl, int i, Bitmap bitmap);
	}
}

弱引用缓存类的应用

			Bitmap bm = asyncImageLoader.asyncLoadImage(strUrl, 0, callback);
			oneView.image.setImageBitmap(null);
			if(bm==null) {
				oneView.image.setBackgroundResource(R.drawable.slt);
			} else {
				oneView.image.setImageBitmap(bm);
			}
其中,asyncImageLoader是上述封装类的实例,R.drawable.slt是图片未加载出来时显示的图片资源。
callback是所调用的接口:
	//图片下载成功后执行的回调接口
	LoadFinishCallBack callback = new LoadFinishCallBack() {
		@Override
		public void loadFinish(String strUrl, int i, Bitmap bitmap) {
			if (bitmap != null) {
				ImageView imageView = (ImageView) gridView.findViewWithTag(strUrl);
				if(imageView != null) {
					imageView.setImageBitmap(bitmap);
				}
			}
		};
	};

应用举例

package com.wei.adapter;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import com.wei.util.AsyncImageLoader;
import com.wei.util.MyApplication;
import com.wei.util.MyImgCache;
import com.wei.util.MyThread;
import com.wei.util.AsyncImageLoader.LoadFinishCallBack;
import com.wei.wotao.FavorActivity;
import com.wei.wotao.R;
import com.wei.wotao.WebViewActivity;


import android.R.integer;
import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;


public class FavorAdapter extends BaseAdapter{
	private LayoutInflater mInflater;// 动态布局映射
	private JSONArray list;
	private Context context; 
	GridView gridView;
	private int i = 0;
	AsyncImageLoader asyncImageLoader;
	int itemWidth;
	private String nickname;
	public FavorAdapter(JSONArray list,GridView gridView,Context context,String nickname){
		this.list = list;
		this.context = context;
		this.gridView = gridView;
		this.mInflater = LayoutInflater.from(context);
		itemWidth = MyApplication.screenWidth/2;
		asyncImageLoader  = new AsyncImageLoader();
		this.nickname = nickname;
//		SharedPreferences share = getSharedPreferences("logInfo", 2);
//		nickname = share.getString("username", "");
	}
	public void setList(JSONArray list) {
		this.list = list;
	}
	public JSONArray getList() {
		return list;
	}
	@Override
	public int getCount() {
		// TODO Auto-generated method stub
		return list.length();
	}

	@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
		OneView oneView;
		if (convertView == null) {
			convertView = mInflater.inflate(R.layout.subitem_item_grid_favor2, null);//根据布局文件实例化view
			oneView = new OneView();
			oneView.promotion_price = (TextView) convertView.findViewById(R.id.promotion_price);//找某个控件
			oneView.price = (TextView) convertView.findViewById(R.id.price);
			oneView.sub_price = (TextView) convertView.findViewById(R.id.sub_price);
			oneView.image = (ImageView) convertView.findViewById(R.id.image);
			oneView.remove = (Button) convertView.findViewById(R.id.remove);
			convertView.setTag(oneView);//把View和某个对象关联起来
		} else {
			oneView = (OneView) convertView.getTag();
		}
		JSONObject jObject = null;
		try {
			jObject = list.getJSONObject(position);//根据position获取集合类中某行数据
			oneView.promotion_price.setText("现价¥"+jObject.get("promotion_price").toString());//给该控件设置数据(数据从集合类中来)
			oneView.price.setText("原价¥"+jObject.get("price").toString());
			oneView.price.getPaint().setFlags(Paint. STRIKE_THRU_TEXT_FLAG);
			Float float1 = Float.valueOf(jObject.get("price").toString())- Float.valueOf(jObject.get("promotion_price").toString());
			if (float1 == 0) {
				oneView.sub_price.setVisibility(View.INVISIBLE);
			}
			oneView.sub_price.setText(float1.toString()+" ");
			String strUrl = jObject.get("pic_url").toString() + "_310x310.jpg";
			oneView.image.setTag(strUrl);
			Bitmap bm = asyncImageLoader.asyncLoadImage(strUrl, 0, callback);
			oneView.image.setImageBitmap(null);
			if(bm==null) {
				oneView.image.setBackgroundResource(R.drawable.slt);
			} else {
				oneView.image.setImageBitmap(bm);
			}
			bm = null;
			final String itemID = jObject.get("itemID").toString();
			oneView.remove.setOnClickListener(new View.OnClickListener() {
				@Override
				public void onClick(View v) {
					// TODO Auto-generated method stub
					new MyThread("favor.php?frm=3g&action=favorOrCancel&nickname=" + nickname
							+"&id="+itemID+"&type=sp&favor=0", 0, handler).start();
					Toast.makeText(context, "取消成功"+nickname, 1000).show();
					
				}
			});
		} catch (Exception e) {
			// TODO: handle exception
		}
		return convertView;
	}
	/** 把每行布局文件的各个控件包装成一个对象  */
	private class OneView{
		TextView promotion_price;
		TextView price;
		TextView sub_price;
		ImageView image;
		Button remove;
	}
	//图片下载成功后执行的回调接口
	LoadFinishCallBack callback = new LoadFinishCallBack() {
		@Override
		public void loadFinish(String strUrl, int i, Bitmap bitmap) {
			if (bitmap != null) {
				ImageView imageView = (ImageView) gridView.findViewWithTag(strUrl);
				if(imageView != null) {
					imageView.setImageBitmap(bitmap);
				}
			}
		};
	};
	private Handler handler = new Handler() {
		public void handleMessage(android.os.Message msg) {
			if (msg.what == 0){
				String string = (String) msg.obj;
				if("1".equals(string)){
					Toast.makeText(context, "取消成功", 1000).show();
				}
			}
		};
	};
}



网络图片加载优化(一)利用弱引用缓存异步加载

【Android】网络图片加载优化(一)利用弱引用缓存异步加载 分类: Android开发2013-11-08 23:27 581人阅读 评论(2) 收藏 举报 android异步...
  • pi9nc
  • pi9nc
  • 2014年01月21日 19:44
  • 761

Android异步加载学习笔记之四:利用缓存优化网络加载图片及ListView加载优化

如果不做任何处理,直接用网络加载图片在网速快的情况下可能没什么不好的感觉,但是如果使用移动流量或是网络不好的时候,问题就来了,要么用户会抱怨流量使用太多,要么抱怨图片加载太慢,如论从哪个角度出发,都不...
  • true100
  • true100
  • 2015年08月11日 21:02
  • 931

浅谈Android中的异步加载之ListView中图片的缓存及优化三

隔了很久没写博客,现在必须快速脉动回来。今天我还是接着上一个多线程中的异步加载系列中的最后一个使用异步加载实现ListView中的图片缓存及其优化。具体来说这次是一个综合Demo.但是个人觉得里面还算...
  • u013064109
  • u013064109
  • 2016年06月25日 02:56
  • 6517

Android网络图片加载优化

当我们使用淘宝app浏览产品的时候(很多其他app也是如此),就会发现每次下拉产品目录进行更新加载新图片的时候,都是出现对应的Item的时,才开始从网络下载并加载图片,从而出现很长的延时~...
  • chen52671
  • chen52671
  • 2015年03月28日 21:44
  • 1710

Android图片异步加载框架Android-Universal-Image-Loader

Android-Universal-Image-Loader是一个图片异步加载,缓存和显示的框架。这个框架已经被很多开发者所使用,是最常用的几个Android开源项目之一,主流的应用,随便反编译几个,...
  • HanTangSongMing
  • HanTangSongMing
  • 2014年12月17日 09:03
  • 17470

Android网络图片加载三级缓存

一、概述网络图片的加载在Android的开发中是一个必不可少的功能,今天我们就来聊聊网络图片加载三级缓存策略。 所谓的三级缓存策略是指通过网络、本地、内存三级来缓存图片,减少不必要的网络交互,避免浪...
  • chenzheng8975
  • chenzheng8975
  • 2017年02月11日 17:01
  • 559

android 视频的缩略图 缓存机制和 异步加载缩略图

在这次的工作开发项目中,涉及到一个视频缩略图的视频列表;这个在大家看来,制作视频缩略图就是两行代码就搞定的事。确实是这样的,百度一下,每个帖子都知道制作视频缩略图的方法,在这里确实也是一样的,但是我要...
  • CSDN_LQR
  • CSDN_LQR
  • 2016年05月15日 16:32
  • 627

Android图片加载优化--图片缓存

转载请注明出处:作者CJstar为什么要使用内存缓存 内存缓存: 内存缓存是指将已经赋值的对象保存在内存中,当再次使用的时候直接去内存读取,不再做重复的创建操作。 内存缓存的优势: 对象的重复使...
  • CJ_star
  • CJ_star
  • 2015年09月17日 19:52
  • 1027

网络环境较差时,优化网络加载图片

思路:从加载图片的本身和手机的存储两方面考虑 解决办法: 1.找现有图片格式的替换着 在众多的图片格式中,选择Google的WebP。理由:压缩效率高,而且对android的支持更优秀,使用We...
  • zzc901205
  • zzc901205
  • 2016年09月26日 15:46
  • 421

Android 图片加载优化

Android中通过Bitmap对象来使用图片,在加载Bitmap对象的时候,可能会导致UI线程被阻塞,用户体验差或者ANR问题;Bitmap对象迅速的消耗掉大量的内存,出现OutOfMemory异常...
  • u010483016
  • u010483016
  • 2015年06月01日 10:21
  • 972
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:【Android】网络图片加载优化(一)利用弱引用缓存异步加载
举报原因:
原因补充:

(最多只允许输入30个字)