Android--三级缓存

例子:从客户端发送联网请求来获取服务器端列表数据,包括
图片

三级缓存:这里的第三级缓存是用了AsyncTask
个人理解三级缓存:
1,一级缓存是将图片资源加载进内存,能保证在使用时加载过的图片,快速滑动时,不需要加载
2,二级缓存是将图片写入硬盘,即使关了机,也是保存到了本地
3,三级缓存是去服务器端读取对应的图片

在ListView使用三级缓存会存在图片闪动的bug
原因:
	converView被复用
解决:
	1,每次getView()都将图片的url保存ImageView上:imageView.setTag(imagePath)
	2,在分线程中准备请求服务器加载图片之前,比较加载图片的url与ImageView中保存的最新图片的Url是否同一个,
		如果是,继续执行加载远程图片
		如果不是,当前加载的图片的任务不应该再执行
	3,在主线程准备显示图片之前,比较加载到图片的url与ImageView中保存的最新图片的url是否同一个
		如果不是同一个,不需要显示此图片
		如果相同,显示
/*
    用于加载图片,并显示的类
 */
public class ImageLoader {

    private Context context;
    private int loadingImage;
    private int errorImage;

    public ImageLoader(Context context, int loadingImage, int errorImage) {
        this.context = context;
        this.loadingImage = loadingImage;
        this.errorImage = errorImage;
    }

    private Map<String, Bitmap> cacheMap=new HashMap<String,Bitmap>();

    public void loadIamge(String imagePath, ImageView imageView) {
        //将需要显示的图片URL保存到imageView身上
        //getTag()相当于标识
        //为了检测视图是否被复用
        imageView.setTag(imagePath);

        //一级缓存
        /*
            如果有,显示
            如果没有,进入二级缓存
         */
        Bitmap bitmap=getFristCache(imagePath);
        if(bitmap!=null){
            imageView.setImageBitmap(bitmap);
            return;
        }

        //二级缓存
        /*
            如果有,显示,并保存在一级缓存
            如果没有,进入三级缓存
         */
        bitmap=getSecondCasche(imagePath);
        if(bitmap!=null){
            imageView.setImageBitmap(bitmap);
            cacheMap.put(imagePath,bitmap);
            return;
        }

        //三级缓存
        /*
            没有显示错误,
            有则保存进一二级缓存,并显示
         */
        loadingImageFromThirdCache(imagePath,imageView);
    }

    /*
        根据图片url从三级缓存中取对应的bitmap对象
     */
    private void loadingImageFromThirdCache(final String imagePath, final ImageView imageView) {
        new AsyncTask<Void,Void,Bitmap>() {
            @Override
            protected void onPreExecute() {
                imageView.setImageResource(loadingImage);

            }

            //联网请求得到Bimap对象
            //在分线程中执行,可能需要等待一定时间才会被执行
            //在等待过程中imageVIew得tag值就可能改变了
            //如果改变了,就不应该再去加载(此图片此时不需要显示)
            @Override
            protected Bitmap doInBackground(Void... voids) {
                //得到连接
                Bitmap bitmap=null;
                try {
                    /*
                        在准备请求服务器图片前,判断是否需要加载
                        也就是我们为每张图片设置标识,但是当我们快速滑动时

                     */
                    @SuppressLint("WrongThread")
                    String newImagePath= (String) imageView.getTag();
                    if(newImagePath!=imagePath){//视图已经复用
                        return null;
                    }

                    URL url=new URL(imagePath);
                    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                    connection.setConnectTimeout(5000);
                    connection.setReadTimeout(5000);
                    connection.connect();
                    int responseCode = connection.getResponseCode();
                    if(responseCode==200){
                        InputStream is=connection.getInputStream();
                        //将is封装为bitmap
                        bitmap = BitmapFactory.decodeStream(is);
                        is.close();

                        if(bitmap!=null){
                            //缓存到一级缓存(分线程)
                            cacheMap.put(imagePath,bitmap);
                            //缓存到二级缓存(分线程)
                            String filePath = context.getFilesDir().getAbsolutePath();
                            String fileName=imagePath.substring(imagePath.lastIndexOf("/")+1);
                            filePath=filePath+"/"+fileName;
							//第一个参数是图片的格式,png,jpeg这些,点进去就有了。第二个参数是压缩,也就是这个图片原本的大小,然后压缩成百分之几,。第三个参数是二级缓存的文件输出流
                            bitmap.compress(Bitmap.CompressFormat.JPEG,100,new FileOutputStream(filePath));
                        }
                    }
                    connection.disconnect();
                } catch (Exception e) {
                    e.printStackTrace();
                }

                return bitmap;
            }

            @Override
            protected void onPostExecute(Bitmap bitmap) {//从联网请求图片到得到图片对象需要哦一定时间,视图可能被复用,不需要显示
                String newImagePath= (String) imageView.getTag();
                if(newImagePath!=imagePath){
                    return;
                }

                if(bitmap==null){
                    //如果没有,显示错误图片
                    imageView.setImageResource(errorImage);
                }else{//如果有,显示
                    imageView.setImageBitmap(bitmap);
                }
            }
        }.execute();
    }

