安卓图片的异步请求及使用LruCache缓存和手机内存两层存储图片,避免重新加载页面带来的重新请求

看到网友的一片技术博客讲解了LruCache的使用,我把它加到了我的项目中,但是加入断点发现,列表上下滑动时,确实可以不用重新加载图片,但是重新打开这个activity或者重新启动应用,LruCache的缓存都不再存在,而需要重新联网下载,所有我对这个方法加以改进,加入了一层往手机内存存储图片的过程。

这样的话,使用图片时,先从LruCache中加载,如果LruCache中不存在该图片资源的话,再从手机存储中进行加载,如果同样不存在,则先显示一个默认图片。

另一方面,我的项目使用图片的是listview,在它滑动的时候,不进行请求图片的操作,以免浪费不必要的流量,在它不滑动的时候,会检查哪些图片没有在手机存储中,进行异步请求,并将返回的bitmap同时存储在LruCache和手机存储中。

下面是关键代码,其中有一些是我项目中的变量,可以忽略:

public class TaocanListAdapter extends BaseAdapter implements OnScrollListener{
		 /**
		 * 记录所有正在下载或等待下载的任务。
		 */
	    private Set<BitmapWorkerTask> taskCollection;  

	    /**
	     * 图片缓存技术的核心类,用于缓存所有下载好的图片,在程序内存达到设定值时会将最少最近使用的图片移除掉。
	     */
	    private LruCache<String, Bitmap> mMemoryCache;    

	    /**
	     * 第一张可见图片的下标
	     */
	    private int mFirstVisibleItem;  

	    /**
	     * 一屏有多少张图片可见
	     */
	    private int mVisibleItemCount;  

	    /**
	     * 记录是否刚打开程序,用于解决进入程序不滚动屏幕,不会下载图片的问题。
	     */
	    private boolean isFirstEnter = true;  

		final OnCheckedChangeListener checkedChangeListener = new OnCheckedChangeListener() {

			@Override
			public void onCheckedChanged(CompoundButton btn, boolean value) {
				if(value){
					if(yixuanNum < xuanXiangNum){
						//值有改变则设为1
						btn.setTag(R.id.tag_check, 1);
						yixuanNum += 1;
						checkedTaocanIdList.add((String)btn.getTag(R.id.tag_dataid));
					}else{
						Toast.makeText(TaoCanActivity.this, "最多能选" + xuanXiangNum + "个", Toast.LENGTH_SHORT).show();
						btn.setChecked(false);
					}
				}else{
					checkedTaocanIdList.remove((String)btn.getTag(R.id.tag_dataid));
					yixuanNum -= 1;
				}

			}
		};

