一级:在内存中将下载的图片保存起来。使用HashMap存储。其中url为图片在服务器端存储的位置,唯一的
真正开发时,使用的是LruCache类来实现内存数据的缓存。好处:能够自动的清理最少使用的内存中的数据。
优点:加载速度最快
二级:在手机存储中将下载的图片保存起来。保存的路径为:storage/sdcard/Android/data/应用包名/files/xxx.jpg
说明:①如果保存在sd卡中,一定需要写入SD卡的权限。②可以考虑的存储位置:sd卡的路径1,使 用storage/sdcard/Android/data/应用包名/files/ 或者可以考虑手机内部存储位置:data/data/应用包名/files/
优点:不需要联网
三级:图片保存在服务器上。
优点:图片资源一定存在
二、如何实现图片的三级缓存
过程一:首先考虑从一级缓存中,查找指定url对应的Bitmap对象是否存在。
如果存在:将此Bitmap对象设置到ImageView上显示。
如果不存在:考虑二级缓存
过程二:其次从二级缓存指定的文件目录下,找相应的url对应的文件是否存在?
url: http://192.168.56.1:8080//Web_server/images/f18.jpg
filePath:storage/sdcard/Android/data/应用包名/files/f18.jpg
如果url对应的file存在:使用BitmapFactory.decodeFile(Filepath)将返回的Bitmap对象设置给ImageView显示,
同时,将此Bitmap对象保存在一级缓存中
如果url对应的file不存在:考虑使用三级缓存
过程三:使用AsyncTask实现联网的操作,找指定的url对应的图片资源。
如果找到了:使用流的方式将此资源下载,并还原为内存中的Bitmap对象,并设置给ImageView显示。同时,将此对象
保存到一级、二级缓存中
如果没有找到指定的资源:给ImageView设置一个“加载失败”的图片
三、问题的解决
1.问题:出现了图片的闪动
2.问题的原因?复用了convertView.
(如果初始化ListView时,界面上有9个item,那么最多需要10个convertView(意味着就有10个ImageView)
来盛装所有的数据显示。但是集合数据的大小比如是100个,即有100个ShopInfo对象,意味着有100个url。
也就是有100个url加载过来的图片需要显示在有限的10个ImageView上)
3.如何解决?
3.1 当调用getView()–>ImageLoader.loadImage()方法时,就给当前的ImageView设置一个tag:使用url充当
3.2 当在分线程联网之前(doInBackgroud()时),判断即将联网的url1地址与当前ImageView最新的tag是否一致
一致:那就执行联网的操作
不一致:取消对url1的联网操作
3.3 在联网下载了图片以后,判断是否要将此图片设置给ImageView显示。
如何判断:判断联网下载的图片对应的url是否与当前ImageView最新的tag一致
一致:那就将下载的图片设置给ImageView显示
不一致:不要显示。
下面是具体实现的代码(前提你必须先把你的WEB工程搭建好和在列表清单中加入联网权限,如果是自带的模拟器还需要加入sd卡的读写权限)
main.xml
<ListView
android:id="@+id/lv_main"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</ListView>
<LinearLayout
android:id="@+id/ll_main_loading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:orientation="vertical"
android:visibility="gone" >
<ProgressBar
style="?android:attr/progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="正在加载中"
android:textSize="20sp" />
</LinearLayout>
item_main_load.xml 用来布局你的商品排列的位置
这里写代码片
<ImageView
android:id="@+id/iv_item_icon"
android:layout_width="80dp"
android:layout_height="80dp"
android:scaleType="fitXY"
android:src="@drawable/ic_launcher" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center_vertical"
android:layout_marginLeft="10dp"
>
<TextView
android:id="@+id/tv_item_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="商品名称"
android:textSize="20sp"
android:layout_marginBottom="5dp"
/>
<TextView
android:id="@+id/tv_item_price"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="商品价格"
android:textSize="20sp"
/>
</LinearLayout>
MainActivity .java
public class MainActivity extends ActionBarActivity {
Handler handler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_OK:
lv_main.setAdapter(new ShopAdapter(MainActivity.this,list));
ll_main_loading.setVisibility(View.GONE);
break;
case MSG_NULL:
Toast.makeText(MainActivity.this, "请求数据失败", 0).show();
ll_main_loading.setVisibility(View.GONE);
break;
case MSG_ERROR:
Toast.makeText(MainActivity.this, "联网失败", 0).show();
ll_main_loading.setVisibility(View.GONE);
break;
}
};
};
private ListView lv_main;
private LinearLayout ll_main_loading;
private List<ShopInfo> list;
private ShopAdapter shopadadpter;
protected static final int MSG_OK = 0;
protected static final int MSG_NULL = 1;
protected static final int MSG_ERROR = 2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
lv_main = (ListView) findViewById(R.id.lv_main);// 得到ListView控件
ll_main_loading = (LinearLayout) findViewById(R.id.ll_main_loading);// 得到LinearLayout控件
// shopadadpter = new ShopAdapter(MainActivity.this, list);
// 1 主线程 显示提示视图
ll_main_loading.setVisibility(View.VISIBLE);
// 2 子线程 联网下载数据
new Thread() {
private HttpURLConnection conn;
private InputStream inputStream;
public void run() {
String path = "http://192.168.56.1:8080/Web_server/ShopListServlet";
try {
URL url = new URL(path);
conn = (HttpURLConnection) url
.openConnection();
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
conn.connect();
int code = conn.getResponseCode();
if (code == 200) {
inputStream = conn.getInputStream();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = -1;
while ((len = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, len);
}
String josnarray = outputStream.toString();// 得到服务端传送过来的数据
Gson gson = new Gson();// 创建GSon对象
list = gson.fromJson(josnarray,
new TypeToken<List<ShopInfo>>() {
}.getType());
String tag="tag";
handler.sendEmptyMessage(MSG_OK);
} else {
handler.sendEmptyMessage(MSG_NULL);
}
} catch (Exception e) {
handler.sendEmptyMessage(MSG_ERROR);
e.printStackTrace();
}finally{
if(conn!=null){
try {
conn.connect();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(inputStream!=null){
try {
inputStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
};
}.start();
}
}
ShopAdapter.java 适配器
public class ShopAdapter extends BaseAdapter{
private Context context;
private List<ShopInfo> list;
private Imageload imageload ;
String tag="tag";
public ShopAdapter(Context context, List<ShopInfo> list) {
super();
this.context = context;
this.list = list;
imageload = new Imageload(context, R.drawable.loading, R.drawable.error);
}
@Override
public int getCount() {
return list.size();
}
@Override
public Object getItem(int arg0) {
// TODO Auto-generated method stub
return list.get(arg0);
}
@Override
public long getItemId(int arg0) {
// TODO Auto-generated method stub
return arg0;
}
@Override
public View getView(int arg0, View arg1, ViewGroup arg2) {
if(arg1==null){
arg1=View.inflate(context, R.layout.item_main_load, null);
}
//获取相应的视图对象
ImageView iv_item_icon = (ImageView) arg1.findViewById(R.id.iv_item_icon);//商品的图片
TextView tv_item_name = (TextView) arg1.findViewById(R.id.tv_item_name);//商品的名称
TextView tv_item_price = (TextView) arg1.findViewById(R.id.tv_item_price);//商品的价格
//装配数据
ShopInfo shopInfo = list.get(arg0);
tv_item_name.setText(shopInfo.getName());
tv_item_price.setText("价格: Y"+shopInfo.getPrice());
imageload.loadImage(shopInfo.getImagepath(),iv_item_icon);
return arg1;
}
}
ShopInfo.java 商品的实体类
public class ShopInfo {
private int id;//商品id
private String name;//商品名字
private double price;//商品价格
private String imagepath;//图片路径
public ShopInfo() {
super();
// TODO Auto-generated constructor stub
}
public ShopInfo(int id, String name, double price, String imagepath) {
super();
this.id = id;
this.name = name;
this.price = price;
this.imagepath = imagepath;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public String getImagepath() {
return imagepath;
}
public void setImagepath(String imagepath) {
this.imagepath = imagepath;
}
}
Imageload.java 实现缓存的具体方法
public class Imageload {
private Context context;
private int loadimage;
private int errorimage;
String tag=”tag”;
public Imageload(Context context, int loadimage, int errorimage) {
super();
this.context = context;
this.loadimage = loadimage;
this.errorimage = errorimage;
}
HashMap<String, Bitmap> map=new HashMap<String, Bitmap>();
/**
* 图片加载的方法
* @param url 对应图片的服务器上的url
* @param imageview 将要设置的ImageView对象
*/
public void loadImage(String url,ImageView imageview){
imageview.setTag(url);//将url关联Imageview对象
//一级缓存
Bitmap bitmap=FormfirstCache(url);
if(bitmap!=null){
imageview.setImageBitmap(bitmap);
return;
}
//二级缓存
bitmap=FomSecondCache(url);
if(bitmap!=null){
imageview.setImageBitmap(bitmap);
map.put(url, bitmap);
return;
}
//三级缓存
Fromthrid(url,imageview);
}
/**
* 三级缓存
* 联网下载图片数据的操作
* @param url
* @param imageview
*/
private void Fromthrid(final String url, final ImageView imageview) {
new AsyncTask<Void, Void, Bitmap>() {
@Override
protected void onPreExecute() {
//第一步 主线程显示提示视图
imageview.setImageResource(loadimage);
}
//第二部 联网下载数据
@Override
protected Bitmap doInBackground(Void... arg0) {
Bitmap bitmap = null;
HttpURLConnection conn=null;
InputStream inputStream=null;
//在进行联网操作之前,判断要联网获取的图片的url地址与当前的imageView要显示的url地址是否一致
String currenturl = (String) imageview.getTag();
if(currenturl != url){
return null;//表示如果两个地址不同,就说明出现了convertView的复用,那就不要联网了
}
try {
URL urlweb = new URL(url);
conn = (HttpURLConnection) urlweb.openConnection();
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
conn.connect();
int code = conn.getResponseCode();
if(code==200){
inputStream = conn.getInputStream();
bitmap = BitmapFactory.decodeStream(inputStream);
//同时将bitmap对象缓存到一级 二级 缓存中去
//一级缓存
map.put(url, bitmap);
//二级缓存
String filename = url.substring(url.lastIndexOf("/")+1);//文件名 f18.jpg
String filepath=context.getExternalFilesDir(null).getPath()+"/"+filename;
File file=new File(filepath);
//将内层Bitmap的对象保存到手机内部
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, new FileOutputStream(file));
return bitmap;
}
} catch (Exception e) {
e.printStackTrace();
}
finally{
if(conn!=null){
conn.disconnect();
}
if(inputStream!=null){
try {
inputStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
return null;
}
@Override
protected void onPostExecute(Bitmap result) {
String curneturl = (String) imageview.getTag();
if(curneturl!=url){
return;
}
if(result!=null){
imageview.setImageBitmap(result);
}
}
}.execute();
}
/**
* 二级缓存
* @param url http://192.168.10.165:8080//Web_server/images/f18.jpg
* @return filepath storage/sdcard/Android/data/应用包名/files/f18.jpg
*/
private Bitmap FomSecondCache(String url) {
String filename = url.substring(url.lastIndexOf("/")+1);//文件名 f18.jpg
String filepath=context.getExternalFilesDir(null).getPath()+"/"+filename;
Bitmap bitmap = BitmapFactory.decodeFile(filepath);
return bitmap;
}
/**
* 一级缓存
* @param url
* @return
*/
private Bitmap FormfirstCache(String url) {
return map.get(url);
}
}
“`