java RecyclerView 滚动时滞后

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/41923817/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 06:15:05  来源:igfitidea点击:

Lags when RecyclerView scrolling

javaandroidandroid-studioandroid-recyclerviewrecycler-adapter

提问by Nikita G.

I can not understand what is the reason the brakes when you scroll through my RecyclerView. Scrolling is called by mRecyclerView.smoothScrollToPosition(position);When you scroll through the list of twitches (rendering time frame> 16ms)

我不明白滚动 RecyclerView 时刹车的原因是什么。mRecyclerView.smoothScrollToPosition(position);当您滚动抽搐列表时调用滚动(渲染时间范围> 16ms)

AlphaAdapter.java

AlphaAdapter.java

public class AlphaAdapter extends RecyclerView.Adapter<AlphaAdapter.ViewHolder> {
    private static final String TAG = "AlphaAdapter";
    private Context mContext;
    private List<PrimaryWeapon> mGunList;
    private boolean isAnimate;

    private final int VIEW_TYPE_NULL        = 0;
    private final int VIEW_TYPE_ITEM        = 1;


    public AlphaAdapter(Context mContext, List<PrimaryWeapon> mGunList) {
        this.mContext = mContext;
        this.mGunList = mGunList;
    }

    public AlphaAdapter(Context mContext, List<PrimaryWeapon> mGunList, boolean a) {
        this.mContext = mContext;
        this.mGunList = mGunList;
        this.isAnimate = a;
    }

    @Override
    public int getItemViewType(int position) {
        try {
            if (mGunList.get(position).getNameWeapon().equals(""))
                return VIEW_TYPE_NULL;
            else return VIEW_TYPE_ITEM;
        } catch (Exception e) {
            e.printStackTrace();
            Log.e("AlphaAdapter", String.valueOf(position));
            return VIEW_TYPE_NULL;
        }
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = null;
        if (viewType == VIEW_TYPE_ITEM)
            view = LayoutInflater.from(mContext).inflate(R.layout.item_game, parent, false);
        else
            view = LayoutInflater.from(mContext).inflate(R.layout.item_null, parent, false);
        return new ViewHolder(view);
    }

    @Override
    public void onViewDetachedFromWindow(ViewHolder holder) {
        super.onViewDetachedFromWindow(holder);
        holder.itemView.clearAnimation();
    }