		public TaocanListAdapter(){
			super();
			taskCollection = new HashSet<BitmapWorkerTask>();
	        // 获取应用程序最大可用内存
	        int maxMemory = (int) Runtime.getRuntime().maxMemory();
	        int cacheSize = maxMemory / 8;
	        // 设置图片缓存大小为程序最大可用内存的1/8
	        mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
	            @Override
	            protected int sizeOf(String key, Bitmap bitmap) {
	                return bitmap.getByteCount();
	            }
	        };
	        taocanListView.setOnScrollListener(this);
		}

		@Override
		public View getView(int position, View convertView, ViewGroup arg2) {
			ViewHolder holder = null;
			if(convertView == null){
				convertView = LayoutInflater.from(getApplicationContext()).inflate(R.layout.taocan_list_item, null);
				holder = new ViewHolder();
				holder.taocan_caiImg = (ImageView)convertView.findViewById(R.id.taocan_caiImg);
				holder.taocan_caiming_text = (TextView)convertView.findViewById(R.id.taocan_caiming_text);
				holder.taocan_caiming_jiage = (TextView)convertView.findViewById(R.id.taocan_caiming_jiage);
				holder.taocan_tianjia_btn = (CheckBox)convertView.findViewById(R.id.taocan_tianjia_btn);
				convertView.setTag(holder);
			}
			holder = (ViewHolder)convertView.getTag();
			Taocan taocan = taocanList.get(position);
			holder.taocan_caiming_text.setText(taocan.getFoodCnName());
			holder.taocan_caiming_jiage.setText(taocan.getPrice() + " 元/" + taocan.getUnit());
			holder.taocan_tianjia_btn.setTag(R.id.tag_dataid, taocan.getFoodId());
			if(position < xuanXiangNum && holder.taocan_tianjia_btn.getTag(R.id.tag_check) == null){
				holder.taocan_tianjia_btn.setChecked(true);
				yixuanNum += 1;
				checkedTaocanIdList.add(taocan.getFoodId());
			}
			holder.taocan_tianjia_btn.setOnCheckedChangeListener(checkedChangeListener);

			if(taocan.getPhotoPath() != null && !taocan.getPhotoPath().equals("")){
				String imgurl = globalVariables.getEdnpointOut() + "/food/" + taocan.getPhotoPath();
				holder.taocan_caiImg.setTag(imgurl);
				setImageView(imgurl, holder.taocan_caiImg);
			}else{
				holder.taocan_caiImg.setImageDrawable(getResources().getDrawable(R.drawable.cai));
			}
			return convertView;
		}

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

		@Override
		public Object getItem(int arg0) {
			return null;
		}

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

	    @Override
	    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
	            int totalItemCount) {
	        mFirstVisibleItem = firstVisibleItem;
	        mVisibleItemCount = visibleItemCount;
	        // 下载的任务应该由onScrollStateChanged里调用,但首次进入程序时onScrollStateChanged并不会调用,
	        // 因此在这里为首次进入程序开启下载任务。
	        if (isFirstEnter && visibleItemCount > 0) {
	            loadBitmaps(firstVisibleItem, visibleItemCount);
	            isFirstEnter = false;
	        }
	    }  

	    @Override
	    public void onScrollStateChanged(AbsListView view, int scrollState) {
	        // 仅当GridView静止时才去下载图片,GridView滑动时取消所有正在下载的任务
	        if (scrollState == SCROLL_STATE_IDLE) {
	            loadBitmaps(mFirstVisibleItem, mVisibleItemCount);
	        } else {
	            cancelAllTasks();
	        }
	    } 

		 /**
	     * 给ImageView设置图片。首先从LruCache中取出图片的缓存,设置到ImageView上。如果LruCache中没有该图片的缓存,
	     * 就给ImageView设置一张默认图片。
	     *
	     * @param imageUrl
	     *            图片的URL地址,用于作为LruCache的键。
	     * @param imageView
	     *            用于显示图片的控件。
	     */
	    private void setImageView(String imageUrl, ImageView imageView) {
	        Bitmap bitmap = getBitmapFromMemoryCache(imageUrl);
	        if (bitmap != null) {
	            imageView.setImageBitmap(bitmap);
	        } else {
	        	/**
	             * 对本地缓存的查找
	             */
	        	Bitmap bitmap1 = findImgFromStorage(imageUrl);
	        	imageView.setImageBitmap(bitmap1);
	            if(bitmap1 == null){
	            	imageView.setImageResource(R.drawable.cai);
	            }
	        }
	    }  

	    /**
         * 对本地缓存的查找
         */
	    public Bitmap findImgFromStorage(String imageUrl) {
			 String bitmapName = imageUrl.substring(imageUrl.lastIndexOf("/") + 1);
	         String path = Environment.getExternalStorageDirectory().getPath();
	         File cacheDir = new File(path + "/mkb/");
	         File[] cacheFiles = cacheDir.listFiles();
	         int i = 0;
	         if(null != cacheFiles){
	            for(; i<cacheFiles.length; i++)  {
	                if(bitmapName.equals(cacheFiles[i].getName())) {
	                    break;
	                }
	            }
	            if(i < cacheFiles.length) {
	            	return BitmapFactory.decodeFile(path + "/mkb/" + bitmapName);  

	            }
	         }
			return null;
		}

		/**
	     * 将一张图片存储到LruCache中。
	     *
	     * @param key
	     *            LruCache的键,这里传入图片的URL地址。
	     * @param bitmap
	     *            LruCache的键,这里传入从网络上下载的Bitmap对象。
	     */
	    public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
	        if (getBitmapFromMemoryCache(key) == null) {
	            mMemoryCache.put(key, bitmap);
	        }
	    }  

		/**
	     * 从LruCache中获取一张图片,如果不存在就返回null。
	     *
	     * @param key
	     *            LruCache的键,这里传入图片的URL地址。
	     * @return 对应传入键的Bitmap对象,或者null。
	     */
	    public Bitmap getBitmapFromMemoryCache(String key) {
	        return mMemoryCache.get(key);
	    }  

		/**
	     * 加载Bitmap对象。此方法会在LruCache中检查所有屏幕中可见的ImageView的Bitmap对象,
	     * 如果发现任何一个ImageView的Bitmap对象不在缓存中,就会开启异步线程去下载图片。
	     *
	     * @param firstVisibleItem
	     *            第一个可见的ImageView的下标
	     * @param visibleItemCount
	     *            屏幕中总共可见的元素数
	     */
	    private void loadBitmaps(int firstVisibleItem, int visibleItemCount) {
	        try {
	            for (int i = firstVisibleItem; i < firstVisibleItem + visibleItemCount; i++) {
	            	Taocan taocan = taocanList.get(i);
	            	if(taocan.getPhotoPath() != null && !taocan.getPhotoPath().equals("")){
	            		Bitmap bitmap1 = findImgFromStorage(taocan.getPhotoPath());
	            		//如果内存中不存在则重新请求
	    	            if(bitmap1 == null){
	    	            	String imageUrl = globalVariables.getEdnpointOut() + "/food/" + taocan.getPhotoPath();
	    	                Bitmap bitmap = getBitmapFromMemoryCache(imageUrl);
	    	                if (bitmap == null) {
	    	                    BitmapWorkerTask task = new BitmapWorkerTask();
	    	                    taskCollection.add(task);
	    	                    task.execute(imageUrl);
	    	                } else {
	    	                    ImageView imageView = (ImageView) taocanListView.findViewWithTag(imageUrl);
	    	                    if (imageView != null && bitmap != null) {
	    	                        imageView.setImageBitmap(bitmap);
	    	                    }
	    	                }
	    	            }
	    	        }

	            }
	        } catch (Exception e) {
	            e.printStackTrace();
	        }
	    }  

		 /**
	     * 取消所有正在下载或等待下载的任务。
	     */
	    public void cancelAllTasks() {
	        if (taskCollection != null) {
	            for (BitmapWorkerTask task : taskCollection) {
	                task.cancel(false);
	            }
	        }
	    }  

		/**
	     * 异步下载图片的任务。
	     *
	     * @author zhangda
	     */
	    class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap> {  

	        /**
	         * 图片的URL地址
	         */
	        private String imageUrl;  

	        @Override
	        protected Bitmap doInBackground(String... params) {
	            imageUrl = params[0];
	            // 在后台开始下载图片
	            Bitmap bitmap = downloadBitmap(params[0]);
	            if (bitmap != null) {
	                // 图片下载完成后缓存到LrcCache中
	                addBitmapToMemoryCache(params[0], bitmap);
	            }  

	          //存储至手机内存中
	            String path = Environment.getExternalStorageDirectory().getPath();
	            File dir = new File(path + "/mkb/");
                if(!dir.exists())
                {
                    dir.mkdirs();
                }    

                File bitmapFile = new File(path + "/mkb/" + imageUrl.substring(imageUrl.lastIndexOf("/") + 1));
                if(!bitmapFile.exists())
                {
                    try {
                        bitmapFile.createNewFile();
                    }
                    catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                FileOutputStream fos;
                try
                {
                    fos = new FileOutputStream(bitmapFile);
                    bitmap.compress(Bitmap.CompressFormat.PNG,
                            100, fos);
                    fos.close();
                }
                catch (FileNotFoundException e)
                {
                    e.printStackTrace();
                }
                catch (IOException e)
                {
                    e.printStackTrace();
                }

	            return bitmap;
	        }  

	        @Override
	        protected void onPostExecute(Bitmap bitmap) {
	            super.onPostExecute(bitmap);
	            // 根据Tag找到相应的ImageView控件,将下载好的图片显示出来。
	            ImageView imageView = (ImageView) taocanListView.findViewWithTag(imageUrl);
	            if (imageView != null && bitmap != null) {
	                imageView.setImageBitmap(bitmap);
	            }
	            taskCollection.remove(this);
	        }  

	        /**
	         * 建立HTTP请求,并获取Bitmap对象。
	         *
	         * @param imageUrl
	         *            图片的URL地址
	         * @return 解析后的Bitmap对象
	         */
	        private Bitmap downloadBitmap(String imageUrl) {
	            Bitmap bitmap = null;
	            HttpURLConnection con = null;
	            try {
	                URL url = new URL(imageUrl);
	                con = (HttpURLConnection) url.openConnection();
	                con.setConnectTimeout(5 * 1000);
	                con.setReadTimeout(10 * 1000);
	                bitmap = BitmapFactory.decodeStream(con.getInputStream());
	            } catch (Exception e) {
	                e.printStackTrace();
	            } finally {
	                if (con != null) {
	                    con.disconnect();
	                }
	            }
	            return bitmap;
	        }  

	    }  

	    private class ViewHolder{
			private ImageView taocan_caiImg;
			private TextView taocan_caiming_text;
			private TextView taocan_caiming_jiage;
			private CheckBox taocan_tianjia_btn;
		}
	}
