Listview内存泄漏

我有一个简单的列表视图与适配器。 我动态创建了10多个listviewitems。 然后我一次又一次地上下滚动….我可以看到可用的内存不断下降……

我需要在哪里免费? 注意 – 有一个imageview – 但在我的测试中我没有使用任何图像,所以它是View.GONE。

另外 – 我可以使用哪个工具来分析android上的内存使用情况。 我找到了你的Kit,但是如何为android配置它(我在设备上运行应用程序)/

Activity类

package org.BJ.Food4All.Activities.NewRecipe; import org.BJ.Food4All.R; import org.BJ.Food4All.Recipe; import org.BJ.Food4All.Recipe.Instruction; import org.BJ.Food4All.Activities.RecipeBook.RecipeInstructionsListViewAdapter; import org.BJ.Food4All.Activities.RecipeBook.SharedData; import org.BJ.Food4All.utils.CameraUtil; import org.BJ.Food4All.utils.ImageUploadItem; import android.app.ListActivity; import android.content.Intent; import android.graphics.Bitmap; import android.os.Bundle; import android.util.Log; import android.view.ContextMenu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ContextMenu.ContextMenuInfo; import android.view.View.OnClickListener; import android.widget.AdapterView; import android.widget.EditText; public class Instructions extends ListActivity implements OnClickListener { private final static String mTAG = "Instructions"; private EditText mInstructionEditText = null; private RecipeInstructionsListViewAdapter mListViewAdapter = null; private Recipe mEditRecipe = PrivateResources.GetRecipe(); private CameraUtil mCameraUtil = new CameraUtil(this); private int mSelectedEntryIndex = -1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.new_recipe_instruction_tab); mInstructionEditText = (EditText)findViewById(R.id.newRecipeInstructionEditTextId); View addInstructionButton = findViewById(R.id.naddInstructionButtonId); // Sanity check if(mInstructionEditText == null || addInstructionButton == null) { Log.e(mTAG, "NULL pointers"); // secure exit finish(); } // Set up click listeners for all the buttons addInstructionButton.setOnClickListener(this); mListViewAdapter = new RecipeInstructionsListViewAdapter(this, R.layout.recipes_instruction_list_single_view_entry, mEditRecipe.GetInstructions()); setListAdapter(mListViewAdapter); registerForContextMenu(getListView()); } public void onClick(View v) { switch(v.getId()) { case R.id.naddInstructionButtonId: AddInstructionToRecipe(v); break; default: Log.e(mTAG, "Invalid ID:" + v.getId()); // secure exit finish(); } } private void AddInstructionToRecipe(View v) { String instructionText = mInstructionEditText.getText().toString(); if(instructionText == null) { return; } Instruction newInstruction = new Instruction( mEditRecipe.GetInstructions().size() + 1, // Index instructionText, // The instruction null, true); if( mEditRecipe.AddInstruction(newInstruction) != true) { // TODO - ERROR } else { mListViewAdapter.notifyDataSetChanged(); } } /* * (non-Javadoc) * @see android.app.Activity#onCreateContextMenu(android.view.ContextMenu, android.view.View, android.view.ContextMenu.ContextMenuInfo) */ @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.instructions_ctx_menu, menu); super.onCreateContextMenu(menu, v, menuInfo); } /* * (non-Javadoc) * @see android.app.Activity#onContextItemSelected(android.view.MenuItem) */ @Override public boolean onContextItemSelected(MenuItem item) { super.onContextItemSelected(item); AdapterView.AdapterContextMenuInfo menuInfo; menuInfo = (AdapterView.AdapterContextMenuInfo)item.getMenuInfo(); mSelectedEntryIndex = menuInfo.position; switch(item.getItemId()) { case R.id.deleteId: mEditRecipe.RemoveInstruction(mSelectedEntryIndex); mListViewAdapter.notifyDataSetChanged(); return true; case R.id.takePictureId: mCameraUtil.TakePicture(); return true; } return false; } /* * (non-Javadoc) * @see android.app.Activity#onActivityResult(int, int, android.content.Intent) */ @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { // String imageLocation = mCameraUtil.onActivityResult(requestCode, resultCode, data); Bitmap imageBitmap = mCameraUtil.onActivityResult(requestCode, resultCode, data); // TODO - switch to parameter passed in the intent!!!! like TakePicture(index); // mEditRecipe.GetInstructions().get( mSelectedEntryIndex ).SetBitmap( imageBitmap ); //SetInstructionImageLocation(imageLocation); mSelectedEntryIndex = -1; // Update the listviewitem with the picture mListViewAdapter.notifyDataSetChanged(); } } 

