RecyclerView适配器不想重用ViewHolders

时间:2017-04-18 10:50:25

标签: android android-fragments android-recyclerview

我遇到了内存泄漏问题。 我有一个带有FragmentStatePagerAdapter的片段,里面有一个片段(还有更多片段,我只留下一个用于测试)。 这个片段有RecyclerView和一些不同的ViewHolders。

例如,这是一个简单的ViewHolder,它只在RecyclerView中的一个位置使用 - 作为标题。

public class CurrencyViewHolder extends RecyclerView.ViewHolder {

    @BindView(R.id.currency_date)
    TextView currencyDate;
    @BindView(R.id.currency_usd)
    TextView currencyUsd;
    @BindView(R.id.currency_eur)
    TextView currencyEur;
    @BindView(R.id.currency_info)
    LinearLayout currencyInfo;

    public CurrencyViewHolder(View itemView, boolean nightMode) {
        super(itemView);
        ButterKnife.bind(this, itemView);
    }

    public void bind(CbrResponse response) {
        currencyDate.setText(String.format("asdsdf %s", response.getDate()));
        currencyUsd.setText(String.format("USD %,.2f", response.getUSD().getValue()));
        currencyEur.setText(String.format("EUR %,.2f", response.getEUR().getValue()));
    }
}

但是,正如你所看到的,在这张照片上enter image description here

我有12个这个班级的实例。我已经将12笔交易转发给了另一个片段而我的 ViewHolders由于某种原因被重新创建了!

我认为这件事会导致我的内存泄漏。我不确定你想要哪个代码所以我放了一些适配器方法,以防你想看到它。

NewsListRecyclerAdapter是我的适配器,执行下面的代码。注意只创建一次这个类的内容!。

Here is a memory graph. You clearly can see momory increase. manual GC does not help. And after a few more transactions I'll get OUT OF MEMORY EXCEPTION

public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            if (viewType == Constant.TYPE_TEASER) {
                View view = inflater.inflate(R.layout.item_teaser_recycler, parent, false);
                return new TeaserViewHolder(view, reference, isNightMode);
            } else if (viewType == Constant.TYPE_CURRENCY_HEADER) {
                View view = inflater.inflate(R.layout.item_list_currency, parent, false);
                return new CurrencyViewHolder(view, isNightMode);
            } else if (viewType == Constant.TYPE_TEASER_VISITED) {
                View view = inflater.inflate(R.layout.item_teaser_recycler, parent, false);
                return new TeaserVisitedViewHolder(view, reference, isNightMode);
            }
            throw new RuntimeException(
                    "there is no type that matches the type " + viewType
                            + " + make sure your using types correctly");
        }

        @Override
        public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
            NewsListItem item = sortedList.get(position);
            if (holder instanceof TeaserVisitedViewHolder) {
                ((TeaserVisitedViewHolder) holder).bind(item);
            } else if (holder instanceof TeaserViewHolder) {
                ((TeaserViewHolder) holder).bind(item);
            } else if (holder instanceof CurrencyViewHolder) {
                ((CurrencyViewHolder) holder).bind(item.getCurrency());
            } else {
                throw new RuntimeException(
                        "there is no type that matches the type " + holder.getClass().getSimpleName()
                                + " + make sure your using types correctly");
            }
        }

UPD

这是我使用ViewPager的framgnet:

public class PagerListsFragment extends NightModeFragment {
    private final String LOG_TAG = PagerListsFragment.class.getSimpleName();


    @BindView(R.id.circlePagerListsIndicator)
    TextCirclePageIndicator pageIndicator;
    @BindView(R.id.settings_button)
    ImageButton settingsButton;
    @BindView(R.id.pager_lists_header)
    FrameLayout pagerListsHeader;
    @BindView(R.id.pagerLists)
    ViewPager viewPager;
    @BindView(R.id.linear_layout)
    LinearLayout linearLayout;

//    ArticlesFragment articlesFragment;
//    FavouritesFragment favouritesFragment;


    PagerListsFragmentAdapter fragmentAdapter;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d("PagerListsFragment", "onCreate");
        fragmentAdapter = new PagerListsFragmentAdapter(getContext(), getChildFragmentManager());
//        articlesFragment = new ArticlesFragment();
//        favouritesFragment = new FavouritesFragment();
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        Log.d("PagerListsFragment", "onViewCreated");
        viewPager = (ViewPager) view.findViewById(R.id.pagerLists);
        viewPager.setAdapter(fragmentAdapter);

        viewPager.setPageMargin((int) getResources().getDimension(R.dimen.lists_pager_margin));

        pageIndicator =
                (TextCirclePageIndicator) view.findViewById(R.id.circlePagerListsIndicator);
        pageIndicator.setViewPager(viewPager);

