關於加載圖片避免出現內存溢出的問題

 



剛開始接觸android開發的時候 經常會碰到一個問題 就是 listview 裏的圖片太多 會導致 listview 的OutOfMemoryException發生,

而網上卻沒有很詳細的解決方案,只有例如 軟引用 ,手動recycle 資源,縮小bitmap等等。(不過貌似都治標不治本,所以以前這個問題 一直困擾了我很久。。。)


即使使用了這些解決方案 也很可能碰到 以下的幾個問題


1. 圖片 比如 bitmap 或者 drawable 雖然可以用recycle 方法手動釋放,但是 釋放的時機。

2. 即使使用手動釋放,但由於 圖片被 imageview 或者其他控件引用 導致發生異常 比如 trying to use a recycled bitmap



我比較傻瓜的解決方案是


testmemoryadapter.java

  1. package com.testmemoryadapter; 
  2.  
  3. import java.util.ArrayList; 
  4.  
  5. import android.content.Context; 
  6. import android.graphics.Bitmap; 
  7. import android.graphics.BitmapFactory; 
  8. import android.util.Log; 
  9. import android.view.LayoutInflater; 
  10. import android.view.View; 
  11. import android.view.ViewGroup; 
  12. import android.widget.BaseAdapter; 
  13. import android.widget.ImageView; 
  14.  
  15. public class TestAdapterextends BaseAdapter { 
  16.  
  17.     private ArrayList list; 
  18.     private LayoutInflater mInflater; 
  19.     private Context context; 
  20.     //這個用來保存 imageview 的引用 
  21.     private ArrayList viewList =new ArrayList(); 
  22.     //這個用來 保存 bitmap 
  23.     private ArrayList bitmapList =new ArrayList(); 
  24.  
  25.     @Override 
  26.     public int getCount() { 
  27.         // TODO Auto-generated method stub 
  28.         return list.size(); 
  29.     } 
  30.  
  31.     @Override 
  32.     public Object getItem(int arg0) { 
  33.         // TODO Auto-generated method stub 
  34.         return null
  35.     } 
  36.  
  37.     @Override 
  38.     public long getItemId(int arg0) { 
  39.         // TODO Auto-generated method stub 
  40.         return 0
  41.     } 
  42.  
  43.     public TestAdapter(Context context, ArrayList list) { 
  44.         super(); 
  45.         this.context = context; 
  46.         this.mInflater = LayoutInflater.from(context); 
  47.         this.list = list; 
  48.     } 
  49.  
  50.     @Override 
  51.     public View getView(int position, View convertView, ViewGroup arg2) { 
  52.         // TODO Auto-generated method stub 
  53.  
  54.         convertView = mInflater.inflate(R.layout.test_list_row, null); 
  55.  
  56.         ImageView iv = (ImageView) convertView.findViewById(R.id.imageView); 
  57.          
  58.          
  59.         //用try catch 塊包圍住 
  60.         try
  61.             setImage(iv); 
  62.         } catch (OutOfMemoryError e) { 
  63.             // 這裏就是當內存泄露時 需要做的事情 
  64.             e.printStackTrace(); 
  65.  
  66.             Log.d("memory", "out"); 
  67.              
  68.             //釋放內存資源 
  69.             recycleMemory(); 
  70.              
  71.             //將剛纔 發生異常沒有執行的 代碼 再重新執行一次 
  72.             setImage(iv); 
  73.  
  74.         } 
  75.  
  76.         return convertView; 
  77.     } 
  78.  
  79.      
  80.     //這裏是關鍵 
  81.     private void recycleMemory() { 
  82.         //一屏顯示多少行 這裏就設置爲多少。不設也行 主要是用戶體驗好 不會將用戶看到的圖片設爲默認圖片 
  83.         int showCount = 10
  84.          
  85.         // 
  86.         for (int i =0; i < viewList.size()-showCount; i++) { 
  87.             ImageView iv = (ImageView) viewList.get(i); 
  88.             /***
  89.              *  這裏是關鍵! 將 imageview 設置一張默認的圖片 ,
  90.              *  用於解決當釋放bitmap的時候 還有其他 控件對他保持引用
  91.              *  就不會發生trying to use a recycled bitmap異常了
  92.              */ 
  93.             iv.setImageResource(R.drawable.default_cover); 
  94.             //從list中去除 
  95.             viewList.remove(i); 
  96.         } 
  97.  
  98. //      viewList = new ArrayList(); 
  99.  
  100.         for (int i =0; i < bitmapList.size()-10; i++) { 
  101.  
  102.             Bitmap bitmap = (Bitmap) bitmapList.get(i); 
  103.             //這裏就開始釋放bitmap 所佔的內存了 
  104.             if (!bitmap.isRecycled()) { 
  105.                 bitmap.recycle(); 
  106.                 System.out.println("recycle "); 
  107.             } 
  108.             //從list中去除 
  109.             bitmapList.remove(i); 
  110.         } 
  111.  
  112. //      bitmapList = new ArrayList(); 
  113.     } 
  114.      
  115.     private void setImage(ImageView iv){ 
  116.         /***
  117.          * 從sdcard獲取 圖片  這張圖片 只要不超過  android對於圖片大小的限制即可
  118.          * 我用了 一張比較大的圖片 也通過測試
  119.          */ 
  120.         Bitmap bitmap = BitmapFactory.decodeFile("/sdcard/test/1.jpg"); 
  121.  
  122.         iv.setImageBitmap(bitmap); 
  123.          
  124.         //將這個控件 添加到 list裏 
  125.         viewList.add(iv); 
  126.         //將要 釋放的 bitmap也添加到list裏 
  127.         bitmapList.add(bitmap); 
  128.     } 
  129.  