适配器类

 package org.BJ.Food4All.Activities.RecipeBook; import java.util.ArrayList; import org.BJ.Food4All.R; import org.BJ.Food4All.Recipe.Instruction; import org.BJ.Food4All.utils.GlobalDefs; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Color; import android.graphics.Typeface; import android.net.Uri; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; import android.widget.ArrayAdapter; import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; public class RecipeInstructionsListViewAdapter extends ArrayAdapter { private Context mContext; private ArrayList mItems; private LayoutInflater mInflater; public RecipeInstructionsListViewAdapter(Context context, int textViewResourceId, ArrayListitems) { super(context, textViewResourceId, items); mContext = context; mItems = items; mInflater = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder = new ViewHolder(); if (convertView == null) { convertView = mInflater.inflate(R.layout.recipes_instruction_list_single_view_entry, null); } if(super.getItem(position) != null) { holder.instructionIndex = (TextView) convertView.findViewById( R.id.listUp_RecipeInstructionNumberTextBoxId); holder.instructionText = (TextView) convertView.findViewById( R.id.listUp_RecipeInstructionTextTextBoxId); holder.instructionImage = (ImageView)convertView.findViewById( R.id.listUp_RecipeInstructionImageViewId); Typeface tf = Typeface.createFromAsset(mContext.getAssets(), "Eras_Bold.ttf"); holder.instructionIndex.setTypeface(tf); holder.instructionIndex.setTextSize(30); holder.instructionIndex.setTextColor(GlobalDefs.GetHeadlineColor()); holder.instructionIndex.setText( Integer.toString(mItems.get(position).getIndex())); tf = Typeface.createFromAsset(mContext.getAssets(), "Arial.ttf"); holder.instructionText.setTypeface(tf); holder.instructionText.setTextSize(14); holder.instructionText.setTextColor(Color.BLACK); holder.instructionText.setText(mItems.get(position).getText()); Bitmap imageBitmap = mItems.get(position).GetBitmap(); // String imageLocation = mItems.get(position).GetInstructionImageLocation(); if(imageBitmap != null) { holder.instructionImage.setImageBitmap(imageBitmap);// setImageURI( Uri.parse(imageLocation )); holder.instructionImage.setVisibility(View.VISIBLE); } else { holder.instructionImage.setVisibility(View.GONE); } convertView.setTag(holder); convertView.setLayoutParams(new ListView.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT)); } else { } return convertView; } @Override public boolean isEnabled(int position) { return true; } static class ViewHolder { TextView instructionIndex; TextView instructionText; ImageView instructionImage; } } 

我不确定将它归类为错误是否正确,但每次使用Typeface.createFromAsset时如果创建新的字体资产并且不释放它。 看到这个 。

您可以做的是在加载应用程序时加载字体并静态引用它们。 我把我的字体放在应用程序中。

 public class YourApp extends android.app.Application { public void onCreate() { super.onCreate(); // typeface caching initializeTypefaces(); } public static class Fonts { public static Typeface THEOREM; } private void initializeTypefaces(){ Fonts.THEOREM = Typeface.createFromAsset(getAssets(), "fonts/theorem.otf"); } } 

然后我在我的适配器中执行此操作:

 textView.setTypeface(YourApp.Fonts.THEOREM); 

您可以到这里查看如何在Android中使用Application。

最后,看起来你每次都在创建ViewHolder,而不是仅在convertView为null时。 我会回顾一下这个video来了解整体情况。 http://www.google.com/events/io/2010/sessions/world-of-listview-android.html

以下是我如何使用ViewHolder方法的示例:

 @Override public View getView(int pos, View convertView, ViewGroup parent) { ViewHolder holder; if(convertView == null || convertView.getTag() == null){ convertView = inflater.inflate(R.layout.list_item, parent, false); holder = new ViewHolder(); holder.text1 = (TextView)convertView.findViewById(R.id.list_item_text1); holder.text2 = (TextView)convertView.findViewById(R.id.list_item_text2); holder.text1.setTypeface(YourApp.Fonts.THEOREM); // only happens once when recycling! convertView.setTag(holder); }else{ holder = (ViewHolder) convertView.getTag(); } holder.text1.setText("someText"); holder.text2.setText("someText"); return convertView; } 

if convertView != null您可以尝试释放资源。 如果它是你可以找到你的Views and null它们Views and null 。 您还可以尝试获取ImageView位图并回收它。 您还可以将ViewHolder作为成员添加到RecipeInstructionsListViewAdapter并在构造函数中实例化一次。