Android - 为什么在片段替换后 RAM 使用量继续增加?

Android - Why RAM usage continues to increase after fragment replace?

我正在使用 NavigationDrawer 并像这样替换片段:

ft.setCustomAnimations(
                        android.R.anim.slide_in_left, android.R.anim.slide_out_right
                );
                ft.replace(container, fragment);

                // Start the animated transition.
                ft.commit();

当应用程序启动并加载第一个片段时,分配堆约为 15 MB。 在我用第二个片段替换第一个片段并做了一些工作后,分配堆约为 40MB。 然后我用第三个片段替换第二个片段,分配堆保持在 ~40MB(如果强制垃圾收集)分配堆减少 ~7 MB。 如果,我不会加载第二个片段,而是从第一个片段转到第三个片段,分配堆将保持在 ~17 MB。

第二个片段是否被破坏了,如果被破坏了,为什么内存没有被释放?

据我所知,没有 addToBackStack() 片段不会添加到后台堆栈,应该在删除后销毁(替换调用删除和添加)

第二个片段基本上是一个列表视图

public class ArtistListFragment extends Fragment {

    private FragmentArtistAdapter adapter;
    private static final List<ArtistsEntity> articles = new ArrayList<>();
    private GetAdds feed = new GetAdds();
    private GridView list;
    private String request = "new";
    private ToggleButton tbLeft;
    private ToggleButton tbRight;
    private ToggleButton tbAbc;

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

    private Tracker t;
    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        t = ((UILApplication) getActivity().getApplication()).getTracker();
        t.setScreenName("Artist List Screen");
        t.send(new HitBuilders.EventBuilder()
                .setCategory("Open Screen")
                .build());
        t.send(new HitBuilders.EventBuilder()
                .setCategory("view")
                .setAction("choose")
                .setLabel("new")
                .build());
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view =  inflater.inflate(R.layout.fragment_artist_list, container, false);
        setSwipeLayout(view);

        eVideoScroll.resetState();
        adapter = new FragmentArtistAdapter(getActivity(), articles);
        list = (GridView) view.findViewById(R.id.gridview);
        tbLeft = (ToggleButton) view.findViewById(R.id.tb_left);
        tbRight = (ToggleButton) view.findViewById(R.id.tb_right);
        tbAbc = (ToggleButton) view.findViewById(R.id.tb_abc);
        tbLeft.setOnCheckedChangeListener(leftListener);
        tbRight.setOnCheckedChangeListener(rightListener);
        tbAbc.setOnCheckedChangeListener(abcListener);
        tbLeft.setClickable(false);
        list.setAdapter(adapter);
        list.setOnItemClickListener(new AdapterView.OnItemClickListener() {

            @Override
            public void onItemClick(AdapterView<?> parent, View view,
                                    int position, long id) {
                BusProvider.getInstance().post(new OpenArtistInfo(
                                articles.get(position)
                                        .getSlug()));
            }

        });
        feed = new GetAdds();
        feed.execute();
        list.setOnScrollListener(eVideoScroll);

