大叔程序员的第10天 @内存泄露

一,convertView与ViewHolder

能引起内存泄露的代码: BadAdapter.java

public class BadAdapter extends BaseAdapter {
    ......

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        Log.d("MyAdapter", "Position:" + position + "---"
                + String.valueOf(System.currentTimeMillis()));
        final LayoutInflater inflater = (LayoutInflater) mContext
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View v = inflater.inflate(R.layout.list_item_icon_text, null);
        ((ImageView) v.findViewById(R.id.icon)).setImageResource(R.drawable.icon);
        ((TextView) v.findViewById(R.id.text)).setText(mData[position]);
        return v;
    }
}

修正优化示例代码示例代码:GoodAdapter.java

public class GoodAdapter extends BaseAdapter {

    ......

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        Log.d("MyAdapter", "Position:" + position + "---"
                + String.valueOf(System.currentTimeMillis()));
        ViewHolder holder;
        if (convertView == null) {
            final LayoutInflater inflater = (LayoutInflater) mContext
                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = inflater.inflate(R.layout.list_item_icon_text, null);
            holder = new ViewHolder();
            holder.icon = (ImageView) convertView.findViewById(R.id.icon);
            holder.text = (TextView) convertView.findViewById(R.id.text);
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }
        holder.icon.setImageResource(R.drawable.icon);
        holder.text.setText(mData[position]);
        return convertView;
    }

    static class ViewHolder {
        ImageView icon;
        TextView text;
    }
}

MainActivity.java

public class MainActivity extends ListActivity {
    private BadAdapter/GoodAdapter mAdapter;

    private String[] mArrData;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        mArrData = new String[1000];
        for (int i = 0; i < 1000; i++) {
            mArrData[i] = "Google IO Adapter";
        }
        mAdapter = new BadAdapter/GoodAdapter(this, mArrData);
        setListAdapter(mAdapter);
    }
}

相关说明:

提升Adapter的两种方法

To work efficiently the adapter implemented here uses two techniques:
-It reuses the convertView passed to getView() to avoid inflating View when it is not necessary

(译:重用缓存convertView传递给getView()方法来避免填充不必要的视图)
-It uses the ViewHolder pattern to avoid calling findViewById() when it is not necessary

(译:使用ViewHolder模式来避免没有必要的调用findViewById():因为太多的findViewById也会影响性能)
ViewHolder类的作用
-The ViewHolder pattern consists in storing a data structure in the tag of the view
returned by getView().This data structures contains references to the views we want to bind data to,
thus avoiding calling to findViewById() every time getView() is invoked

(译:ViewHolder模式通过getView()方法返回的视图的标签(Tag)中存储一个数据结构,这个数据结构包含了指向我们

要绑定数据的视图的引用,从而避免每次调用getView()的时候调用findViewById())

二、Cursor未正常关闭

1,避免close()之前发生异常

Cursor c;
try {  
    c = queryCursor();  
    int a = c.getInt(1);  
    ......
    // 如果出错,后面的cursor.close()将不会执行
    //c.close();  
} catch (Exception e) {  
} finally{
    if (c != null) {
        c.close();
    }

2. Cursor需要继续使用,不能马上关闭
    有没有这种情况?怎么办?
    答案是有,CursorAdapter就是一个典型的例子。
    CursorAdapter示例如下:

1
2
3
4
5
6
mCursor = getContentResolver().query(CONTENT_URI, PROJECTION,
null, null, null);
mAdapter = new MyCursorAdapter(this, R.layout.list_item, mCursor);
setListAdapter(mAdapter);
// 这里就不能关闭执行mCursor.close(),
// 否则list中将会无数据

这样的Cursor应该什么时候关闭呢?
    这是个可以说好回答也可以说不好回答的问题,那就是在Cursor不再使用的时候关闭掉。
    比如说,
    上面的查询,如果每次进入或者resume的时候会重新查询执行。
    一般来说,也只是这种需求,很少需要看不到界面的时候还在不停地显示查询结果,如果真的有,不予讨论,记得最终关掉就OK了。
    这个时候,我们一般可以在onStop()方法里面把cursor关掉(同时意味着你可能需要在onResume()或者onStart()重新查询一下)。

1
2
3
4
5
6
@Override
protected void onStop() {
    super.onStop();
    // mCursorAdapter会释放之前的cursor,相当于关闭了cursor
    mCursorAdapter.changeCursor(null);
}

 附上CursorAdapter的changeCursor()方法源码

* Change the underlying cursor to a new cursor. If there is an existing cursor it will be
 * closed.
 *
 * @param cursor The new cursor to be used
 */
public void changeCursor(Cursor cursor) {
    Cursor old = swapCursor(cursor);
    if (old != null) {
        old.close();
    }
}
 
/**
 * Swap in a new Cursor, returning the old Cursor.  Unlike
 * {@link #changeCursor(Cursor)}, the returned old Cursor is <em>not</em>
 * closed.
 *
 * @param newCursor The new cursor to be used.
 * @return Returns the previously set Cursor, or null if there wasa not one.
 * If the given new Cursor is the same instance is the previously set
 * Cursor, null is also returned.
 */
public Cursor swapCursor(Cursor newCursor) {
    if (newCursor == mCursor) {
        return null;
    }
    Cursor oldCursor = mCursor;
    if (oldCursor != null) {
        if (mChangeObserver != null) oldCursor.unregisterContentObserver(mChangeObserver);
        if (mDataSetObserver != null) oldCursor.unregisterDataSetObserver(mDataSetObserver);
    }
    mCursor = newCursor;
    if (newCursor != null) {
        if (mChangeObserver != null) newCursor.registerContentObserver(mChangeObserver);
        if (mDataSetObserver != null) newCursor.registerDataSetObserver(mDataSetObserver);
        mRowIDColumn = newCursor.getColumnIndexOrThrow("_id");
        mDataValid = true;
        // notify the observers about the new cursor
        notifyDataSetChanged();
    } else {
        mRowIDColumn = -1;
        mDataValid = false;
        // notify the observers about the lack of a data set
        notifyDataSetInvalidated();
    }
    return oldCursor;
}

(部分来自http://www.cnblogs.com/qianxudetianxia/archive/2012/11/19/2757376.html)

原文地址:https://www.cnblogs.com/linxiaojiang/p/2959004.html