看到網友的一片技術博客講解了LruCache的使用,我把它加到了個人項目中,可是加入斷點發現,列表上下滑動時,確實能夠不用從新加載圖片,可是從新打開這個activity或者從新啓動應用,LruCache的緩存都再也不存在,而須要從新聯網下載,全部我對這個方法加以改進,加入了一層往手機內存存儲圖片的過程。 java
這樣的話,使用圖片時,先從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);