        return view;
    }

    private SwipeRefreshLayout swipeLayout;

    private void setSwipeLayout(View view) {
        swipeLayout = (SwipeRefreshLayout) view.findViewById(R.id.swipe_container);
        swipeLayout.setOnRefreshListener(refreshPage);
        swipeLayout.setColorSchemeResources(android.R.color.holo_blue_bright,
                android.R.color.holo_green_light,
                android.R.color.holo_orange_light,
                android.R.color.holo_red_light);
    }

    private final SwipeRefreshLayout.OnRefreshListener refreshPage = new SwipeRefreshLayout.OnRefreshListener() {
        @Override
        public void onRefresh() {
            new ResetVideoSelection().execute();
        }
    };

    boolean checkInternetConnection() {
        ConnectivityManager cm = (ConnectivityManager) getActivity()
                .getSystemService(Context.CONNECTIVITY_SERVICE);
        // test for connection
        if (cm.getActiveNetworkInfo() != null
                && cm.getActiveNetworkInfo().isAvailable()
                && cm.getActiveNetworkInfo().isConnected()) {
            return true;
        } else {
            AlertDialog alertDialog = new AlertDialog.Builder(getActivity()).create();
            alertDialog.setTitle(getString(R.string.no_internet_conection));
            alertDialog
                    .setMessage(getString(R.string.open_network_settings));
            alertDialog.setButton(AlertDialog.BUTTON_POSITIVE, getString(R.string.yes_response),
                    new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int which) {
                            Intent intent = new Intent(
                                    Settings.ACTION_WIRELESS_SETTINGS);
                            startActivity(intent);
                        }
                    });
            alertDialog.setButton(AlertDialog.BUTTON_NEGATIVE, getString(R.string.no_response),
                    new DialogInterface.OnClickListener() {

                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            dialog.dismiss();
                        }
                    });
            alertDialog.setIcon(R.drawable.ic_launcher);
            alertDialog.show();
            return false;
        }
    }

    void emptyResultDialog() {
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        builder.setMessage(ErrorMessage.INSTANCE.getErrorMessage())
                .setCancelable(false)
                .setPositiveButton(R.string.close_response,
                        new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface dialog, int id) {

                            }
                        });

        Dialog dialog = builder.create();
        dialog.show();

    }

    private class GetAdds extends AsyncTask<Void, Void, List<ArtistsEntity>> {
        @Override
        protected void onPreExecute() {

            if (!checkInternetConnection()) {
                swipeLayout.setRefreshing(true);
                cancel(true);
                eVideoScroll.loading = false;

            } else {
                if (!swipeLayout.isRefreshing())
                    swipeLayout.setRefreshing(true);
            }
        }

        @Override
        protected List<ArtistsEntity> doInBackground(Void... params) {
            int trial = 0;
            MaterialRequest jParse = new MaterialRequest();
            List<ArtistsEntity> result = null;
            int page = PaggingData.INSTANCE.artistListCurrentPage + 1;

            while (result == null && trial < 5 && !isCancelled()) {
                trial++;
                result = jParse.getArtists(request, page);
            }
            return result;

        }

        @Override
        protected void onPostExecute(List<ArtistsEntity> result) {
            super.onPostExecute(result);
            if (result != null && result.size() > 0) {
                if (PaggingData.INSTANCE.artistListCurrentPage <= 1)

                    articles.clear();
                articles.addAll(result);
                adapter.notifyDataSetChanged();
            } else
                emptyResultDialog();
            eVideoScroll.loading = false;
            swipeLayout.setRefreshing(false);

        }

    }


    private final CompoundButton.OnCheckedChangeListener leftListener = new CompoundButton.OnCheckedChangeListener() {

        @Override
        public void onCheckedChanged(CompoundButton buttonView,
                                     boolean isChecked) {
            if (isChecked) {
                request = "new";
                new ResetVideoSelection().execute();
                tbRight.setChecked(false);
                tbAbc.setChecked(false);
                tbLeft.setClickable(false);
                t.send(new HitBuilders.EventBuilder()
                        .setCategory("view")
                        .setAction("choose")
                        .setLabel("new")
                        .build());
            } else {
                tbLeft.setClickable(true);

            }

        }
    };
    private final CompoundButton.OnCheckedChangeListener rightListener = new CompoundButton.OnCheckedChangeListener() {

        @Override
        public void onCheckedChanged(CompoundButton buttonView,
                                     boolean isChecked) {
            if (isChecked) {
                request = "popular";
                new ResetVideoSelection().execute();
                tbLeft.setChecked(false);
                tbAbc.setChecked(false);
                tbRight.setClickable(false);
                t.send(new HitBuilders.EventBuilder()
                        .setCategory("view")
                        .setAction("choose")
                        .setLabel("popular")
                        .build());
            } else {
                tbRight.setClickable(true);

            }
        }
    };
    private final CompoundButton.OnCheckedChangeListener abcListener = new CompoundButton.OnCheckedChangeListener() {

        @Override
        public void onCheckedChanged(CompoundButton buttonView,
                                     boolean isChecked) {
            if (isChecked) {
                request = "abc";
                new ResetVideoSelection().execute();
                tbRight.setChecked(false);
                tbLeft.setChecked(false);
                tbAbc.setClickable(false);
                t.send(new HitBuilders.EventBuilder()
                        .setCategory("view")
                        .setAction("choose")
                        .setLabel("abc")
                        .build());
            } else {
                tbAbc.setClickable(true);

            }
        }
    };

    private class ResetVideoSelection extends AsyncTask<Void, Void, Void> {
        @Override
        protected void onPreExecute() {
            eVideoScroll.resetState();
            list.setSelection(0);
        }

        @Override
        protected Void doInBackground(Void... params) {
            // if (videoArticles.size() > 0)
            // while (listVideo.getSelectedItemPosition() != 0) {
            //
            // }
            return null;

        }

        @Override
        protected void onPostExecute(Void result) {
            super.onPostExecute(result);
            feed = new GetAdds();
            feed.execute();

        }

    }

    private final EndlessVideoScrollListener eVideoScroll = new EndlessVideoScrollListener();

    private class EndlessVideoScrollListener implements AbsListView.OnScrollListener {

        private int visibleThreshold = 5;
        private int previousTotal = 0;
        private boolean loading = true;

        public void resetState() {
            visibleThreshold = 5;
            previousTotal = 0;
            loading = true;
            PaggingData.INSTANCE.artistListPages = 0;
            PaggingData.INSTANCE.artistListCurrentPage = 0;
        }

        public EndlessVideoScrollListener() {
        }

        public EndlessVideoScrollListener(int visibleThreshold) {
            this.visibleThreshold = visibleThreshold;
        }

        @Override
        public void onScroll(AbsListView view, int firstVisibleItem,
                             int visibleItemCount, int totalItemCount) {
            if (loading) {
                if (totalItemCount > previousTotal) {
//                  loading = false;
                    previousTotal = totalItemCount;
                }
            }
            if (!loading
                    && (totalItemCount - visibleItemCount) <= (firstVisibleItem + visibleThreshold)) {
                // I load the next page of gigs using a background task,
                // but you can call any function here.
                if (PaggingData.INSTANCE.artistListCurrentPage + 1 <= PaggingData.INSTANCE.artistListPages) {

                    feed = new GetAdds();
                    feed.execute();
                    loading = true;
                }
            }
        }

        @Override
        public void onScrollStateChanged(AbsListView view, int scrollState) {
        }
    }

    @Override
    public void onPause() {
        super.onPause();
        feed.cancel(true);
        swipeLayout.setRefreshing(false);
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        ((NavigationDrawerActivity) activity).onSectionAttached(getArguments().getString("title"));

    }

}

事实上,Fragment 会在移入和移出视野时被销毁并重新创建(与 Activitys 不同),前提是 addToBackStack() 未被使用。

我怀疑

1. 您已在您的应用程序清单中设置 android:largeHeap = true

2. 您的 Fragment 可能正在使用大量 ImageView/VideoView 或缓存 Bitmap带有静态引用的 s,这也可能是堆增长不受限制的原因。

这些是我在这种情况下能想到的一些原因。