【问题标题】:why viewPager cause memory leak?为什么 viewPager 会导致内存泄漏?
【发布时间】:2018-01-28 21:22:53
【问题描述】:

在我的应用程序中,我使用了 viewPager,它使用 FragmentStatePagerAdapter 调整片段。 每个片段都包含 gridView 和 GridViewAdapter。

一切正常,但每次我交换时,内存使用量都会增加很多并浪费内存。 为什么会发生,我该如何解决?

FragmentAdapter 类:

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;

import java.util.Calendar;


public class FragmentAdapter extends FragmentStatePagerAdapter {

    private static final int NUM_ITEMS = 12;


    public FragmentAdapter(FragmentManager fm) {
        super(fm);
    }

    @Override
    public Fragment getItem(int position) {
        switch (position) {
            case Calendar.JANUARY:
                return MonthFragment.newInstance(Calendar.JANUARY);
            case Calendar.FEBRUARY:
                return  MonthFragment.newInstance(Calendar.FEBRUARY);
            case Calendar.MARCH:
                return MonthFragment.newInstance(Calendar.MARCH);
            case Calendar.APRIL:
                return  MonthFragment.newInstance(Calendar.APRIL);
            case Calendar.MAY:
                return  MonthFragment.newInstance(Calendar.MAY);
            case Calendar.JUNE:
                return  MonthFragment.newInstance(Calendar.JUNE);
            case Calendar.JULY:
                return  MonthFragment.newInstance(Calendar.JULY);
            case Calendar.AUGUST:
                return  MonthFragment.newInstance(Calendar.AUGUST);
            case Calendar.SEPTEMBER:
                return  MonthFragment.newInstance(Calendar.SEPTEMBER);
            case Calendar.OCTOBER:
                return  MonthFragment.newInstance(Calendar.OCTOBER);
            case Calendar.NOVEMBER:
                return MonthFragment.newInstance(Calendar.NOVEMBER);
            case Calendar.DECEMBER:
                return MonthFragment.newInstance(Calendar.DECEMBER);
            default:
                throw new IllegalArgumentException("there is problem with position", new Throwable("NUM_ITEMS is not 12"));
        }
    }

    @Override
    public int getCount() {
        return NUM_ITEMS;
    }

}

MonthFragment 类:

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.GridView;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;


public class MonthFragment extends Fragment {

    private static final String ARG_MONTH = "arg_month";
    private int fragmentMonthId;

    private GridView gridView;
    private GridViewAdapter adapter;
    private ArrayList<Date> monthDates;

    public MonthFragment() {
        // Required empty public constructor
    }


    public static MonthFragment newInstance(int monthId) {
        MonthFragment fragment = new MonthFragment();
        Bundle args = new Bundle();
        args.putInt(ARG_MONTH, monthId);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getArguments() != null) {
            fragmentMonthId = getArguments().getInt(ARG_MONTH);
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_month, container, false);
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        gridView = view.findViewById(R.id.days);

        monthDates = getDates();
        adapter = new GridViewAdapter(getContext(), R.layout.day, 0, monthDates);

        gridView.setAdapter(adapter);
    }

    private ArrayList<Date> getDates() {
        ArrayList<Date> arrayList = new ArrayList<>();
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.DAY_OF_MONTH, 1);
        calendar.set(Calendar.MONTH, fragmentMonthId);
        calendar.set(Calendar.YEAR, calendar.get(Calendar.YEAR));
        for (int i = 0; i < calendar.getMaximum(Calendar.DAY_OF_MONTH); i++) {
            arrayList.add(calendar.getTime());
            calendar.add(Calendar.DATE, 1);
        }
        return arrayList;
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        monthDates.clear();
        adapter = null;
        gridView.setAdapter(null);
        gridView = null;
    }
}

GridViewAdapter 类:

package com.example.kobimoshe.calendarwithpager;

import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;


public class GridViewAdapter extends ArrayAdapter<Date> {

    ArrayList<Date> datesList;

    public GridViewAdapter(@NonNull Context context, int resource, int textViewResourceId, @NonNull List<Date> objects) {
        super(context, resource, textViewResourceId, objects);
        datesList = (ArrayList<Date>) objects;
    }

    @NonNull
    @Override
    public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
        if (convertView == null) {
            convertView = LayoutInflater.from(getContext()).inflate(R.layout.day, parent, false);
        }
        TextView dateView = (TextView) convertView.findViewById(R.id.date);
        dateView.setText(datesList.get(position).toString());

        return convertView;
    }

}

更新:

经过实验和研究,我假设两件事:

  1. GC 不会删除片段内的元素,即使我在片段被销毁时删除了它们(我假设因为当 onDestroyView 被调用时,内存使用图并没有减少)。

  2. 每次交换也会造成内存泄漏(我已经创建了一个新的 ViewPager,它使用 FragmentStatePagerAdapter 来设置空片段。每次在此应用中交换片段时,内存使用量都会增加)。

    李>

谁能向我解释为什么这些元素不能完全删除?如何修复代码以便在删除片段时清理它们? 即使片段以某种方式被删除或从中删除,保存在内存中。如何从内存中删除片段以节省恒定的内存大小?

【问题讨论】:

  • 如果您使用的是 FragmentStatePagerAdapter,那么我怀疑问题可能出在您的 GridViews 而不是整个 ViewPager...!
  • 我已经添加了我的代码。你能看看吗?它应该有帮助
  • 您是否在运行缓慢的模拟器?您的代码中是否有任何计算或长时间运行的循环?,您是否在片段 GridView 中使用 View holder 模式?在您的使用中,GridLayout 是不是 GridView 的最佳替代品?我认为当您亲自回答所有这些问题时,您可以找到解决方案!
  • 我在真实设备上运行应用程序。主要循环在 getDates() 方法中。我没有使用 ViewHolder 模式。
  • 尝试使用视图支架!还要检查 GridLayout 与 GridView 是否更适合您使用?

标签: android android-fragments memory-leaks android-viewpager android-pageradapter


【解决方案1】:

检查您的引用,99% 的内存泄漏是由本应被垃圾收集但仍然存在的持久对象引起的。确保您摆脱不再使用的对象。

此外,FragmentStatePagerAdapter 在您有大量片段并且在页面之间切换时会增加一些开销时效果最佳。考虑尝试FragmentPagerAdapter,它更适合少量的片段。正如已经提到的,你应该实现viewholder pattern,因为你有一个视图列表。

【讨论】:

  • 我更新了我的代码,现在我在我的网格视图适配器中使用 ViewHolder 模式,但仍然没有任何改变..我有内存泄漏。
  • 你能提供一个Logcat消息什么的吗?
  • 我没有得到任何异常 .. 代码有效,但每次交换时内存使用量都会增加,并且不会释放。
【解决方案2】:

我已经成功解决了这个问题。

在 onDetach 方法中的片段类上,我删除了所有子片段的引用。然后,我打电话给Runtime.getRuntime().gc(),以强制GC从未使用的元素中清理内存;

【讨论】:

    猜你喜欢
    • 2013-07-12
    • 2017-02-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-06-10
    相关资源
    最近更新 更多