場景以下:緩存
一、加載的圖片過大 二、一次加載的圖片過多 三、上述兩種狀況兼有
主要緣由:網絡
一、移動設備會限制每一個APP所可以使用的內存,最小爲16M,有的設備分配較多,但總的來講,都會有所限制。 二、在Android中圖片加載到內存中是以位圖的方式存儲的,在Android2.3以後默認狀況下使用ARGB_8888,這種方式下每一個像素要使用4個字節來存儲。加載圖片會佔用大量的內存。
在實際加載圖片的時候,咱們不多加載原始大圖,通常都是按照比例採樣縮放,這樣既節省內存又保證圖片不失真。具體實施步驟以下:異步
這裏須要用的BitmapFactory的decode系列方法和BitmapFactory.Options。當使用decode系列方法加載圖片時,必定要將Options的inJustDecodeBounds屬性設置爲true。ide
BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds=true; BitmapFactory.decodeFile(path, options);
public int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) { // Raw height and width of image final int height = options.outHeight; final int width = options.outWidth; int inSampleSize = 1; if (height > reqHeight || width > reqWidth) { if (width > height) { inSampleSize = Math.round((float) height / (float) reqHeight); } else { inSampleSize = Math.round((float) width / (float) reqWidth); } } return inSampleSize; }
//計算圖片的縮放比例 options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); options.inJustDecodeBounds = false; Bitmap bitmap= BitmapFactory.decodeFile(path, options);
首先要根據界面展現圖片控件的大小來肯定縮放比例,this
public class PhotoWallAdapter extends ArrayAdapter<String> implements OnScrollListener { /** * 記錄全部正在下載或等待下載的任務。 */ private Set<BitmapWorkerTask> taskCollection; /** * 圖片緩存技術的核心類,用於緩存全部下載好的圖片,在程序內存達到設定值時會將最少最近使用的圖片移除掉。 */ private LruCache<String, Bitmap> mMemoryCache; /** * GridView的實例 */ private GridView mPhotoWall; /** * 第一張可見圖片的下標 */ private int mFirstVisibleItem; /** * 一屏有多少張圖片可見 */ private int mVisibleItemCount; /** * 記錄是否剛打開程序,用於解決進入程序不滾動屏幕,不會下載圖片的問題。 */ private boolean isFirstEnter = true; public PhotoWallAdapter(Context context, int textViewResourceId, String[] objects, GridView photoWall) { super(context, textViewResourceId, objects); mPhotoWall = photoWall; 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(); } }; mPhotoWall.setOnScrollListener(this); } @Override public View getView(int position, View convertView, ViewGroup parent) { final String url = getItem(position); View view; if (convertView == null) { view = LayoutInflater.from(getContext()).inflate(R.layout.photo_layout, null); } else { view = convertView; } final ImageView photo = (ImageView) view.findViewById(R.id.photo); // 給ImageView設置一個Tag,保證異步加載圖片時不會亂序 photo.setTag(url); setImageView(url, photo); return view; } /** * 給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 { imageView.setImageResource(R.drawable.empty_photo); } } /** * 將一張圖片存儲到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); } @Override public void onScrollStateChanged(AbsListView view, int scrollState) { // 僅當GridView靜止時纔去下載圖片,GridView滑動時取消全部正在下載的任務 if (scrollState == SCROLL_STATE_IDLE) { loadBitmaps(mFirstVisibleItem, mVisibleItemCount); } else { cancelAllTasks(); } } @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; } } /** * 加載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++) { String imageUrl = Images.imageThumbUrls[i]; Bitmap bitmap = getBitmapFromMemoryCache(imageUrl); if (bitmap == null) { BitmapWorkerTask task = new BitmapWorkerTask(); taskCollection.add(task); task.execute(imageUrl); } else { ImageView imageView = (ImageView) mPhotoWall.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 guolin */ 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); } return bitmap; } @Override protected void onPostExecute(Bitmap bitmap) { super.onPostExecute(bitmap); // 根據Tag找到相應的ImageView控件,將下載好的圖片顯示出來。 ImageView imageView = (ImageView) mPhotoWall.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; } } }
因爲咱們使用了LruCache來緩存圖片,因此不須要擔憂內存溢出的狀況,當LruCache中存儲圖片的總大小達到容量上限的時候,會自動把最近最少使用的圖片從緩存中移除。url