列表视图内存泄漏 [英] Listview memory leak

查看:124
本文介绍了列表视图内存泄漏的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

我在哪里需要释放什么? 注意 - 有一个ImageView的 - 但在我的测试中,我没有使用任何图像,以便它View.GONE

另外 - 与工具,我可以分析在Android的内存使用情况。我发现yourKit,但我怎么配置为Android(我在设备上运行应用程序)/

Activity类

 包org.BJ.Food4All.Activities.NewRecipe;


进口org.BJ.Food4All.R;
进口org.BJ.Food4All.Recipe;
进口org.BJ.Food4All.Recipe.Instruction;
进口org.BJ.Food4All.Activities.RecipeBook.RecipeInstructionsListViewAdapter;
进口org.BJ.Food4All.Activities.RecipeBook.SharedData;
进口org.BJ.Food4All.utils.CameraUtil;
进口org.BJ.Food4All.utils.ImageUploadItem;

进口android.app.ListActivity;
进口android.content.Intent;
进口android.graphics.Bitmap;
进口android.os.Bundle;
进口android.util.Log;
进口android.view.ContextMenu;
进口android.view.MenuInflater;
进口android.view.MenuItem;
进口android.view.View;
进口android.view.ContextMenu.ContextMenuInfo;
进口android.view.View.OnClickListener;
进口android.widget.AdapterView;
进口android.widget.EditText;

公共类指令扩展ListActivity实现OnClickListener
{
    私人最终静态字符串MTAG =说明;
    私人的EditText mInstructionEditText = NULL;
    私人RecipeInstructionsListViewAdapter mListViewAdapter = NULL;
    私人配方mEditRecipe = PrivateResources.GetRecipe();

    私人CameraUtil mCameraUtil =新CameraUtil(本);

    私人诠释mSelectedEntryIndex = -1;

    @覆盖
    保护无效的onCreate(包savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        的setContentView(R.layout.new_recipe_instruction_tab);

        mInstructionEditText =(EditText上)findViewById(R.id.newRecipeInstructionEditTextId);
        查看addInstructionButton = findViewById(R.id.naddInstructionButtonId);

        // 完整性检查
        如果(mInstructionEditText == NULL || addInstructionButton == NULL)
        {
            Log.e(MTAG,空指针);
            //安全退出
            完();
        }

        //设置点击监听所有的按钮
        addInstructionButton.setOnClickListener(本);

        mListViewAdapter =新RecipeInstructionsListViewAdapter(这一点,R.layout.recipes_instruction_list_single_view_entry,mEditRecipe.GetInstructions());

        setListAdapter(mListViewAdapter);

        registerForContextMenu(getListView());
    }

    公共无效的onClick(视图v)
    {
        开关(v.getId())
        {
            案例R.id.naddInstructionButtonId:
                AddInstructionToRecipe(五);
                打破;

            默认:
                Log.e(MTAG,无效的编号:+ v.getId());
                //安全退出
                完();

        }
    }

    私人无效AddInstructionToRecipe(视图v)
    {
        字符串instructionText = mInstructionEditText.getText()的toString()。

        如果(instructionText == NULL)
        {
            返回;
        }

        指令newInstruction =新指令(mEditRecipe.GetInstructions()。大小()+ 1,//指数
                                                        instructionText,//指令
                                                        空值,
                                                        真正);

        如果(mEditRecipe.AddInstruction(newInstruction)!=真)
        {
            // TODO  - 错误
        }
        其他
        {
            mListViewAdapter.notifyDataSetChanged();
        }
    }

    / *
     *(非Javadoc中)
     * @see android.app.Activity#onCreateContextMenu(android.view.ContextMenu,android.view.View,android.view.ContextMenu.ContextMenuInfo)
     * /
    @覆盖
    公共无效onCreateContextMenu(文本菜单菜单,
                                    视图V,
                                    ContextMenuInfo menuInfo)
    {
        MenuInflater充气= getMenuInflater();
        inflater.inflate(R.menu.instructions_ctx_menu,菜单);

        super.onCreateContextMenu(菜单,V,menuInfo);
    }

    / *
     *(非Javadoc中)
     * @see android.app.Activity#onContextItemSelected(android.view.MenuItem)
     * /
    @覆盖
    公共布尔onContextItemSelected(菜单项项)
    {
        super.onContextItemSelected(项目);

        AdapterView.AdapterContextMenuInfo menuInfo;
        menuInfo =(AdapterView.AdapterContextMenuInfo)item.getMenuInfo();
        mSelectedEntryIndex = menuInfo.position;

        开关(item.getItemId())
        {
            案例R.id.deleteId:
                mEditRecipe.RemoveInstruction(mSelectedEntryIndex);
                mListViewAdapter.notifyDataSetChanged();
                返回true;

            案例R.id.takePictureId:
                mCameraUtil.TakePicture();
                返回true;
        }

        返回false;
    }

    / *
     *(非Javadoc中)
     * @see android.app.Activity#onActivityResult(INT,INT,android.content.Intent)
     * /
    @覆盖
    保护无效onActivityResult(INT申请code,
                                    INT结果code,
                                    意图数据)
    {
//字符串imageLocation = mCameraUtil.onActivityResult(要求code,因此code,数据);
        位图imageBitmap = mCameraUtil.onActivityResult(要求code,因此code,数据);
        // TODO  - 开关的意图传递的参数!!!!像TakePicture(指数);
// mEditRecipe.GetInstructions()得到(mSelectedEntryIndex).SetBitmap(imageBitmap)。 // SetInstructionImageLocation(imageLocation);
        mSelectedEntryIndex = -1;

        //更新与图片中的ListViewItem
        mListViewAdapter.notifyDataSetChanged();
    }
}
 