adapter = new TaocanListAdapter();
taocanListView.setAdapter(adapter);
时间: 2024-10-24 01:31:07

安卓图片的异步请求及使用LruCache缓存和手机内存两层存储图片,避免重新加载页面带来的重新请求的相关文章

artdialog 异步加载页面 生成验证码

artdialog  异步加载一个页面 需求:例如现在好多网站的登录或注册 都是点击弹出一个层出来 然后在上面登录.注册 这个登录可能在网站的每个页面都会有,但是我们又不能在每个页面都这一段html加载出来不显示,到需要用的时候,在给shou出来,这样做于情于理都说!不!!过!!!去!!!!!! 恰好以前接触过artdialog  不多说上代码,(注意思维,代码是死的方法是活,解决需求不一定非要这个方法 ) 1.页面html代码 1 <head runat="server">

无法加载一个或多个请求的类型。有关更多信息,请检索 LoaderExceptions 属性。

新建一个MVC4的项目,引用DAL后,将DAL的连接字符串考入: <connectionStrings>     <add name="brnmallEntities" connectionString="metadata=res://*/BrnMall.csdl|res://*/BrnMall.ssdl|res://*/BrnMall.msl;provider=System.Data.SqlClient;provider connection string

wpf prism4 出现问题:无法加载一个或多个请求的类型。有关更多信息,请检索 LoaderExceptions 属性。

