Android三级图片缓存

一级:在内存中将下载的图片保存起来。使用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);
}

}
“`

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值