2014-01-05 34 views
1

我在BaseAdapter中使用了我的getView()的以下代码。Android ViewHolder模式内存泄漏

当我尝试旋转手机几次时,每当堆内存增加。虽然我在内存分析器中分析了这一点,但我发现新的TextView正在创建,但旧的并没有被破坏。

我应该怎么做才能解决这个问题?

全部适配器代码:

package in.mypack.ui; 

import static in.mypack.Util.getHelper; 
import in.mypack.data.MyClass; 
import in.mypack.MyMap; 

import java.util.ArrayList; 
import java.util.Locale; 
import java.util.Map.Entry; 

import android.graphics.Color; 
import android.graphics.Paint; 
import android.graphics.Typeface; 
import android.view.LayoutInflater; 
import android.view.View; 
import android.view.ViewGroup; 
import android.widget.BaseAdapter; 
import android.widget.Filter; 
import android.widget.TextView; 

public class MyAdapter extends BaseAdapter { 

    private Filter filter; 
    private MyMap<String, MyClass> items; 
    private MyMap<String, MyClass> totalItems; 
    private Locale locale; 
    private LayoutInflater inflater; 
    @SuppressWarnings("unused") 
    private final String TAG = "MyAdapter"; 

    public MyAdapter(MyMap<String, MyClass> objects) { 
     items = objects; 
     inflater = getHelper().getLayoutInflater(); 
    } 

    private static class ViewHolder { 
     TextView one, two, three; 
    } 

    @Override 
    public View getView(int position, View convertView, ViewGroup parent) { 
     ViewHolder holder = null; 
     if (convertView == null) { 
      convertView = inflater.inflate(R.layout.list_item, null); 
      holder = new ViewHolder(); 
      holder.one = (TextView) convertView.findViewById(R.id.one); 
      holder.two = (TextView) convertView.findViewById(R.id.two); 
      holder.three = (TextView) convertView.findViewById(R.id.three); 
      Typeface font = Typeface.createFromAsset(getHelper().getAssets(), getHelper().getString(R.string.font_custom)); 
      holder.one.setTypeface(font); 
      holder.two.setTypeface(font); 
      holder.three.setTypeface(font); 
      convertView.setTag(holder); 
     } 
     else { 
      holder = (ViewHolder) convertView.getTag(); 
     } 

     MyClass myObject = getItem(position); 
     holder.one.setText(myObject.getName()); 
     holder.two.setText(myObject.getInfo()); 
     holder.three.setText(myObject.getSize()); 
     addColors(convertView, holder, myObject); 
     return convertView; 
    } 

    private void addColors(View convertView, ViewHolder holder, MyClass myObject) { 
     if (myObject.isValid()) { 
      convertView.setBackgroundColor(Color.argb(255,225,225,225)); 
      holder.one.setPaintFlags(holder.one.getPaintFlags() & (~Paint.STRIKE_THRU_TEXT_FLAG)); 
      holder.two.setPaintFlags(holder.one.getPaintFlags() & (~Paint.STRIKE_THRU_TEXT_FLAG)); 
     } 
     else { 
      convertView.setBackgroundColor(Color.argb(255,185,185,185)); 
      holder.one.setPaintFlags(holder.one.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG); 
      holder.two.setPaintFlags(holder.one.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG); 
     } 
    } 

    public Filter getFilter() { 
     if (filter == null) { 
      locale = Locale.getDefault(); 
      filter = new Filter() { 
       @Override 
       protected FilterResults performFiltering(CharSequence query) { 
        FilterResults results = new FilterResults(); 
        if (totalItems == null) { 
         totalItems = new MyMap<String, MyClass>(); 
         totalItems.putAll(items); 
        } 

        if (query == null || 0 == query.length()) { 
         results.count = totalItems.size(); 
         results.values = totalItems; 
        } 
        else { 
         MyMap<String, MyClass> filteredList = new MyMap<String, MyClass>(); 
         MyMap<String, MyClass> containsList = new MyMap<String, MyClass>(); 
         int size = totalItems.size(); 
         for (int i = 0; i < size; i++) { 
          Entry<String, MyClass> entry = totalItems.getEntry(i); 
          if (entry.getValue().getTitle().toLowerCase(locale).startsWith(query.toString().toLowerCase(locale))) { 
           filteredList.putEntry(entry); 
          } else if (entry.getValue().getTitle().toLowerCase(locale).contains(query.toString().toLowerCase(locale))) { 
           containsList.putEntry(entry); 
          } 
         } 
         filteredList.putAll(containsList); 
         results.count = filteredList.size(); 
         results.values = filteredList; 
        } 
        return results; 
       } 

       @SuppressWarnings("unchecked") 
       @Override 
       protected void publishResults(CharSequence query, FilterResults results) { 
        items.clear(); 
        items.putAll((MyMap<String, MyClass>) results.values); 
        notifyDataSetChanged(); 
       } 

      }; 
     } 
     return filter; 
    } 

    public void filter(String query) { 
     getFilter().filter(query); 
    } 

    @Override 
    public int getCount() { 
     return items.size(); 
    } 

    @Override 
    public MyClass getItem(int index) { 
     return items.get(index); 
    } 

    @Override 
    public long getItemId(int position) { 
     return position; 
    } 

    public void add(MyClass myObject) { 
     items.sortOnPut(myObject.getName(), myObject, MyMap.Sorting.Value); 
    } 
} 
+0

某些东西持有对它们的引用,因此它们不是垃圾回收,但未在代码段中显示。 – laalto

+0

也许你正在泄漏你的活动参考, – marcinj

+0

你能否给出完整的适配器类代码?,我想你可能会泄露你的活动上下文。有在此代码没有问题,这段代码是好的 –

回答

0

在列表中的适配器,你不应该使用应用程序上下文,请使用活动场景。 如果你正在使用片段,并且不想重新创建它,使用Fragment的setRetainInstance(boolean)为true,但不使用应用程序上下文。

+0

将给它一个尝试 – Peter

+0

解释_why_你不应该在列表适配器中使用应用程序上下文吗? –

+0

http: //android-developers.blogspot.in/2009/01/avoiding-m emory-leaks.html –

0

虽然TextViews被保留在内存中,但这并不意味着它是您使用视图持有者模式是罪魁祸首。

在这种情况下,我相信它是你的过滤器 - 你正在创建一个内部匿名类,它保持对Adapter的引用,该Adapter继续引用Inflater,它继续引用创建它的Context。

筛选器(performFiltering)在后台线程中运行,并且将使活动保持比想要的长。

尝试将Filter的实现移至单独的类或静态内部类。

+0

我使用应用程序上下文来创建inflater,但不是活动上下文 无论如何,我会试试看,并在这里更新 – Peter

0

您是否试图单独在您的ViewHolder中声明TextView?而不是将它们内联在一起?

0

我已经经历了RecyclerViews泄漏片段的视图层次的问题,由于在片段被破坏的RecyclerView不是从适配器注销。

您的内存泄漏可能存在,并且与ViewHolders无关。

作为一个快速的解决方案,你可以从#onDestroyView()适配器注销RecyclerView:

@Override 
public void onDestroyView() { 
    super.onDestroyView(); 
    mRecyclerView.setAdapter(null); 
} 

看一看this,在这里您可以找到完整的解释。