Search code examples
androidandroid-fragmentsmemory-leaksbitmapandroid-memory

My application uses a lot of Ram and closes unexpectedly


Now I have almost finished my app. To tell the truth and I am with the design of the app. I'm getting images to make it look prettier. And I've run into a problem. First I want to explain what makes the app, so they can better understand the problem.

The app is a game of questions, the answers are true or false. Each question has an image. Each question is a fragment that will change with fragmentManager.

The application questions without images works perfectly. But adding images, the application when it reaches the last question stops and closes automatically.

1- Using a virtual machine with Android 2.1, the application does not stop, but if you are getting slower. 2- Using a mobile xperia Z2 with Android 4.4.4 application to get to the question number 10 stops and closes.

I think the problem is the Managing Memory Bitmap.

But how to implement the Managing Memory Bitmap to clean the memory used by the images.

The main code of the first group of questions is:

public class Grp1Fragment extends Fragment {

    private int ContArrayAsk = 0;
    private int ContRight = 0;
    private int ContFailed= 0;
    private int ContArrayResults =0;
    private String msg = "";

    private Button buttonTrue;
    private Button buttonFalse;
    private Button buttonNextAsk;
    private Button buttonShareScore;

    private View view;



    String[] arrayFragmentAResultsGrp1 = new String[]{"false","false","false","true","false","true","true","true","false","true",};

    private Fragment[] fragmentsChangeAsk = new Fragment[]{
                                                    new Grp1FragmentP1(),
                                                    new Grp1FragmentE1(),
                                                    new Grp1FragmentP2(),
                                                    new Grp1FragmentE2(),
                                                    new Grp1FragmentP3(),
                                                    new Grp1FragmentE3(),
                                                    new Grp1FragmentP4(),
                                                    new Grp1FragmentE4(),
                                                    new Grp1FragmentP5(),
                                                    new Grp1FragmentE5(),
                                                    ...

                                };

    public Grp1Fragment() {
        // Required empty public constructor


    }


    @Override
    public void onActivityCreated(Bundle savedInstanceState){
        super.onActivityCreated(savedInstanceState);

        android.support.v4.app.FragmentManager managerAsk = getActivity().getSupportFragmentManager();
        managerAsk.beginTransaction()
                .add(R.id.fragmentaskGRP1, fragmentsChangeAsk[0])
                .add(R.id.fragmentaskGRP1, fragmentsChangeAsk[1])
                ...
                .hide(fragmentsChangeAsk[1])
                .hide(fragmentsChangeAsk[2])
        ...
                .commit();
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        view = inflater.inflate(R.layout.fragment_grp1, container, false);
        return view;
    }


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


        buttonTrue = (Button) view.findViewById(R.id.buttontruegpr1);
        if(buttonTrue != null){
            buttonTrue.setOnClickListener(new View.OnClickListener() {
                public void onClick(View v) {
                    if (arrayFragmentAResultsGrp1[ContArrayResults].equals("true")) {
                        Toast.makeText(getActivity(), "Respuesta correcta", Toast.LENGTH_SHORT).show();
                        ContRight++;
                        ContArrayAsk++;
                        ContArrayAsk++;
                        ContArrayResults++;
                        setContent(ContArrayAsk);
                        validarpantallafinal();
                    }else{
                        Toast.makeText(getActivity(), "Respuesta incorrecta", Toast.LENGTH_SHORT).show();
                        ContFailed++;
                        ContArrayAsk++;
                        ContArrayResults++;
                        setContent(ContArrayAsk);
                        buttontrueorfalsedisabled();
                        validarpantallafinal();
                    }
                }
            });
        }