    /*
        根据图片url从二级缓存中取对应的bitmap对象
     */
    private Bitmap getSecondCasche(String imagePath) {
        //  /data/data/packageName/files/
        String filePath = context.getFilesDir().getAbsolutePath();
        String fileName=imagePath.substring(imagePath.lastIndexOf("/")+1);
        filePath=filePath+"/"+fileName;
        return BitmapFactory.decodeFile(filePath);
    }

    /*
        根据图片url从一级缓存中取对应的bitmap对象
     */
    private Bitmap getFristCache(String imagePath) {
        return cacheMap.get(imagePath);
    }

}

在这个事例中,我们在BaseAdapter中调用了这个方法:

 public class ItemBaseAdapter extends BaseAdapter{

        private ImageLoader imageLoader;

        public ItemBaseAdapter(){
            imageLoader=new ImageLoader(MainActivity.this,R.drawable.loading,R.drawable.error);
        }

        @Override
        public int getCount() {
            return data.size();
        }

        @Override
        public Object getItem(int position) {
            return data.get(position);
        }

        @Override
        public long getItemId(int position) {
            return 0;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {

            if(convertView==null){
                convertView=View.inflate(MainActivity.this,R.layout.item_main,null);
            }

            ShopInfo shopInfo=data.get(position);
            ImageView imageView = (ImageView) convertView.findViewById(R.id.iv_item);
            TextView name=(TextView)convertView.findViewById(R.id.name_item);
            TextView price=(TextView)convertView.findViewById(R.id.price_item);

            name.setText(shopInfo.getName());
            price.setText(shopInfo.getPrice()+"元");
            String imagePath = shopInfo.getImagePath();
            //根据匹配路径动态请求服务加载图片并显示

            imageLoader.loadIamge(imagePath,imageView);
            return convertView;
        }
    }
}

服务器Servlet代码(用Json返回相关的数据):

@WebServlet("/ShopInfoServlet")
public class ShopInfoServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        //获取所有商品信息
        List<ShopInfo> list=new ArrayList<ShopInfo>();
        list=getAllShops(request);
        //转换为json字符串
        String s = new Gson().toJson(list);
        System.out.println(s);

        //将数据写向客户端
        response.setContentType("text/json;charset=utf-8");
        response.getWriter().write(s);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }


    /*
        返回所有商品信息对象集合
     */
    private List<ShopInfo> getAllShops(HttpServletRequest request){

        //准备一个空集合
        List<ShopInfo> list=new ArrayList<ShopInfo>();
        //得到image的真实路径
        String imagesPath=getServletContext().getRealPath("/image");
        //创建images文件file对象
        File file=new File(imagesPath);
        //得到images文件下的所有图片文件的file对象数组
        File[] files=file.listFiles();
        for(int i=0;i<files.length;i++){
            int id =i+1;
            //图片名称
            String imageName=files[i].getName();
            //System.out.println(imageName);
            //商品名称
            String name=imageName.substring(0,imageName.lastIndexOf("."))+"商品名称";
            //System.out.println(name);
            //图片路径
            String imagePath="http://"+request.getLocalAddr()+":"+request.getLocalPort()
                    +"/"+request.getContextPath()+"/image/"+imageName;
            System.out.println(imagePath);
            //图片价格
            float price =new Random().nextInt(20)+20;
            ShopInfo info=new ShopInfo(id,name,price,imagePath);
            list.add(info);
        }
        return list;
    }
}

其他代码也就是布局,还有视图,handler发送消息的一些操作。这里就不展示了。
还有就是,我在测试时遇到一个问题,很重点!!!!也就是为什么我虚拟机上运行成功了,但是真机上确不行呢????
在这里插入图片描述

在这里插入图片描述
这一行是这样子的,看起来没什么问题。

最后经过询问老师,原来是:
targetSdkVersion版本的问题,高版本不允许明文http,除非上https,或者配置网络安全
之后我就将targetSdkVersion版本调到了25(老师叫我调到27)
之后,就运行成功了!!!!

发布了117 篇原创文章 · 获赞 1 · 访问量 7057

猜你喜欢

转载自blog.csdn.net/qq_43616001/article/details/104416102