package com.testmemoryadapter;

import java.util.ArrayList;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;

public class TestAdapter extends BaseAdapter {

	private ArrayList list;
	private LayoutInflater mInflater;
	private Context context;
	//這個用來保存 imageview 的引用
	private ArrayList viewList = new ArrayList();
	//這個用來 保存 bitmap
	private ArrayList bitmapList = new ArrayList();

	@Override
	public int getCount() {
		// TODO Auto-generated method stub
		return list.size();
	}

	@Override
	public Object getItem(int arg0) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public long getItemId(int arg0) {
		// TODO Auto-generated method stub
		return 0;
	}

	public TestAdapter(Context context, ArrayList list) {
		super();
		this.context = context;
		this.mInflater = LayoutInflater.from(context);
		this.list = list;
	}

	@Override
	public View getView(int position, View convertView, ViewGroup arg2) {
		// TODO Auto-generated method stub

		convertView = mInflater.inflate(R.layout.test_list_row, null);

		ImageView iv = (ImageView) convertView.findViewById(R.id.imageView);
		
		
		//用try catch 塊包圍住
		try {
			setImage(iv);
		} catch (OutOfMemoryError e) {
			// 這裏就是當內存泄露時 需要做的事情
			e.printStackTrace();

			Log.d("memory", "out");
			
			//釋放內存資源
			recycleMemory();
			
			//將剛纔 發生異常沒有執行的 代碼 再重新執行一次
			setImage(iv);

		}

		return convertView;
	}

	
	//這裏是關鍵
	private void recycleMemory() {
		//一屏顯示多少行 這裏就設置爲多少。不設也行 主要是用戶體驗好 不會將用戶看到的圖片設爲默認圖片
		int showCount = 10;
		
		//
		for (int i = 0; i < viewList.size()-showCount; i++) {
			ImageView iv = (ImageView) viewList.get(i);
			/***
			 *  這裏是關鍵! 將 imageview 設置一張默認的圖片 ,
			 *  用於解決當釋放bitmap的時候 還有其他 控件對他保持引用
			 *  就不會發生trying to use a recycled bitmap異常了
			 */
			iv.setImageResource(R.drawable.default_cover);
			//從list中去除
			viewList.remove(i);
		}

//		viewList = new ArrayList();

		for (int i = 0; i < bitmapList.size()-10; i++) {

			Bitmap bitmap = (Bitmap) bitmapList.get(i);
			//這裏就開始釋放bitmap 所佔的內存了
			if (!bitmap.isRecycled()) {
				bitmap.recycle();
				System.out.println("recycle ");
			}
			//從list中去除
			bitmapList.remove(i);
		}

//		bitmapList = new ArrayList();
	}
	
	private void setImage(ImageView iv){
		/***
		 * 從sdcard獲取 圖片  這張圖片 只要不超過  android對於圖片大小的限制即可 
		 * 我用了 一張比較大的圖片 也通過測試
		 */
		Bitmap bitmap = BitmapFactory.decodeFile("/sdcard/test/1.jpg");

		iv.setImageBitmap(bitmap);
		
		//將這個控件 添加到 list裏
		viewList.add(iv);
		//將要 釋放的 bitmap也添加到list裏
		bitmapList.add(bitmap);
	}

}

  1. <?xmlversion="1.0"encoding="utf-8"?> 
  2. <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android" 
  3.     android:layout_width="match_parent" 
  4.     android:layout_height="match_parent" 
  5.     android:orientation="vertical"> 
  6.  
  7.     <ImageView 
  8.         android:id="@+id/imageView" 
  9.         android:layout_width="80dip" 
  10.         android:layout_height="80dip"android:src="@drawable/default_cover"/> 
  11.  
  12. </LinearLayout> 
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="80dip"
        android:layout_height="80dip" android:src="@drawable/default_cover"/>

</LinearLayout>

  1. <?xml version="1.0" encoding="utf-8"?> 
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
  3.     android:layout_width="fill_parent" 
  4.     android:layout_height="fill_parent" 
  5.     android:orientation="vertical"
  6.  
  7.     <TextView 
  8.         android:layout_width="fill_parent" 
  9.         android:layout_height="wrap_content" 
  10.         android:text="@string/hello" /> 
  11.  
  12.     <ListView 
  13.         android:id="@+id/testListView" 
  14.         android:layout_width="fill_parent" 
  15.         android:layout_height="fill_parent"
  16.     </ListView> 
  17.  
  18. </LinearLayout> 
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/hello" />

    <ListView
        android:id="@+id/testListView"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" >
    </ListView>

</LinearLayout>

  1. package com.testmemoryadapter; 
  2.  
  3. import java.util.ArrayList; 
  4.  
  5. import android.app.Activity; 
  6. import android.os.Bundle; 
  7. import android.widget.ListView; 
  8.  
  9. public class MainActivityextends Activity { 
  10.     /** Called when the activity is first created. */ 
  11.     @Override 
  12.     public void onCreate(Bundle savedInstanceState) { 
  13.         super.onCreate(savedInstanceState); 
  14.         setContentView(R.layout.main); 
  15.         ArrayList testList = new ArrayList(); 
  16.          
  17.         for (int i =0; i < 30; i++) { 
  18.             testList.add(0); 
  19.         } 
  20.          
  21.         TestAdapter ta = new TestAdapter(this,testList); 
  22.          
  23.         ListView lv = (ListView) findViewById(R.id.testListView); 
  24.          
  25.         lv.setAdapter(ta); 
  26.          
  27.     } 
package com.testmemoryadapter;

import java.util.ArrayList;

import android.app.Activity;
import android.os.Bundle;
import android.widget.ListView;

public class MainActivity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        ArrayList testList = new ArrayList();
        
        for (int i = 0; i < 30; i++) {
			testList.add(0);
		}
        
        TestAdapter ta = new TestAdapter(this,testList);
        
        ListView lv = (ListView) findViewById(R.id.testListView);
        
        lv.setAdapter(ta);
        
    }
}


接下來放心大膽的測試吧嗎哈哈 ,這個解決方案 雖然並不是很規範 但是基本能解決 內存溢出的問題。我用了500k左右的圖片 測試下沒問題 還有我的運行版本是2.2。希望能給被這個問題困擾的朋友們提供些思路。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章