        buttonFalse = (Button) view.findViewById(R.id.buttonfalsegpr1);
        if(buttonFalse != null) {
            buttonFalse.setOnClickListener(new View.OnClickListener() {
                public void onClick(View v) {
                    if (arrayFragmentAResultsGrp1[ContArrayResults].equals("false")) {
                        Toast.makeText(getActivity(), "Respuesta correcta", Toast.LENGTH_SHORT).show();
                        ContRight++;
                        ContArrayAsk++;
                        ContArrayAsk++;
                        ContArrayResults++;
                        setContent(ContArrayAsk);
                        validarpantallafinal();
                    } else {
                        Toast.makeText(getActivity(), "Respuesta incorrecta", Toast.LENGTH_SHORT).show();
                        ContFailed++;
                        ContArrayAsk++;
                        ContArrayResults++;
                        setContent(ContArrayAsk);
                        buttontrueorfalsedisabled();
                        validarpantallafinal();
                    }
                }
            });
        }


        buttonNextAsk = (Button) view.findViewById(R.id.buttonnextaskgrp1);
        buttonNextAsk.setEnabled(false);
        buttonNextAsk.setVisibility(View.INVISIBLE);
        if(buttonFalse != null) {
            buttonNextAsk.setOnClickListener(new View.OnClickListener() {
                public void onClick(View v) {
                    //Toast.makeText(getActivity(), "Ha pulsado el botón siguiente pregunta", Toast.LENGTH_LONG).show();
                    buttontrueorfalseenabled();
                    ContArrayAsk++;
                    setContent(ContArrayAsk);
                    validarpantallafinal();
                }
            });
        }


        buttonShareScore = (Button) view.findViewById(R.id.buttonsharescoregrp1);
        buttonShareScore.setEnabled(false);
        buttonShareScore.setVisibility(View.INVISIBLE);
        if(buttonShareScore != null) {
            buttonShareScore.setOnClickListener(new View.OnClickListener() {
                public void onClick(View v) {
                    msg = msg.concat(getString(R.string.message_share_score_grp1) +
                                                "\nPreguntas acertadas: "+ Integer.toString(ContRight) +
                                                "\nPreguntas falladas: " + Integer.toString(ContFailed) +
                                                "\n" + getString(R.string.message_share));
                    Intent intent = new Intent();
                    intent.setAction(Intent.ACTION_SEND);
                    intent.putExtra(Intent.EXTRA_TEXT, msg);
                    intent.setType("text/plain");
                    startActivity(Intent.createChooser(intent,
                            getString(R.string.action_share)));
                    //Toast.makeText(getActivity(), "Ha pulsado el botón compartir resultados\nAciertos:" + Integer.toString(ContRight) + "\nFallos:" + Integer.toString(ContFailed), Toast.LENGTH_LONG).show();
                }
            });
        }

    }

    public void buttontrueorfalseenabled(){
        buttonTrue.setEnabled(true);
        buttonTrue.setVisibility(View.VISIBLE);
        buttonFalse.setEnabled(true);
        buttonFalse.setVisibility(View.VISIBLE);
        buttonNextAsk.setEnabled(false);
        buttonNextAsk.setVisibility(View.INVISIBLE);
    }

    public void buttontrueorfalsedisabled(){
        buttonTrue.setEnabled(false);
        buttonTrue.setVisibility(View.INVISIBLE);
        buttonFalse.setEnabled(false);
        buttonFalse.setVisibility(View.INVISIBLE);
        buttonNextAsk.setEnabled(true);
        buttonNextAsk.setVisibility(View.VISIBLE);
    }

    public void validarpantallafinal(){
        if (ContArrayAsk == fragmentsChangeAsk.length-1){
            buttonTrue.setEnabled(false);
            buttonTrue.setVisibility(View.INVISIBLE);
            buttonFalse.setEnabled(false);
            buttonFalse.setVisibility(View.INVISIBLE);
            buttonNextAsk.setEnabled(false);
            buttonNextAsk.setVisibility(View.INVISIBLE);
            buttonShareScore.setEnabled(true);
            buttonShareScore.setVisibility(View.VISIBLE);
            TextView score = (TextView) view.findViewById(R.id.textScoregrp1);
            score.setText("Felicidades, ha terminado las preguntas del apartado de Historia.\n"+
                           "Las preguntas acertadas son: " + Integer.toString(ContRight) +
                           "\nLas preguntas falladas son: " + Integer.toString(ContFailed));

        }
    }

    public void setContent(int index){

        Fragment toHide1 = null;
        Fragment toHide2 = null;
        Fragment toHide3 = null;
        Fragment toHide4 = null;
        Fragment toHide5 = null;
        Fragment toHide6 = null;
        Fragment toHide7 = null;
        Fragment toHide8 = null;
        Fragment toHide9 = null;
        Fragment toHide10 = null;
        Fragment toHide11 = null;
        Fragment toHide12 = null;
        Fragment toHide13 = null;
        Fragment toHide14 = null;
        Fragment toHide15 = null;
        Fragment toHide16 = null;
        Fragment toHide17 = null;
        Fragment toHide18 = null;
        Fragment toHide19 = null;
        Fragment toHide20 = null;
        Fragment toShow = null;

        switch (index){
            case 0:
                toHide1 = fragmentsChangeAsk[1];
                toHide2 = fragmentsChangeAsk[2];
                toHide3 = fragmentsChangeAsk[3];
                toHide4 = fragmentsChangeAsk[4];
                toHide5 = fragmentsChangeAsk[5];
                toHide6 = fragmentsChangeAsk[6];
                toHide7 = fragmentsChangeAsk[7];
                toHide8 = fragmentsChangeAsk[8];
                toHide9 = fragmentsChangeAsk[9];
                toHide10 = fragmentsChangeAsk[10];
                toHide11 = fragmentsChangeAsk[11];
                toHide12 = fragmentsChangeAsk[12];
                toHide13 = fragmentsChangeAsk[13];
                toHide14 = fragmentsChangeAsk[14];
                toHide15 = fragmentsChangeAsk[15];
                toHide16 = fragmentsChangeAsk[16];
                toHide17 = fragmentsChangeAsk[17];
                toHide18 = fragmentsChangeAsk[18];
                toHide19 = fragmentsChangeAsk[19];
                toHide20 = fragmentsChangeAsk[20];
                toShow = fragmentsChangeAsk[0];
                break;
            case 1:
                toHide1 = fragmentsChangeAsk[0];
                toHide2 = fragmentsChangeAsk[2];
                ...
        case 20:
        ...
        }
        android.support.v4.app.FragmentManager managerAsk = getActivity().getSupportFragmentManager();
        managerAsk.beginTransaction()
                .hide(toHide1)
                .hide(toHide2)
                ...
                .show(toShow)
                .commit();

    }


}

