【android】Adapter 的 convertView 复用浅析

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。


  • 操作环境
  • 导读
  • 参数说明
  • 源码浅析


  • 操作系统: win7-64bit 旗舰版
  • android 版本: android-23


平时在使用 ListView 或 GridView 时,常需要自己复写 Adapter 的 View getView(int position, View convertView, ViewGroup parent) 方法,但对其中的 convertView 却不太了解,没法做到物尽其用;殊不知其用途可不小,可以在一定程度上避免 OOM


  • 在 Adapter.java 中,对参数 convertView 的说明如下:

    • 说明: convertView 是一个可重用的 View,当然,也有可能是 Null
    • 良好的习惯是,在使用之前都检测一下;
     * Get a View that displays the data at the specified position in the data set. You can either
     * create a View manually or inflate it from an XML layout file. When the View is inflated, the
     * parent View (GridView, ListView...) will apply default layout parameters unless you use
     * {@link android.view.LayoutInflater#inflate(int, android.view.ViewGroup, boolean)}
     * to specify a root view and to prevent attachment to the root.
     * @param position The position of the item within the adapter's data set of the item whose view
     *        we want.
     * @param convertView The old view to reuse, if possible. Note: You should check that this view
     *        is non-null and of an appropriate type before using. If it is not possible to convert
     *        this view to display the correct data, this method can create a new view.
     *        Heterogeneous lists can specify their number of view types, so that this View is
     *        always of the right type (see {@link #getViewTypeCount()} and
     *        {@link #getItemViewType(int)}).
     * @param parent The parent that this view will eventually be attached to
     * @return
    View getView(int position, View convertView, ViewGroup parent);


  • 再来看 AbsListView.java 的 obtainView 函数:
    • 说明:该函数的主要作用之一就是对 convertView 的复用;
    • AbsListView 维持:View[] mActiveViewsArrayList mScrapViews 来对 convertView 进行回收管理的;
    • mScrapViews 主要用于存放不在 ListView 屏幕内那些 convertView,其内部的 convertView 的排列是没有一定顺序的,使用的时候需留意;
    • mActiveViews 则专门用于保存在 ListView 中显示的 convertView;
    • 对于具有临时状态的 convertView,那么该 View 将不被纳入回收操作;
    • 对于类型为 ITEM_VIEW_TYPE_HEADER_OR_FOOTER 的视图,也是不进行回收操作的;
    • 对于其他的 convertView:首先通过 getScrapView( position ) 从 mScrapViews 中查询( 如果不存在,则需由之后的 getView 创建;如果存在,则将其返回,实现View的复用查询操作 );在调用 getView 函数后,如果得到一个新的 convertView,那么之前的 converView 将被继续保留在 mScrapViews 中,等待下一次根据 position 的查询;
     * Get a view and have it show the data associated with the specified
     * position. This is called when we have already discovered that the view is
     * not available for reuse in the recycle bin. The only choices left are
     * converting an old view or making a new one.
     * @param position The position to display
     * @param isScrap Array of at least 1 boolean, the first entry will become true if
     *                the returned view was taken from the scrap heap, false if otherwise.
     * @return A view displaying the data associated with the specified position
    View obtainView(int position, boolean[] isScrap) {
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "obtainView");

        isScrap[0] = false;

        // Check whether we have a transient state view. Attempt to re-bind the
        // data and discard the view if we fail.
        final View transientView = mRecycler.getTransientStateView(position);
        if (transientView != null) {
            final LayoutParams params = (LayoutParams) transientView.getLayoutParams();

            // If the view type hasn't changed, attempt to re-bind the data.
            if (params.viewType == mAdapter.getItemViewType(position)) {
                final View updatedView = mAdapter.getView(position, transientView, this);

                // If we failed to re-bind the data, scrap the obtained view.
                if (updatedView != transientView) {
                    setItemViewLayoutParams(updatedView, position);
                    mRecycler.addScrapView(updatedView, position);

            isScrap[0] = true;

            // Finish the temporary detach started in addScrapView().
            return transientView;

        final View scrapView = mRecycler.getScrapView(position);
        final View child = mAdapter.getView(position, scrapView, this);
        if (scrapView != null) {
            if (child != scrapView) {
                // Failed to re-bind the data, return scrap to the heap.
                mRecycler.addScrapView(scrapView, position);
            } else {
                isScrap[0] = true;

                // Finish the temporary detach started in addScrapView().

        if (mCacheColorHint != 0) {

        if (child.getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {

        setItemViewLayoutParams(child, position);

        if (AccessibilityManager.getInstance(mContext).isEnabled()) {
            if (mAccessibilityDelegate == null) {
                mAccessibilityDelegate = new ListItemAccessibilityDelegate();
            if (child.getAccessibilityDelegate() == null) {


        return child;
  • 总结:
    • RecycleBin 提供了 mScrapViews 和 mActiveViews 来实现复用机制;
    • AbsListView 已经提供了 RecycleBin 机制,为我们尽可能的提供复用机制;但在平时开发使用的过程中,我们还是应当尽量避免无谓的 new 操作;
    • 对于常用的 ListView 或者 GridView,如果 convertView 的布局大体都相同,可以尽量重用,避免 item 数量过大或过于分配频繁导致 OOM;





我现在在做一个聊天软件,这两天遇到个纠结的问题,我的聊天页面是一个ListView,item布局是左右的那种,来消息是在左边,type为2,发出在右边,type为1,如果不复用convertView就时不时的出现OOM,可是现在代码如下,麻烦各位看看是不是我的方法有问题,我在geView的时候有时候可能即使type为1却显示在左边,下面贴出主要的getView代码,中间省略了一些资源设置:rn[code=java] public View getView(int position, View convertView, ViewGroup parent)rn rn MessageBean mb = mbList.get(position);rn ViewHolder holder = null;rn View convertView1,convertView2;rn if(mb.getType().equalsIgnoreCase("1"))rn convertView1 = convertView;rn if(convertView1 == null)rn convertView1 = vi.inflate(R.layout.list_say_me_item, null);rn holder=new ViewHolder();rn holder.tvTime = (TextView) convertView1.findViewById(R.id.tv_sendtime);rn holder.tvText = (TextView) convertView1.findViewById(R.id.messagedetail_row_text);rn holder.layout = (LinearLayout) convertView1.findViewById(R.id.layout_bj);rn convertView1.setTag(holder);rn elsern holder = (ViewHolder) convertView1.getTag(); rn rn convertView = convertView1;rn else if(mb.getType().equalsIgnoreCase("2"))rn convertView2 = convertView;rn if(convertView2 == null)rn convertView2 = vi.inflate(R.layout.list_say_he_item, null);rn holder = new ViewHolder();rn holder.tvTime = (TextView) convertView2.findViewById(R.id.tv_sendtime);rn holder.tvText = (TextView) convertView2.findViewById(R.id.messagedetail_row_text);rn holder.layout = (LinearLayout) convertView2.findViewById(R.id.layout_bj);rn convertView2.setTag(holder); rn elsern holder = (ViewHolder) convertView2.getTag(); rn rn convertView = convertView2;rn rn rn return convertView;rn [/code] 论坛