Android RecyclerView: использование памяти увеличивается во время прокрутки, если 'setIsRecyclabe (false)' не используется - PullRequest
0 голосов
/ 16 июня 2020

В моем приложении android (Java) я показываю список из примерно 1800 контактов в режиме recyclerview. При выполнении профиля памяти было обнаружено, что при прокрутке представления ресайклера использование памяти быстро увеличивалось. Итак, я нашел здесь этот вопрос, в котором упоминалась та же проблема, и опробовал решение, которое заключалось в setIsRecyclable (false) в onBindViewHolder, и оно сработало. Результаты профилирования приведены ниже.


Случай 1: setIsRecyclable (False) не используется

Начальное использование памяти: ~ 40M [Java = 5.9M Native = 5M Graphics = 20,3M Stack = 0,3M Code = 5.3M Others = 0.8M]

Пиковое использование памяти: ~ 345M [Java = 187,5M Native = 39,1M Graphics = 101,5M Stack = 0,4M Code = 11,6 M Others = 6.5M]

enter image description here

Также было обнаружено, что пиковое использование памяти увеличивается с увеличением количества элементов в списке. После остановки непрерывной прокрутки на некоторое время использование памяти снижается, но только до 162 МБ.


Случай 2: после добавления setIsRecyclable (False) в onBindViewHolder

Начальное использование памяти: ~ 42 МБ [Java = 5,8 МБ = 5,5 МБ графика = 20,2 МБ = 0,3 МБ, код = 9,4 млн. Другое = 0,8 МБ]

Пиковое использование памяти: ~ 100 МБ [Java = 43,9 млн собственных = 9,7 млн ​​графики = 32,6 млн стек = 0,4 млн кодов = 11,7 млн ​​других = 2,2 млн]

enter image description here

Также в этом случае , увеличение количества элементов в списке существенно не повлияло на использование памяти. Хотя пиковое использование памяти составляет около 100 МБ, в среднем большую часть времени оно составляет около 70 МБ, что даже лучше.


Исходный код фрагмента, содержащего recyclerView

Примечание.
* Класс адаптера определяется как внутренний класс класса Fragment, а класс ViewHolder определяется как внутренний класс класса адаптера.
* 'App.personList' - это stati c arrayList, содержащий список контактов, а App - это класс ViewModel.
* адаптер1 - единственный интересующий адаптер. Избегайте адаптера 2 (обрабатывает другой небольшой список)

public class FragmentAllContacts extends Fragment 
{

public static MainActivity main;
public RecyclerView contactsView, tagsView;
LinearLayoutManager llm, lln;
Button filterCloseButton;
CardView filterView;
Adapter_ContactListView adapter1;
Adapter_TagListView adapter2;

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)
{
    setHasOptionsMenu(true);
    main = (MainActivity) getActivity();
    return inflater.inflate(R.layout.fragment_all_contacts, container, false);
}

@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState)
{
    super.onViewCreated(view, savedInstanceState);
    adapter1 = new Adapter_ContactListView(App.personList,getContext());
    adapter2 = new Adapter_TagListView(App.tagList,getContext());
    filterView = getView().findViewById(R.id.cardView7);
    FloatingActionButton fab = getView().findViewById(R.id.create_contact_fab);


    contactsView = getView().findViewById(R.id.allContacts_recyclerView);
    contactsView.setAdapter(adapter1);
    llm = new LinearLayoutManager(main.getBaseContext());
    contactsView.setLayoutManager(llm);
    contactsView.scrollToPosition(App.AllConnections.scrollPosition);

    tagsView = getView().findViewById(R.id.allTags_recyclerView);
    tagsView.setAdapter(adapter2);
    lln = new LinearLayoutManager(main.getBaseContext(), LinearLayoutManager.HORIZONTAL, false);
    tagsView.setLayoutManager(lln);

}







class Adapter_ContactListView extends RecyclerView.Adapter<Adapter_ContactListView.ViewHolder> implements Filterable {

    List<Person_PersistentData> contactsFiltered;
    Context context;


    public Adapter_ContactListView(List<Person_PersistentData> list, Context context)
    {
        this.contactsFiltered = list;
        this.context = context;
    }

    @Override
    public Adapter_ContactListView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.card_view_list, parent, false);
        Adapter_ContactListView.ViewHolder pane = new Adapter_ContactListView.ViewHolder(v);
        return pane;
    }

    @Override
    public void onBindViewHolder(Adapter_ContactListView.ViewHolder pane, int position) 
    {
        pane.setIsRecyclable(false);
        final Person_PersistentData rec = contactsFiltered.get(position);
        pane.nameView.setText(rec.personName + " (" + rec.personID + ")");
        Uri imageUri = App.FSManager.getProfilePic(rec.personID);
        if (imageUri != null) {pane.imageView.setImageURI(imageUri);} 
        else {pane.imageView.setImageResource(R.drawable.ico_60px);}
        if (App.AllConnections.personSelectionStack.contains(rec.personID)) {pane.cv.setBackgroundColor(context.getResources().getColor(R.color.rgb_000_070_100));}
        else
        {pane.cv.setBackgroundColor(context.getResources().getColor(R.color.rgb_020_020_020));}

        pane.cv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view)
            {
                if(App.AllConnections.selectionMode)
                {
                    App.Person_SelectionInput(rec.personID);
                    Adapter_ContactListView.this.notifyDataSetChanged();
                }
                else
                {
                    App.PersonInfo.id = rec.personID;
                    main.startTask(T.personInfo);
                }
            }
        });


        pane.cv.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View view)
            {
                App.Person_SelectionInput(rec.personID);
                Adapter_ContactListView.this.notifyDataSetChanged();
                return false;
            }
        });
        //animate(holder);

    }

    @Override
    public int getItemCount() {
        //returns the number of elements the RecyclerView will display
        return contactsFiltered.size();
    }

    @Override
    public void onAttachedToRecyclerView(RecyclerView recyclerView) {
        super.onAttachedToRecyclerView(recyclerView);

    }

    @Override
    public Filter getFilter() {  ...  }

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

    @Override
    public int getItemViewType(int position) {
        return position;
    }

    class ViewHolder extends RecyclerView.ViewHolder {

        CardView cv;
        TextView nameView;
        ImageView imageView;

        public ViewHolder(@NonNull View itemView)
        {
            super(itemView);
            cv = itemView.findViewById(R.id.cardView);
            nameView = itemView.findViewById(R.id.name);
            imageView = itemView.findViewById(R.id.imageViewZ);
        }
    }
}



} 


Вопрос

Итак, 'setIsRecyclable (False)' должен предотвращать повторное использование представлений, а это должен вызывать большее использование памяти. Но вместо этого он показывает противоположное поведение. Также я думаю, что приложение наверняка скроет sh, если ему придется обрабатывать еще больший список без использования setIsRecyclable (false). Почему это происходит?

1 Ответ

1 голос
/ 16 июня 2020

getItemId(int) и getItemViewType(int) НИКОГДА не должны возвращать сам position, вы нарушаете контракт recyclerview, заставляя его создавать новых держателей просмотра для каждой отдельной позиции, чтобы повторно использовать существующие представления.

Это является причиной вашей проблемы - каждая позиция имеет уникальный itemViewType, поэтому они начинают очень быстро заполняться recycledViewPool, поскольку они только вставляются и никогда не удаляются из нее. setIsRecyclable(False) позволяет обойти проблему, не помещая их в recyclerViewPool, но это не решает проблему отсутствия повторного использования представления.

Просто удалите getItemId и getItemViewType переопределения, потому что вы не используете их правильно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...