WPF Prism 框架 程序 出现 问题: 无法加载一个或多个请求的类型.有关更多信息,请检索 LoaderExceptions 属性. 1.开始以为是配置的问题,找了半天,最后原来是有个依赖类库没有引用. 错误如图: 2.最后找到这个,原来是有个依赖模块问题: 如图: 哎呀,恍然大悟,主程序 引用System.Windows.Interactivity.dll类库,问题解决!

“无法加载一个或多个请求的类型。有关更多信息,请检索 LoaderExceptions 属性 “之解决

今天在学习插件系统设计的时候遇到一个问题:“System.Reflection.ReflectionTypeLoadException: 无法加载一个或多个请求的类型. 于是百度一下,很多内容都差不多,摘抄一个: ------------------------------ 今天突然遇到“System.Reflection.ReflectionTypeLoadException: 无法加载一个或多个请求的类型.有关更多信息,请检索 LoaderExceptions 属性.”这样的报错,找了好长时

0803THINKPHP基础:加载页面、请求、常用类、常用方法、获取变量

类.方法.函数========================================================== 一.加载页面 1.系统函数 return view(); 2.系统View类 $view = new View(); return $view->fetch(); 3.通过Controller类 需要控制器继承系统类 return $this->fetch(); 二.获取请求类 1.系统函数 $request = request(); 2.系统Request类 u

爬虫再探实战(四)———爬取动态加载页面——请求json

还是上次的那个网站,就是它.现在尝试用另一种办法——直接请求json文件,来获取要抓取的信息. 第一步,检查元素,看图如下: 过滤出JS文件,并找出包含要抓取信息的js文件,之后就是构造requests请求对象,然后解析json文件啦.源码如下: import requests def save(school_datas): for data in school_datas: # print(data) year = data['year'] province = data['province'

使用JS,使得加载页面之前显示GIF图片

下面是代码,这是伪加载提示条,跟加载速度无关 <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title></title> <style type="text/css"> .current a { font-size: 20px; } .over { display: block; position: abs

使用Ajax异步加载页面时,怎样调试该页面的Js

前言-本人不是干前端的,所以有的名词不专业 在前端中,有时候会遇到这样的框架,http://172.17.11.151:8060/frontend/backend.html#1.html (通过解析URL,把1.html异步加载到指定位置),不知道这样的框架叫什么名字. 那我们就会遇到一个问题,调试1.html的时候怎么办,通过浏览器掉出控制台找不到1.Html页面,无法加断点啊. 解决办法 在1.html 你所要调试位置加上一句代码  debugger;  就可以了,就是这么爽 可以调试了,可

layer iframe加载单个图片或者加载页面

加载单个图片 layer.open({ type: 1, title: false, closeBtn: 0, area: '150px', skin: 'layui-layer-nobg', //没有背景色 shadeClose: true, content: imgurl }); 加载单个页面 layer.open({ type: 2, title: phone, shadeClose: true, shade: 0.8, area: ['180px', '210px'], content: