剛開始接觸android開發的時候 經常會碰到一個問題 就是 listview 裏的圖片太多 會導致 listview 的OutOfMemoryException發生,
而網上卻沒有很詳細的解決方案,只有例如 軟引用 ,手動recycle 資源,縮小bitmap等等。(不過貌似都治標不治本,所以以前這個問題 一直困擾了我很久。。。)
即使使用了這些解決方案 也很可能碰到 以下的幾個問題
1. 圖片 比如 bitmap 或者 drawable 雖然可以用recycle 方法手動釋放,但是 釋放的時機。
2. 即使使用手動釋放,但由於 圖片被 imageview 或者其他控件引用 導致發生異常 比如 trying to use a recycled bitmap。
我比較傻瓜的解決方案是
testmemoryadapter.java
- 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 TestAdapterextends 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);
- }
- }
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);
}
}
- <?xmlversion="1.0"encoding="utf-8"?>
- <LinearLayoutxmlns: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>
<?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>
- <?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>
<?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>
- package com.testmemoryadapter;
- import java.util.ArrayList;
- import android.app.Activity;
- import android.os.Bundle;
- import android.widget.ListView;
- public class MainActivityextends 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);
- }
- }
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。希望能給被這個問題困擾的朋友們提供些思路。