安卓图片的异步请求及使用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);
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。