        pagerListsHeader = (FrameLayout) view.findViewById(R.id.pager_lists_header);
        linearLayout = (LinearLayout) view.findViewById(R.id.linear_layout);

        final int topMarginUp = 0;
        final int topMarginDown = getResources().getDimensionPixelSize(R.dimen.toolbar_height);
        setTopMargin(linearLayout, topMarginDown);

        ImageButton settingsButton = (ImageButton) view.findViewById(R.id.settings_button);
        settingsButton.setOnClickListener(v -> ((MainActivity) getActivity()).openSettings());
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        Log.d("PagerListsFragment", "onDestroyView");
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d("PagerListsFragment", "onDestroy");
    }

    @Override
    protected int getLayoutResource() {
        return R.layout.fragment_list_paged;
    }


    private void setTopMargin(View view, int topMarginInPx) {
        ViewGroup.MarginLayoutParams layoutParams =
                (ViewGroup.MarginLayoutParams) view.getLayoutParams();
        layoutParams.topMargin = topMarginInPx;
        view.requestLayout();
    }

    public static PagerListsFragment getInstace(Bundle extra) {
        PagerListsFragment fragment = new PagerListsFragment();
        fragment.setArguments(extra);
        return fragment;
    }
}

这是我的片段,其中RecyclerView是:

public class NewsFragment extends BaseNewsFragment implements NewsViewState,
        TeaserViewHolder.OnItemClickListener {

    private final Category category = new Category(Category.NEWS);

    @InjectPresenter
    NewsPresenter presenter;

    protected NewsListRecyclerAdapter adapter;
    protected EndlessRecyclerOnScrollListener scrollListener;

    @BindView(R.id.news_recycler)
    protected RecyclerView newsRecycler;
    @BindView(R.id.refresh)
    protected SwipeRefreshLayout refresh;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d("NewsFragment", "onCreate");
        adapter = new NewsListRecyclerAdapter(getActivity(), this);
        presenter.loadNewsAndCurrency(0, true, category);
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        LinearLayoutManager manager = new LinearLayoutManager(getActivity());

        scrollListener = new EndlessRecyclerOnScrollListener(manager) {
            @Override
            public void onLoadMore(int current_page) {
                presenter.loadNewsAndCurrency(adapter.getItemCount(), false, category);
            }
        };
        newsRecycler.addOnScrollListener(scrollListener);
        newsRecycler.setItemAnimator(null);
        newsRecycler.setLayoutManager(manager);
        newsRecycler.setAdapter(adapter);

        refresh.setOnRefreshListener(() -> {
            scrollListener.reset(0, true);
            presenter.loadNewsAndCurrency(0, true, category);
            if (!DEBUG) {
                FlurryAgent.logEvent(FlurryHelper.USER_NEWS_LIST);
            }
        });
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        Log.d("NewsFragment", "onDestroy");
        if (newsRecycler != null) {
            newsRecycler.setAdapter(null);
        }
    }

    @Override
    public void onTeasersLoaded(List<NewsListItem> teasers, boolean refresh) {
        adapter.addAll(teasers, refresh);
    }

    @Override
    public void removeTeaser(NewsListItem item) {
        Log.d("BaseNewsFragment", "Removing item.getType():" + item.getType());
        if (item.getTeaser().getCategoryId() == category.getCategoryInt()) {
            adapter.remove(item);
        }
    }

    @Override
    public void addTeaser(NewsListItem item) {
        if (item.getTeaser().getCategoryId() == category.getCategoryInt()) {
            adapter.add(item);
        }
    }

    @Override
    public void onCurrencyLoaded(NewsListItem currencyItem) {
        adapter.add(currencyItem);
    }

    @Override
    public void onItemClick(NewsListItem teaser) {
        ((MainActivity) getActivity()).onNewsSelected(teaser.getTeaser().getUuid(), category.getCategoryInt());
        adapter.add(teaser);
    }

    @Override
    public void setNightMode() {
        adapter.setNightMode(isNightMode);
        newsRecycler.setBackgroundColor(getCurrentBackgroundColor());
    }

    @Override
    public void hideProgress() {
        refresh.setRefreshing(false);
    }

    @Override
    public void showProgress() {
        refresh.setRefreshing(true);
    }
}

我无法滑动碎片,因为我在ViewPager中只剩下一个framgent(只是为了测试)。

1 个答案:

答案 0 :(得分:0)

您的片段有多少时间重新创建?当您使用ViewHolder滑动时,可能会重新创建新的Fragment来计算12 FragmentStatePagerAdapter次,从而重新设置RecyclerViewRecyclerAdapterViewHolders

你还试过在查看堆之前使用Initiate GC吗?他们可能GC正在等待收集旧的已销毁的视图,但由于有足够的可用内存GC尚未获得这些内容。