适配器类

 包org.BJ.Food4All.Activities.RecipeBook;

进口的java.util.ArrayList;

进口org.BJ.Food4All.R;
进口org.BJ.Food4All.Recipe.Instruction;
进口org.BJ.Food4All.utils.GlobalDefs;

进口android.content.Context;
进口android.graphics.Bitmap;
进口android.graphics.Color;
进口android.graphics.Typeface;
进口android.net.Uri;
进口android.view.LayoutInflater;
进口android.view.View;
进口android.view.ViewGroup;
进口android.view.ViewGroup.LayoutParams;
进口android.widget.ArrayAdapter;
进口android.widget.ImageView;
进口android.widget.ListView;
进口android.widget.TextView;

公共类RecipeInstructionsListViewAdapter扩展ArrayAdapter<结构>
{
    私人语境mContext;
    私人的ArrayList<结构> mItems;
    私人LayoutInflater mInflater;

    公共RecipeInstructionsListViewAdapter(上下文的背景下,INT textViewResourceId,ArrayList的<结构>项目)
    {
        超(背景下,textViewResourceId,项目);

        mContext =背景;
        mItems =项目;

        mInflater =(LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }

    @覆盖
    公共查看getView(INT位置,
                        查看convertView,
                        父母的ViewGroup)
    {
          ViewHolder持有人=新ViewHolder();

          如果(convertView == NULL)
          {
              convertView = mInflater.inflate(R.layout.recipes_instruction_list_single_view_entry,NULL);
          }

          如果(super.getItem(位置)!= 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);

              字体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(位置).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(位置).getText());

              位图imageBitmap = mItems.get(位置).GetBitmap();
//字符串imageLocation = mItems.get(位置).GetInstructionImageLocation();
              如果(imageBitmap!= NULL)
              {
                  holder.instructionImage.setImageBitmap(imageBitmap); // setImageURI(Uri.parse(imageLocation));
                  holder.instructionImage.setVisibility(View.VISIBLE);
              }
              其他
              {
                  holder.instructionImage.setVisibility(View.GONE);
              }

              convertView.setTag(保持器);
              convertView.setLayoutParams(新ListView.LayoutParams(LayoutParams.FILL_PARENT,LayoutParams.WRAP_CONTENT));
          }
          其他
          {
          }

          返回convertView;
    }

    @覆盖
    公共布尔的IsEnabled(INT位置)
    {
        返回true;
    }

    静态类ViewHolder
    {
          TextView的instructionIndex;
          TextView的instructionText;
          ImageView的instructionImage;
    }
}
 

解决方案

我不知道这是否是正确的分类这是一个错误,但每次使用Typeface.createFromAsset如果创建了一个新的字体资产,不松开。请参见

你可以做的就是加载字样,当你加载的应用程序,并引用它们静态。我把我的应用字样。

 公共类YourApp扩展android.app.Application {
    公共无效的onCreate(){
        super.onCreate();

        //字样缓存
        initializeTypefaces();
    }

    公共静态类字体{
        公共静态字样定理;
    }

    私人无效initializeTypefaces(){
        Fonts.THEOREM = Typeface.createFromAsset(getAssets(),字体/ theorem.otf);
    }
}
 

然后,我做我的适配器:

  textView.setTypeface(YourApp.Fonts.THEOREM);
 

您可以去<一href="http://trace.adityalesmana.com/2010/08/declare-global-variable-in-android-via-android-app-application/">here来看看如何在Android中使用的应用程序。

最后,它看起来像你仍然创造只有当convertView是空的ViewHolder每次来代替。我会检讨这个视频来了解全貌。 <一href="http://www.google.com/events/io/2010/sessions/world-of-listview-android.html">http://www.google.com/events/io/2010/sessions/world-of-listview-android.html

下面是我如何使用ViewHolder方法的一个例子:

  @覆盖
公共查看getView(INT POS,查看convertView,ViewGroup中父){
    ViewHolder持有人;

    如果(convertView == NULL || convertView.getTag()== NULL){
        convertView = inflater.inflate(R.layout.list_item,父母,假);
        持有人=新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); //回收时只会发生一次!

        convertView.setTag(保持器);
    }其他{
        支架=(ViewHolder)convertView.getTag();
    }

    holder.text1.setText(someText);
    holder.text2.setText(someText);
    返回convertView;
}
 

I have a simple list view with adapter. I create 10+ listviewitems dynamically. Then I scroll up and down again and again and again.... I can see that available memory keeps going down...

Where do I need to free and what? Note - there is an imageview - but in my test I have not used any images so it is View.GONE.

Also - with which tool can I profile the memory usage on the android. I have found yourKit,but how do I configure it for the android (I run the application on the device)/

The Activity class

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();
    }
}

The adapter class

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<Instruction> 
{
    private Context mContext;
    private ArrayList<Instruction> mItems;
    private LayoutInflater mInflater;

    public RecipeInstructionsListViewAdapter(Context context, int textViewResourceId, ArrayList<Instruction>items) 
    {
        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;
    }
}

解决方案

I'm not sure if it is correct to classify this as a bug, but every time you use Typeface.createFromAsset if creates a new font asset and does not release it. See this.

What you can do is load the typefaces when you load your app and reference them statically. I put my typefaces in Application.

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");
    }
}

And then I do this in my adapter:

textView.setTypeface(YourApp.Fonts.THEOREM);

You can go here to see how to use Application in Android.

Lastly, it looks like your still creating your ViewHolder every time instead of only when convertView is null. I would review this video to get the whole picture. http://www.google.com/events/io/2010/sessions/world-of-listview-android.html

Here is an example of how I use the ViewHolder method:

@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;
}

这篇关于列表视图内存泄漏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