Example of how to show the question with the image:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent" android:layout_height="match_parent"
    android:id="@+id/fragment_grp1_e1">


    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/GRP1error1"
        android:id="@+id/textViewGrp1e1"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        style="@style/textAsk"/>

    <ImageView
        android:id="@+id/imageView1"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:contentDescription="@string/impask"
        android:src="@mipmap/img1"
        android:layout_below="@+id/textViewGrp1e1"
        android:layout_centerHorizontal="true" />

</RelativeLayout>

In Android Studio you can see the used memory. When I run the app it uses 188Mb. When the app is used soon reaches 190MB.

The images we use weigh no more than 300kb. And the weight of the installed app is 6Mb.

Thank you very much for your help


Solution

  • You are using Many Fragments and each have layout with ImageView.

    Your approach seems like :

    Every question is new Fragment and has new layout. You are showing that Fragment when question displays.

    Why your approch is not optimized ?

    • How many time will you add new Fragment when you add new question in questionset. Your current approach is static and you need to add manually a fragment and layout for new question
    • Android DVM spares more heap size when fragment comes in application. You are initializing many fragment in your application. This uses much more Heap size than necessary (approximately 199MB)

    Solution to above points :

    • Better use Less fragment and corresponding layout. When you need to display next question, just replace current layout textfield and imagefield with next question information. This way you do not require to add fragments manually when you want to display new question
    • Nullify any ImageViews which are not in use. This way you will prevent memory leaks For more way to prevent memory leak : an excellent video from Google Engineers Google IO Memory optimization will definitely help you