您的位置:首页 > 移动开发 > Android开发

Android三级图片缓存

2016-05-23 23:49 751 查看
一级:在内存中将下载的图片保存起来。使用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;

4000
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);
}


}

“`
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  存储 android 图片