    @Override
    public boolean onFailedToRecycleView(ViewHolder holder) {
        return true;
    }

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
    @Override
    public void onBindViewHolder(final ViewHolder holder, int position) {
        try {
            long startTime = System.currentTimeMillis();
            if (!getWeapon(position).getNameWeapon().equals("")) {
                if (isAnimate) {
                    if (mLastAnimPosition < position) {
                        mLastAnimPosition = position;
                        Animation a = AnimationUtils.loadAnimation(mContext, R.anim.item_game_anim);
                        holder.itemView.startAnimation(a);
                        a.setAnimationListener(new Animation.AnimationListener() {
                            @Override
                            public void onAnimationStart(Animation animation) {

                            }

                            @Override
                            public void onAnimationEnd(Animation animation) {
                            }

                            @Override
                            public void onAnimationRepeat(Animation animation) {

                            }
                        });
                    }
                }

               Picasso.with(mContext)
                        .load("file:///android_asset/" + getWeapon(position).getImagePath())
                        .placeholder(R.drawable.loading)
                        .error(R.drawable.load_fail)
                        .into(holder.image);
                holder.main.setText(getWeapon(position).getNameWeapon());
                holder.desc.setText(getWeapon(position).getNameSkin());

            }
            Log.i(TAG, String.valueOf(System.currentTimeMillis() - startTime) + "ms onBind (" + String.valueOf(position) + ");");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

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

    private PrimaryWeapon getWeapon(int position) {
        return mGunList.get(position);
    }

    @Override
    public int getItemCount() {
        return mGunList.size();
    }

    class ViewHolder extends RecyclerView.ViewHolder {

        TextView main, desc;
        ImageView image;

        ViewHolder(View itemView) {
            super(itemView);
            main = (TextView) itemView.findViewById(R.id.item_textMain);
            desc = (TextView) itemView.findViewById(R.id.item_textDescription);
            image = (ImageView) itemView.findViewById(R.id.item_image);
        }
    }

}

Layout element that participates in the adapter consists of LinearLayout, ImageView, 2 TextView.

参与adapter的Layout元素由LinearLayout、ImageView、2个TextView组成。

At first I thought that the problem in downloading images, try to load them into AsyncTask, upload bitmap through Picasso and without, but after all removed from their mark, and the list will still lag.

一开始以为是下载图片的问题,尝试加载到AsyncTask中,通过Picasso上传位图,不加,但毕竟去掉了他们的标记,列表还是会滞后。

To dump the RAM after the scroll is, it shows that there are 71 memory instances originally ViewHolder, after adding

滚动后dump内存,显示原来ViewHolder有71个内存实例,添加后

@Override
    public boolean onFailedToRecycleView(ViewHolder holder) {
        return true;
    }

this value is reduced to 15-16, although the rate for RecyclerView - 5copies, and it should override them, but does not create new ones when you scroll. Screenshot 1

这个值减少到 15-16,虽然 RecyclerView 的比率 - 5 个副本,它应该覆盖它们,但是当你滚动时不会创建新的。 截图 1

item_null.xml

item_null.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="74dp"
    android:layout_height="80dp">

</LinearLayout>

item_game.xml

item_game.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_gravity="center"
    android:padding="2dp"
    android:id="@+id/layout"
    android:layout_height="80dp"
    android:fitsSystemWindows="true"
    android:layout_width="74dp">

    <ImageView
        android:id="@+id/item_image"
        android:layout_width="match_parent"
        android:layout_height="56dp"
        />

    <TextView
        android:layout_gravity="top"
        android:layout_width="match_parent"
        android:singleLine="true"
        android:id="@+id/item_textMain"
        android:paddingLeft="4dp"
        android:paddingRight="4dp"
        android:paddingTop="1dp"
        android:gravity="left"
        android:textColor="#FFF"
        android:maxLines="1"
        android:textSize="8sp"
        android:layout_height="10dp" />

        <TextView
            android:layout_gravity="bottom"
            android:layout_width="match_parent"
            android:id="@+id/item_textDescription"
            android:paddingLeft="4dp"
            android:paddingRight="4dp"
            android:gravity="left"
            android:textColor="#FFF"
            android:singleLine="true"
            android:maxLines="1"
            android:maxLength="40"
            android:textSize="6sp"
            android:layout_height="12dp" />

</LinearLayout>

采纳答案by android developer

In response to the answer that was accepted here:

回应这里接受的答案:

How could a Context be a memory leak on an adapter? Or even make it slower? It's attached only to the Activity's RecyclerView anyway... Once the Activity is gone, the context is GC-ed together with the adapter. Not only that, but getting the Context from the view will always return you the same one, so it's useless and calls to functions without any need. You can even make the adapter a static inner class, so that you could access it directly, instead of having a declared field. In short, the context isn't the reason you have a memory leak.

上下文怎么会是适配器上的内存泄漏?或者甚至让它变慢?无论如何,它只附加到 Activity 的 RecyclerView ......一旦 Activity 消失,上下文将与适配器一起进行 GC 处理。不仅如此,从视图中获取 Context 将始终返回相同的 Context,因此它是无用的,并且不需要任何函数调用。您甚至可以使适配器成为静态内部类,以便您可以直接访问它,而无需声明字段。简而言之,上下文不是内存泄漏的原因。

What could be the issue, then? I think you need to cancel the previous image request on Picasso, each time you bind to a ViewHolder, like this:

那可能是什么问题呢?我认为每次绑定到 ViewHolder 时,您都需要取消 Picasso 上的先前图像请求,如下所示:

 Picasso.with(context).cancelRequest(holder.image);

You might also want to do it for all, in case the Activity was destroyed (yet not because of orientation change), but this depends on your needs.

您可能还想为所有人做这件事,以防 Activity 被破坏(但不是因为方向改变),但这取决于您的需要。

I suggest to also try Glide library instead. It seems more modern and still being maintained.

我建议也试试 Glide 库。它似乎更现代,并且仍在维护中。

回答by Andrej Jurkin

You are passing a context to adapter. First of all this could lead to memory leaks and also could be affecting your performance. Instead of passing the context into adapter, just simply get it from ViewHolder. You can always get a context reference inside RecyclerView.Adapter without a need to pass it around.

您正在将上下文传递给适配器。首先,这可能会导致内存泄漏,也可能会影响您的性能。无需将上下文传递给适配器,只需从 ViewHolder 获取它即可。您始终可以在 RecyclerView.Adapter 中获得上下文引用,而无需传递它。

To dump the RAM after the scroll is, it shows that there are 71 memory instances originally ViewHolder.

滚动后dump内存,显示原来ViewHolder有71个内存实例。

Judging from the dump this is most probably the case.

从转储来看,这很可能是这种情况。

@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    Context context = parent.getContext();
    ...
}

@Override
public void onBindViewHolder(final ViewHolder holder, int position) {
    Context context = holder.itemView.getContext();
    ...
}